Installation
import {
WrInput,
WrInputGroup,
WrInputPrefix,
WrInputSuffix,
WrPasswordToggle
} from 'ngwr/input';
import { FormsModule } from '@angular/forms';
@Component({
imports: [
WrInput,
WrInputGroup,
WrInputPrefix,
WrInputSuffix,
WrPasswordToggle,
FormsModule,
],
})
export class MyComponent {
text = signal('');
}Basic usage
<input wrInput placeholder="Type here…" [(ngModel)]="text" />Types
Set any native type — text, number, email, search, tel, url, password.
<input wrInput type="email" placeholder="[email protected]" />
<input wrInput type="number" placeholder="0" />
<input wrInput type="search" placeholder="Search" />Prefix & suffix
Wrap in <wr-input-group> and mark sibling elements with [wrInputPrefix] / [wrInputSuffix]. The group owns the border and focus ring (via :focus-within).
<wr-input-group>
<span wrInputPrefix>$</span>
<input wrInput type="number" [(ngModel)]="amount" />
<span wrInputSuffix>USD</span>
</wr-input-group>Rounded
<input wrInput rounded placeholder="Rounded" />Password reveal
<wr-password-toggle> flips its linked input's type attribute between password and text. Use a template ref to wire them up.
<wr-input-group>
<input wrInput type="password" [(ngModel)]="password" #pwInput />
<wr-password-toggle [for]="pwInput" />
</wr-input-group>Input masking — with ngx-mask
ngwr does not ship a mask directive of its own, and deliberately does not wrap one either — a mask is input plumbing, not a look, so blessing the ecosystem's standard beats owning a second copy of it. ngx-mask is that standard: battle-tested, signal-compatible, MIT, and about 18 KB once minified and gzipped. Because [wrInput] is just a directive on the real <input>, it plugs in on the same element with zero ceremony.
Setup
// 1. Install ngx-mask as a peer dependency:
// pnpm add ngx-mask
// 2. Provide it at bootstrap once (pass overrides for global defaults):
import { provideEnvironmentNgxMask } from 'ngx-mask';
bootstrapApplication(AppComponent, {
providers: [
provideEnvironmentNgxMask({
thousandSeparator: ',',
decimalMarker: '.',
}),
],
});
// 3. Import the directive in any standalone component:
import { NgxMaskDirective } from 'ngx-mask';
@Component({
imports: [NgxMaskDirective, WrInput, FormsModule],
})
export class MyForm {}Common masks
<!-- Phone (US-style) -->
<input wrInput mask="(000) 000-0000" placeholder="(555) 555-5555" [(ngModel)]="phone" />
<!-- Credit card -->
<input wrInput mask="0000 0000 0000 0000" placeholder="4242 4242 4242 4242" [(ngModel)]="card" />
<!-- Expiry MM/YY -->
<input wrInput mask="00/00" placeholder="12/29" [(ngModel)]="expiry" />
<!-- CVC (3 digits) -->
<input wrInput mask="000" placeholder="123" [(ngModel)]="cvc" />
<!-- Date DD/MM/YYYY -->
<input wrInput mask="00/00/0000" placeholder="31/12/2026" [(ngModel)]="date" />
<!-- Money: separator with two-decimal precision -->
<wr-input-group>
<span wrInputPrefix>$</span>
<input wrInput mask="separator.2" [(ngModel)]="money" placeholder="1,234.56" />
</wr-input-group>Full token reference (custom patterns, dynamic masks, locale config) — see the ngx-mask docs.
Disabled
<input wrInput placeholder="Disabled" disabled />In a form, this is the one that behaves natively
[wrInput] styles a real <input>, so its form semantics are Angular's own — and they differ from every ngwr component in the catalog. Worth knowing before you mix the two in one FormGroup.
A native <input> binds through the DefaultValueAccessor Angular ships. Every ngwr value component — <wr-select>, <wr-input-number>, <wr-checkbox> and the rest — binds through Angular 22's forms bridge instead, because the library ships no ControlValueAccessor at all. Two consequences show up as soon as both are in the same form. updateOn: 'blur' and 'submit' hold here and are ignored by the components, and a { emitEvent: false } write repaints this field and not them. Neither is a bug in either half; the guide explains both and what to do about them.
[wrInput] directive
| Name | Description | Type | Default |
|---|---|---|---|
wrInput | Selector. Applies NGWR input styling to a native <input> or <textarea>. | attribute | — |
size | Control size. | 'sm' | 'md' | 'lg' | null | 'md' |
rounded | Pill-shaped corners. | boolean | null | false |
<wr-input-group>
| Name | Description | Type | Default |
|---|---|---|---|
wr-input-group | Wrapper for the input + prefix/suffix/toggle siblings. | component | — |
rounded | Pill-shaped corners. | boolean | null | false |
Prefix & suffix directives
| Name | Description | Type | Default |
|---|---|---|---|
[wrInputPrefix] | Marks an element as the left affix. | attribute | — |
[wrInputSuffix] | Marks an element as the right affix. | attribute | — |
<wr-password-toggle>
| Name | Description | Type | Default |
|---|---|---|---|
forrequired | The linked password <input> reference. | HTMLInputElement | — |
showLabel | Accessible name while the password is hidden — the button is icon-only. Falls back to the input.showPassword catalog key. | string | null | null |
hideLabel | Accessible name while the password is revealed. Falls back to the input.hidePassword catalog key. | string | null | null |
CSS variables
Custom properties ngwr/input publishes. Each default below is declared on the component's own selector, so a :root override is shadowed by it — set them on that selector, on a wrapper you scope yourself, or inline on the element. Unlike the BEM class names, these are the supported way to restyle the component.
| Variable | Default | Declared on |
|---|---|---|
--wr-input-bg | var(--wr-color-surface) | .wr-input +1 variant override |
--wr-input-border | var(--wr-color-outline) | .wr-input +2 variant overrides |
--wr-input-color | var(--wr-color-on-surface) | .wr-input +1 variant override |
--wr-input-font-size | var(--wr-control-font-size-md) | .wr-input +2 variant overrides |
--wr-input-group-affix | var(--wr-color-on-surface-muted) | .wr-input-group |
--wr-input-group-bg | var(--wr-color-surface) | .wr-input-group +1 variant override |
--wr-input-group-border | var(--wr-color-outline) | .wr-input-group +1 variant override |
--wr-input-group-padding-x | 0.75rem | .wr-input-group +1 variant override |
--wr-input-group-radius | var(--wr-control-radius-md) | .wr-input-group +1 variant override |
--wr-input-group-ring | transparent | .wr-input-group +1 variant override |
--wr-input-line-height | var(--wr-control-line-height-md) | .wr-input +2 variant overrides |
--wr-input-padding-x | var(--wr-control-padding-x-md) | .wr-input +3 variant overrides |
--wr-input-padding-y | var(--wr-control-padding-y-md) | .wr-input +2 variant overrides |
--wr-input-placeholder | rgba(var(--wr-color-on-surface-muted-rgb), 0.7) | .wr-input |
--wr-input-radius | var(--wr-control-radius-md) | .wr-input +3 variant overrides |
--wr-input-ring | transparent | .wr-input +2 variant overrides |