https://github.com/toozuuu/ngxsmk-tel-input
International phone input for Angular 17–19. Flags, smart formatting + libphonenumber validation. E.164 output. SSR-safe. i18n/RTL. Standalone CVA.
https://github.com/toozuuu/ngxsmk-tel-input
angular angular-17 angular-18 angular-19 angular-components angular-forms i18n intl-tel-input libphonenumber ngx phone phone-number reactive-forms rtl ssr standalone-components telephone template-driven-forms ui-component
Last synced: about 1 month ago
JSON representation
International phone input for Angular 17–19. Flags, smart formatting + libphonenumber validation. E.164 output. SSR-safe. i18n/RTL. Standalone CVA.
- Host: GitHub
- URL: https://github.com/toozuuu/ngxsmk-tel-input
- Owner: toozuuu
- Created: 2025-08-13T09:32:29.000Z (2 months ago)
- Default Branch: master
- Last Pushed: 2025-08-22T20:13:09.000Z (about 2 months ago)
- Last Synced: 2025-08-22T22:48:27.629Z (about 2 months ago)
- Topics: angular, angular-17, angular-18, angular-19, angular-components, angular-forms, i18n, intl-tel-input, libphonenumber, ngx, phone, phone-number, reactive-forms, rtl, ssr, standalone-components, telephone, template-driven-forms, ui-component
- Language: TypeScript
- Homepage:
- Size: 12.4 MB
- Stars: 3
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
- fucking-awesome-angular - ngxsmk-tel-input - An Angular telephone input component with country dropdown, flags, and robust validation/formatting. (Third Party Components / Form Controls)
- awesome-angular - ngxsmk-tel-input - An Angular telephone input component with country dropdown, flags, and robust validation/formatting. (Third Party Components / Form Controls)
README
# ngxsmk-tel-input
An Angular **telephone input** component with country dropdown, flags, and robust validation/formatting.
Wraps [`intl-tel-input`](https://github.com/jackocnr/intl-tel-input) for the UI and [`libphonenumber-js`](https://github.com/catamphetamine/libphonenumber-js) for parsing/validation. Implements `ControlValueAccessor` so it plugs into Angular Forms.> Emits **E.164** by default (e.g. `+14155550123`). SSR‑safe via lazy browser‑only import.
---
## Screenshots
![]()
![]()
---
## ✨ Features
* Country dropdown with flags
* E.164 output (display can be national with `nationalMode`)
* Reactive & template‑driven Forms support (CVA)
* Built‑in validation using libphonenumber‑js
* SSR‑friendly (no `window` on the server)
* Easy theming via CSS variables
* Nice UX options: label/hint/error text, sizes, variants, clear button, autofocus, select-on-focus
* New: Masking & caret-friendly as-you-type formatting (optional)
* New: Format only when valid (formatWhenValid) and lock once valid (lockWhenValid) to prevent extra digits---
## ✅ Requirements
* Angular **17 – 19**
* Node **18** or **20**> Library `peerDependencies` target Angular `>=17 <20`. Your app can be 17, 18, or 19.
---
## 📦 Install
```bash
npm i ngxsmk-tel-input intl-tel-input libphonenumber-js
```### Add styles & flag assets (in your **app**, not the library)
Update your app’s `angular.json`:
```jsonc
{
"projects": {
"your-app": {
"architect": {
"build": {
"options": {
"styles": [
"node_modules/intl-tel-input/build/css/intlTelInput.css"
],
"assets": [
{ "glob": "**/*", "input": "node_modules/intl-tel-input/build/img", "output": "assets/intl-tel-input/img" }
]
}
}
}
}
}
}
```Optional override to ensure flags resolve (e.g., Vite/Angular 17+): add to your global styles
```css
.iti__flag { background-image: url("/assets/intl-tel-input/img/flags.png"); }
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
.iti__flag { background-image: url("/assets/intl-tel-input/img/flags@2x.png"); }
}
```Restart the dev server after changes.
---
## 🚀 Quick start (Reactive Forms)
```ts
// app.component.ts
import { Component, inject } from '@angular/core';
import { ReactiveFormsModule, FormBuilder, Validators } from '@angular/forms';
import { JsonPipe } from '@angular/common';
import { NgxsmkTelInputComponent, IntlTelI18n, CountryMap } from 'ngxsmk-tel-input';@Component({
selector: 'app-root',
standalone: true,
imports: [ReactiveFormsModule, NgxsmkTelInputComponent, JsonPipe],
template: `
[formatWhenValid]="'typing'"
[lockWhenValid]="true"
>
Value: {{ fg.value | json }}
`
})
export class AppComponent {
private readonly fb = inject(FormBuilder);
fg = this.fb.group({ phone: ['', Validators.required] });// English UI labels (dropdown/search/ARIA)
enLabels: IntlTelI18n = {
selectedCountryAriaLabel: 'Selected country',
countryListAriaLabel: 'Country list',
searchPlaceholder: 'Search country',
zeroSearchResults: 'No results',
noCountrySelected: 'No country selected'
};// Optional: only override the names you care about
enCountries: CountryMap = {
US: 'United States',
GB: 'United Kingdom',
AU: 'Australia',
CA: 'Canada'
};
}```
**Value semantics:** the form control value is **E.164** (e.g., `+14155550123`) when valid, or `null` when empty/invalid.
---
## 📝 Template‑driven usage
```html
```
---
## 🈺 Localization & RTL
You can localize the dropdown/search labels and override country names.
Korean example
```ts
// in component
koLabels = {
selectedCountryAriaLabel: '선택한 국가',
countryListAriaLabel: '국가 목록',
searchPlaceholder: '국가 검색',
zeroSearchResults: '결과 없음',
noCountrySelected: '선택된 국가 없음'
};koCountries = {
KR: '대한민국',
US: '미국',
JP: '일본',
CN: '중국'
};```
Arabic + RTL example
```ts
>
```
## ⚙️ API
### Inputs
| Name | Type | Default | Description |
|------------------------|---------------------------------------------|-------------------------|--------------------------------------------------------------------------------------------|
| `initialCountry` | `CountryCode \| 'auto'` | `'US'` | Starting country. `'auto'` uses geoIp stub (`US` by default). |
| `preferredCountries` | `CountryCode[]` | `['US','GB']` | Pin these at the top. |
| `onlyCountries` | `CountryCode[]` | — | Limit selectable countries. |
| `nationalMode` | `boolean` | `false` | If `true`, **display** national format in the input. Value still emits E.164. |
| `separateDialCode` | `boolean` | `false` | Show dial code outside the input. |
| `allowDropdown` | `boolean` | `true` | Enable/disable dropdown. |
| `placeholder` | `string` | `'Enter phone number'` | Input placeholder. |
| `autocomplete` | `string` | `'tel'` | Native autocomplete. |
| `disabled` | `boolean` | `false` | Disable the control. |
| `label` | `string` | — | Optional floating label text. |
| `hint` | `string` | — | Helper text below the control. |
| `errorText` | `string` | — | Custom error text. |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Control height/typography. |
| `variant` | `'outline' \| 'filled' \| 'underline'` | `'outline'` | Visual variant. |
| `showClear` | `boolean` | `true` | Show a clear (×) button when not empty. |
| `autoFocus` | `boolean` | `false` | Focus on init. |
| `selectOnFocus` | `boolean` | `false` | Select all text on focus. |
| `formatOnBlur` | `boolean` | `true` | Pretty‑print on blur (national if `nationalMode`). |
| `showErrorWhenTouched` | `boolean` | `true` | Show error styles only after blur. |
| `dropdownAttachToBody` | `boolean` | `true` | Attach dropdown to `` (avoids clipping/overflow). |
| `dropdownZIndex` | `number` | `2000` | Z‑index for dropdown panel. |
| `i18n` | `IntlTelI18n` | — | Localize dropdown/search/ARIA labels. |
| `localizedCountries` | `Partial>` | — | Override country display names (ISO-2 keys). |
| `dir` | `'ltr' \| 'rtl'` | `'ltr'` | Text direction for the control. |
| `autoPlaceholder` | `'off' \| 'polite' \| 'aggressive'` | `'polite'` | Example placeholders. Requires `utilsScript` unless `off`. |
| `utilsScript` | `string` | — | Path/URL to `utils.js` (needed for example placeholders). |
| `customPlaceholder` | `(example: string, country: any) => string` | — | Transform the example placeholder. |
| `clearAriaLabel` | `string` | `'Clear phone number'` | ARIA label for the clear button. |
| `lockWhenValid` | `boolean` | `true` | Prevent appending extra digits once the number is valid (editing/replacing still allowed). |> `CountryCode` is the ISO‑2 uppercase code from `libphonenumber-js` (e.g. `US`, `GB`).
### Outputs
| Event | Payload | Description |
| ---------------- | ---------------------------------------------------------- | ------------------------------------ |
| `countryChange` | `{ iso2: CountryCode }` | Fired when selected country changes. |
| `validityChange` | `boolean` | Fired when validity flips. |
| `inputChange` | `{ raw: string; e164: string \| null; iso2: CountryCode }` | Emitted on every keystroke. |### Public methods
* `focus(): void`
* `selectCountry(iso2: CountryCode): void`---
## 🧠 Formatting & validity behavior
* No formatting while invalid. As-you-type masking only starts when the digits form a valid number for the selected country.
* Sri Lanka / “trunk 0”: a national format may include a leading 0 (e.g., 071…). The emitted E.164 always excludes it (+94 71…)—this is expected.
* Lock when valid: with lockWhenValid enabled, once the number is valid, appending more digits is blocked (you can still delete/replace).
For rare patterns not covered by libphonenumber-js, the control falls back to raw digits (no forced mask) until it becomes valid.
---
## 🎨 Theming (CSS variables)
Override on the element or a parent container:
```html
```
Available tokens:
* Input: `--tel-bg`, `--tel-fg`, `--tel-border`, `--tel-border-hover`, `--tel-ring`, `--tel-placeholder`, `--tel-error`, `--tel-radius`, `--tel-focus-shadow`
* Dropdown: `--tel-dd-bg`, `--tel-dd-border`, `--tel-dd-shadow`, `--tel-dd-radius`, `--tel-dd-item-hover`, `--tel-dd-search-bg`, `--tel-dd-z`Dark mode: wrap in a `.dark` parent — tokens adapt automatically.
---
## ✔️ Validation patterns
```html
Phone is requiredPlease enter a valid phone number
```* When **valid** → control value = **E.164** string
* When **invalid/empty** → value = **null**, and validator sets `{ phoneInvalid: true }`> Need national string instead of E.164? Use `(inputChange)` and store `raw`/`national` yourself, or adapt the emitter to output national.
---
## 🌐 SSR notes
* The library lazy‑imports `intl-tel-input` only in the **browser** (guards with `isPlatformBrowser`).
* No `window`/`document` usage on the server path.---
## 🧪 Local development
This repo is an Angular workspace with a library.
```bash
# Build the library
ng build ngxsmk-tel-input# Option A: use it inside a demo app in the same workspace
ng serve demo# Option B: install locally via tarball in another app
cd dist/ngxsmk-tel-input && npm pack
# in your other app
npm i ../path-to-workspace/dist/ngxsmk-tel-input/ngxsmk-tel-input-.tgz
```> Workspace aliasing via `tsconfig.paths` also works (map `"ngxsmk-tel-input": ["dist/ngxsmk-tel-input"]`).
---
## 🧯 Troubleshooting
**UI looks unstyled / bullets in dropdown**
Add the CSS and assets in `angular.json` (see Install). Restart the dev server.**Flags don’t show**
Ensure the assets copy exists under `/assets/intl-tel-input/img` and add the CSS override block above.**`TS2307: Cannot find module 'ngxsmk-tel-input'`**
Build the library first so `dist/ngxsmk-tel-input` exists. If using workspace aliasing, add a `paths` entry to the root `tsconfig.base.json`.**Peer dependency conflict when installing**
The lib peers are `@angular/* >=17 <20`. Upgrade your app or install a compatible version.**Vite/Angular “Failed to resolve import …”**
Clear `.angular/cache`, rebuild the lib, and restart `ng serve`.---
## 📃 License
[MIT](./LICENSE)
## 🙌 Credits
* UI powered by [`intl-tel-input`](https://github.com/jackocnr/intl-tel-input)
* Parsing & validation by [`libphonenumber-js`](https://github.com/catamphetamine/libphonenumber-js)Last updated: 2025-08-19