MCPcopy
hub / github.com/angular/angular / cvaControlCreate

Function cvaControlCreate

packages/forms/signals/src/directive/control_cva.ts:34–117  ·  view source on GitHub ↗
(
  host: ControlDirectiveHost,
  parent: FormField<unknown>,
)

Source from the content-addressed store, hash-verified

32}
33
34export function cvaControlCreate(
35 host: ControlDirectiveHost,
36 parent: FormField<unknown>,
37): () => void {
38 const bindings = createBindings<ControlBindingKey | 'controlValue'>();
39
40 parent.controlValueAccessor!.registerOnChange((value: unknown) => {
41 // Update tracking for 'controlValue' here so that when the effect runs,
42 // `bindingUpdated` sees that the model value matches the last seen view value.
43 // This prevents the framework from writing the same value back to the CVA (CVA loopback).
44 bindings['controlValue'] = value;
45 parent.state().controlValue.set(value);
46 });
47 parent.controlValueAccessor!.registerOnTouched(() => parent.state().markAsTouched());
48
49 const legacyValidators = parent.injector.get(NG_VALIDATORS, null, {optional: true, self: true});
50 if (legacyValidators) {
51 let version: WritableSignal<number> | undefined;
52
53 for (const v of legacyValidators) {
54 if (isValidatorObject(v) && v.registerOnValidatorChange) {
55 version ??= signal(0);
56 v.registerOnValidatorChange(() => {
57 version!.update((n) => n + 1);
58 });
59 }
60 }
61
62 const validatorFns = legacyValidators.map((v) =>
63 typeof v === 'function' ? (v as ValidatorFn) : v.validate.bind(v),
64 );
65 const mergedValidator = Validators.compose(validatorFns);
66
67 const parseErrors = computed(() => {
68 // Read the `version` signal to re-run the validator when legacy validators trigger their change callbacks.
69 version?.();
70 const errors = mergedValidator ? mergedValidator(parent.interopNgControl.control) : null;
71 return reactiveErrorsToSignalErrors(errors, parent.interopNgControl.control);
72 });
73 // We must cast here because `CompatValidationError` claims to have `fieldTree` statically (to
74 // satisfy `ValidationState` elsewhere), but at construction it is created without it and acts as
75 // `WithoutFieldTree` initially.
76 parent.parseErrorsSource.set(
77 parseErrors as unknown as Signal<readonly ValidationError.WithoutFieldTree[]>,
78 );
79 }
80
81 parent.registerAsBinding({
82 reset: () => {
83 const value = parent.state().value();
84 bindings['controlValue'] = value;
85 untracked(() => parent.controlValueAccessor!.writeValue(value));
86 },
87 });
88
89 return () => {
90 const fieldState = parent.state();
91 const value = fieldState.value();

Callers 1

ɵngControlCreateMethod · 0.90

Calls 15

createBindingsFunction · 0.90
signalFunction · 0.90
computedFunction · 0.90
untrackedFunction · 0.90
bindingUpdatedFunction · 0.90
setNativeDomPropertyFunction · 0.90
isValidatorObjectFunction · 0.85
mapMethod · 0.80
composeMethod · 0.80
registerAsBindingMethod · 0.80

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…