Input Showcase
The differentiators: a character counter that counts what users perceive (grapheme clusters — emoji families are 1 character), a password reveal that keeps the caret, one-click copy with a live-region announcement, locale-aware number formatting and debounced commits with a separate per-keystroke stream.
Character counter
showCounter renders a live counter in the subscript end slot. It counts what users perceive — grapheme clusters — so a multi-codepoint family emoji or a flag counts as one character, not eight code units. The default limit mode enforces maxLength natively; soft mode lets typing continue past the limit and turns the counter red instead.
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeTextArea, OgeTextBox } from '@oge-ui/inputs';
@Component({
selector: 'demo-root',
imports: [OgeTextArea, OgeTextBox],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- grapheme-accurate: a family emoji counts as 1 character, not 8 code units -->
<oge-text-box label="Bio" [(value)]="bio" [showCounter]="true" [maxLength]="40" />
<!-- soft mode: typing past the limit is allowed, the counter turns red -->
<oge-text-area label="Tweet" [showCounter]="true" [maxLength]="140" counterMode="soft" />
`,
})
export class Demo {
protected readonly bio = signal('');
}Password reveal & copy
mode='password' automatically adds a reveal toggle that flips the input type in place — the caret position survives and password managers stay attached. showCopyButton adds one-click copy with a transient confirmation announced to screen readers; ideal for API keys and tokens, especially combined with readonly.
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeTextBox } from '@oge-ui/inputs';
@Component({
selector: 'demo-root',
imports: [OgeTextBox],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<oge-text-box label="Password" mode="password" [(value)]="password" />
<oge-text-box label="API token" [(value)]="token" [showCopyButton]="true" [readonly]="true" />
`,
})
export class Demo {
protected readonly password = signal('');
protected readonly token = signal('oge_live_9f0b01cf');
}Locale-aware numbers
format takes standard Intl.NumberFormatOptions (currency, precision, units) and renders it while the field is unfocused; focusing switches to a raw editable number in the locale's decimal notation. Parsing understands grouped input like 1.234,56. Spin buttons and arrow keys step by step with hold-to-repeat, and values clamp to min/max on commit.
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeNumberBox } from '@oge-ui/inputs';
@Component({
selector: 'demo-root',
imports: [OgeNumberBox],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- Intl.NumberFormat display on blur, raw editing on focus -->
<oge-number-box
label="Price (de-DE)"
locale="de-DE"
[format]="{ style: 'currency', currency: 'EUR' }"
[(value)]="price"
/>
<oge-number-box label="Percentage" [format]="{ style: 'percent' }" [step]="0.01" />
`,
})
export class Demo {
protected readonly price = signal<number | null>(1249.9);
}Debounced commits
debounce delays the committed value (the model, forms and valueCommitted) until typing pauses — ideal for search fields that trigger requests. Blur and Enter flush immediately so nothing is ever lost, while inputChange keeps streaming every raw keystroke if you need it.
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeTextBox } from '@oge-ui/inputs';
@Component({
selector: 'demo-root',
imports: [OgeTextBox],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<oge-text-box
label="Search"
[(value)]="query"
[debounce]="400"
(inputChange)="keystrokes = keystrokes + 1"
/>
<!-- value commits 400ms after the last keystroke; blur/Enter flush instantly -->
`,
})
export class Demo {
protected readonly query = signal('');
protected keystrokes = 0;
}