Skip to content

Commit f0ad30c

Browse files
leonsenftAndrewKushnir
authored andcommitted
docs(forms): fix broken example
As written, the example rewrote the input value as the user edited it, causing all kinds of confusing behavior which rendered the input nearly unusable. While such implementations are possible, they require great care to manage the cursor position while editing. For the sake of brevity, this example now only updates the model on `blur` events; once the user is done editing.
1 parent c4e7ac1 commit f0ad30c

1 file changed

Lines changed: 21 additions & 10 deletions

File tree

adev/src/content/guide/forms/signals/custom-controls.md

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -353,10 +353,10 @@ Most state properties use `input()` (read-only from the form). Use `model()` for
353353

354354
Controls sometimes display values differently than the form model stores them - a date picker might display "January 15, 2024" while storing "2024-01-15", or a currency input might show "$1,234.56" while storing 1234.56.
355355

356-
Use `computed()` signals (from `@angular/core`) to transform the model value for display, and handle input events to parse user input back to the storage format:
356+
Use `linkedSignal()` (from `@angular/core`) to transform the model value for display, and handle input events to parse user input back to the storage format:
357357

358358
```angular-ts
359-
import { Component, model, computed, ChangeDetectionStrategy } from '@angular/core';
359+
import { ChangeDetectionStrategy, Component, linkedSignal, model } from '@angular/core';
360360
import { FormValueControl } from '@angular/forms/signals';
361361
362362
@Component({
@@ -365,23 +365,34 @@ import { FormValueControl } from '@angular/forms/signals';
365365
<input
366366
type="text"
367367
[value]="displayValue()"
368-
(input)="handleInput($event.target.value)"
368+
(input)="displayValue.set($event.target.value)"
369+
(blur)="updateModel()"
369370
/>
370371
`,
371372
changeDetection: ChangeDetectionStrategy.OnPush,
372373
})
373374
export class CurrencyInput implements FormValueControl<number> {
374-
value = model<number>(0); // Stores numeric value (1234.56)
375+
// Stores numeric value (1234.56)
376+
readonly value = model.required<number>();
375377
376-
displayValue = computed(() => {
377-
return this.value().toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ','); // Shows "1,234.56"
378-
});
378+
// Stores display value ("1,234.56")
379+
readonly displayValue = linkedSignal(() => formatCurrency(this.value()));
379380
380-
handleInput(input: string) {
381-
const num = parseFloat(input.replace(/[^0-9.]/g, ''));
382-
if (!isNaN(num)) this.value.set(num);
381+
// Update the model from the display value.
382+
updateModel() {
383+
this.value.set(parseCurrency(this.displayValue()));
383384
}
384385
}
386+
387+
// Converts a number to a currency string (e.g. 1234.56 -> "1,234.56").
388+
function formatCurrency(value: number) {
389+
return value.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
390+
}
391+
392+
// Converts a currency string to a number (e.g. "1,234.56" -> 1234.56).
393+
function parseCurrency(value: string) {
394+
return parseFloat(value.replace(/,/g, ''));
395+
}
385396
```
386397

387398
## Validation integration

0 commit comments

Comments
 (0)