From a6472984122a73c2c9016746374ef383c6cb52c4 Mon Sep 17 00:00:00 2001 From: cexbrayat Date: Wed, 22 May 2019 10:22:13 +0200 Subject: [PATCH] feat(forms): formControlName also accepts a number (#30606) This commit relaxes the type of the `formControlName` input to accept both a `string` and a `number`. Currently, when using a `FormArray`, most templates look like: ```
``` Here `formControlName` receives a number whereas its input type is a string. This is fine for VE and `fullTemplateTypeCheck`, but not for Ivy which does a more thorough type checking on inputs with `fullTemplateTypeCheck` enabled and throws `Type 'number' is not assignable to type 'string'`. It is fixable by using `formControlName="{{i}}"` but you have to know the difference between `a="{{b}}"` and `[a]="b"` and change it all over the application codebase. This commit allows the existing code to still type-check. PR Close #30606 --- packages/forms/src/directives/ng_control.ts | 2 +- .../reactive_directives/form_control_name.ts | 10 ++++++++-- packages/forms/src/directives/shared.ts | 4 ++-- tools/public_api_guard/forms/forms.d.ts | 4 ++-- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/forms/src/directives/ng_control.ts b/packages/forms/src/directives/ng_control.ts index 0314773902..de09f7ff83 100644 --- a/packages/forms/src/directives/ng_control.ts +++ b/packages/forms/src/directives/ng_control.ts @@ -36,7 +36,7 @@ export abstract class NgControl extends AbstractControlDirective { * @description * The name for the control */ - name: string|null = null; + name: string|number|null = null; /** * @description diff --git a/packages/forms/src/directives/reactive_directives/form_control_name.ts b/packages/forms/src/directives/reactive_directives/form_control_name.ts index 11992e5842..784ea62a30 100644 --- a/packages/forms/src/directives/reactive_directives/form_control_name.ts +++ b/packages/forms/src/directives/reactive_directives/form_control_name.ts @@ -145,9 +145,13 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy { * @description * Tracks the name of the `FormControl` bound to the directive. The name corresponds * to a key in the parent `FormGroup` or `FormArray`. + * Accepts a name as a string or a number. + * The name in the form of a string is useful for individual forms, + * while the numerical form allows for form controls to be bound + * to indices when iterating over controls in a `FormArray`. */ // TODO(issue/24571): remove '!'. - @Input('formControlName') name !: string; + @Input('formControlName') name !: string | number | null; /** * @description @@ -238,7 +242,9 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy { * Returns an array that represents the path from the top-level form to this control. * Each index is the string name of the control on that level. */ - get path(): string[] { return controlPath(this.name, this._parent !); } + get path(): string[] { + return controlPath(this.name == null ? this.name : this.name.toString(), this._parent !); + } /** * @description diff --git a/packages/forms/src/directives/shared.ts b/packages/forms/src/directives/shared.ts index f40274a653..17347db421 100644 --- a/packages/forms/src/directives/shared.ts +++ b/packages/forms/src/directives/shared.ts @@ -28,8 +28,8 @@ import {SelectMultipleControlValueAccessor} from './select_multiple_control_valu import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from './validators'; -export function controlPath(name: string, parent: ControlContainer): string[] { - return [...parent.path !, name]; +export function controlPath(name: string | null, parent: ControlContainer): string[] { + return [...parent.path !, name !]; } export function setUpControl(control: FormControl, dir: NgControl): void { diff --git a/tools/public_api_guard/forms/forms.d.ts b/tools/public_api_guard/forms/forms.d.ts index 577885628b..2b791597ae 100644 --- a/tools/public_api_guard/forms/forms.d.ts +++ b/tools/public_api_guard/forms/forms.d.ts @@ -255,7 +255,7 @@ export declare class FormControlName extends NgControl implements OnChanges, OnD readonly formDirective: any; isDisabled: boolean; /** @deprecated */ model: any; - name: string; + name: string | number | null; readonly path: string[]; /** @deprecated */ update: EventEmitter; readonly validator: ValidatorFn | null; @@ -353,7 +353,7 @@ export declare const NG_VALUE_ACCESSOR: InjectionToken; export declare abstract class NgControl extends AbstractControlDirective { readonly asyncValidator: AsyncValidatorFn | null; - name: string | null; + name: string | number | null; readonly validator: ValidatorFn | null; valueAccessor: ControlValueAccessor | null; abstract viewToModelUpdate(newValue: any): void;