Skip to content

docs(forms): "Value transformation" section for custom controls does not mention transformedValue #70206

Description

@nsbarsukov

Describe the problem that you experienced

The "Value transformation" section of the Signal Forms custom controls guide teaches a manual pattern: a linkedSignal() for the display value plus a hand-written method that parses the user input back into the model on blur.

import {formatCurrency} from '@angular/common';
import {ChangeDetectionStrategy, Component, linkedSignal, model} from '@angular/core';
import {FormValueControl} from '@angular/forms/signals';

@Component({
  selector: 'app-currency-input',
  template: `
    <input
      type="text"
      [value]="displayValue()"
      (input)="displayValue.set($event.target.value)"
      (blur)="updateModel()"
    />
  `,
})
export class CurrencyInput implements FormValueControl<number> {
  readonly value = model.required<number>();

  readonly displayValue = linkedSignal(() => formatCurrency(this.value(), 'en', 'USD'));

  updateModel() {
    // [...]
  }
}

Angular v22 ships transformedValue() in @angular/forms/signals, which is built for exactly this case: it returns a writable signal of the raw UI value kept in sync with the model through parse / format, exposes parseErrors, and — when used inside a field context — reports those parse errors to the nearest field automatically.

import {transformedValue} from '@angular/forms/signals';

@Component({
  selector: 'number-input',
  template: `<input [value]="rawValue()" (input)="rawValue.set($event.target.value)" />`,
})
export class NumberInput implements FormValueControl<number | null> {
  readonly value = model.required<number | null>();
  protected readonly rawValue = transformedValue(this.value, {
    parse: (val) => {
      // [...]
    },
    format: (val) => val?.toString() ?? '',
  });
}

Because the guide never mentions it, readers hand-roll a weaker version of a feature the framework already provides.

Utility is discoverable only through the API reference — which a reader has no reason to open once the guide appears to have answered the question.

Enter the URL of the topic with the problem

https://angular.dev/guide/forms/signals/custom-controls#value-transformation

Describe what you were looking for in the documentation

The recommended "official" way to build a custom control with value transformation.

Describe what you want to experience that would fix the problem

If the official documentation states that transformedValue is the recommended approach (over a plain linkedSignal), I will align with it an existing feature of Taiga UI:

export abstract class TuiValueTransformer<From, To = unknown> {
    public abstract toControlValue(componentValue: From): To;
    public abstract fromControlValue(controlValue: To): From;
}

⬇️ (next major release) ⬇️

import type {ParseResult, TransformedValueOptions} from '@angular/forms/signals';

/**
 * @deprecated implement `TransformedValueOptions` from `@angular/forms/signals` directly
 */
export abstract class TuiValueTransformer<From, To = unknown>
    implements TransformedValueOptions<To, From>
{
    public abstract parse: (rawValue: From) => ParseResult<To>;
    public abstract format: (value: To) => From;
}

But I need a guarantee that this is really a stable API with no plans to remove it. Mentioning it in the official guide (https://angular.dev/guide/forms/signals/custom-controls#value-transformation) would be a good one.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: docsRelated to the documentationgemini-triagedLabel noting that an issue has been triaged by geminiopen for contributionsAn issue that is suitable for a community contributor (based on its complexity/scope).

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions