docs(forms): update API reference for common APIs and template-driven directives (#27033)

PR Close #27033
This commit is contained in:
Brandon Roberts 2018-11-09 14:11:21 -06:00 committed by Igor Minar
parent 2389a68ea3
commit c331fc6f0c
7 changed files with 290 additions and 91 deletions

View File

@ -26,7 +26,9 @@ function _isAndroid(): boolean {
} }
/** /**
* Turn this mode on if you want form directives to buffer IME input until compositionend * @description
* Provide this token to control if form directives buffer IME input until
* the "compositionend" event occurs.
* @publicApi * @publicApi
*/ */
export const COMPOSITION_BUFFER_MODE = new InjectionToken<boolean>('CompositionEventMode'); export const COMPOSITION_BUFFER_MODE = new InjectionToken<boolean>('CompositionEventMode');

View File

@ -37,9 +37,15 @@ export const ngControlStatusHost = {
}; };
/** /**
* @description
* Directive automatically applied to Angular form controls that sets CSS classes * Directive automatically applied to Angular form controls that sets CSS classes
* based on control status. The following classes are applied as the properties * based on control status.
* become true: *
* @usageNotes
*
* ### CSS classes applied
*
* The following classes are applied as the properties become true:
* *
* * ng-valid * * ng-valid
* * ng-invalid * * ng-invalid
@ -49,8 +55,8 @@ export const ngControlStatusHost = {
* * ng-untouched * * ng-untouched
* * ng-touched * * ng-touched
* *
* @ngModule FormsModule
* @ngModule ReactiveFormsModule * @ngModule ReactiveFormsModule
* @ngModule FormsModule
* @publicApi * @publicApi
*/ */
@Directive({selector: '[formControlName],[ngModel],[formControl]', host: ngControlStatusHost}) @Directive({selector: '[formControlName],[ngModel],[formControl]', host: ngControlStatusHost})
@ -59,11 +65,14 @@ export class NgControlStatus extends AbstractControlStatus {
} }
/** /**
* @description
* Directive automatically applied to Angular form groups that sets CSS classes * Directive automatically applied to Angular form groups that sets CSS classes
* based on control status (valid/invalid/dirty/etc). * based on control status (valid/invalid/dirty/etc).
*
* @see `NgControlStatus`
* *
* @ngModule FormsModule
* @ngModule ReactiveFormsModule * @ngModule ReactiveFormsModule
* @ngModule FormsModule
* @publicApi * @publicApi
*/ */
@Directive({ @Directive({

View File

@ -27,34 +27,36 @@ const resolvedPromise = Promise.resolve(null);
/** /**
* @description * @description
*
* Creates a top-level `FormGroup` instance and binds it to a form * Creates a top-level `FormGroup` instance and binds it to a form
* to track aggregate form value and validation status. * to track aggregate form value and validation status.
* *
* As soon as you import the `FormsModule`, this directive becomes active by default on * As soon as you import the `FormsModule`, this directive becomes active by default on
* all `<form>` tags. You don't need to add a special selector. * all `<form>` tags. You don't need to add a special selector.
* *
* You can export the directive into a local template variable using `ngForm` as the key * You optionally export the directive into a local template variable using `ngForm` as the key
* (ex: `#myForm="ngForm"`). This is optional, but useful. Many properties from the underlying * (ex: `#myForm="ngForm"`). This is optional, but useful. Many properties from the underlying
* `FormGroup` instance are duplicated on the directive itself, so a reference to it * `FormGroup` instance are duplicated on the directive itself, so a reference to it
* will give you access to the aggregate value and validity status of the form, as well as * gives you access to the aggregate value and validity status of the form, as well as
* user interaction properties like `dirty` and `touched`. * user interaction properties like `dirty` and `touched`.
* *
* To register child controls with the form, you'll want to use `NgModel` with a * To register child controls with the form, use `NgModel` with a `name`
* `name` attribute. You can also use `NgModelGroup` if you'd like to create * attribute. You may use `NgModelGroup` to create sub-groups within the form.
* sub-groups within the form.
* *
* You can listen to the directive's `ngSubmit` event to be notified when the user has * If necessary, listen to the directive's `ngSubmit` event to be notified when the user has
* triggered a form submission. The `ngSubmit` event will be emitted with the original form * triggered a form submission. The `ngSubmit` event emits the original form
* submission event. * submission event.
* *
* In template driven forms, all `<form>` tags are automatically tagged as `NgForm`. * In template driven forms, all `<form>` tags are automatically tagged as `NgForm`.
* If you want to import the `FormsModule` but skip its usage in some forms, * To import the `FormsModule` but skip its usage in some forms,
* for example, to use native HTML5 validation, you can add `ngNoForm` and the `<form>` * for example, to use native HTML5 validation, add the `ngNoForm` and the `<form>`
* tags won't create an `NgForm` directive. In reactive forms, using `ngNoForm` is * tags won't create an `NgForm` directive. In reactive forms, using `ngNoForm` is
* unnecessary because the `<form>` tags are inert. In that case, you would * unnecessary because the `<form>` tags are inert. In that case, you would
* refrain from using the `formGroup` directive. * refrain from using the `formGroup` directive.
* *
* @usageNotes
*
* ### Migrating from deprecated ngForm selector
*
* Support for using `ngForm` element selector has been deprecated in Angular v6 and will be removed * Support for using `ngForm` element selector has been deprecated in Angular v6 and will be removed
* in Angular v9. * in Angular v9.
* *
@ -71,8 +73,23 @@ const resolvedPromise = Promise.resolve(null);
* <ng-form #myForm="ngForm"> * <ng-form #myForm="ngForm">
* ``` * ```
* *
* ### Listening for form submission
*
* The following example shows how to capture the form values from the "ngSubmit" event.
*
* {@example forms/ts/simpleForm/simple_form_example.ts region='Component'} * {@example forms/ts/simpleForm/simple_form_example.ts region='Component'}
* *
* ### Setting the update options
*
* The following example shows you how to change the "updateOn" option from its default using
* ngFormOptions.
*
* ```html
* <form [ngFormOptions]="{updateOn: 'blur'}">
* <input name="one" ngModel> <!-- this ngModel will update on blur -->
* </form>
* ```
*
* @ngModule FormsModule * @ngModule FormsModule
* @publicApi * @publicApi
*/ */
@ -85,25 +102,33 @@ const resolvedPromise = Promise.resolve(null);
}) })
export class NgForm extends ControlContainer implements Form, export class NgForm extends ControlContainer implements Form,
AfterViewInit { AfterViewInit {
/**
* @description
* Returns whether the form submission has been triggered.
*/
public readonly submitted: boolean = false; public readonly submitted: boolean = false;
private _directives: NgModel[] = []; private _directives: NgModel[] = [];
/**
* @description
* The `FormGroup` instance created for this form.
*/
form: FormGroup; form: FormGroup;
/**
* @description
* Event emitter for the "ngSubmit" event
*/
ngSubmit = new EventEmitter(); ngSubmit = new EventEmitter();
/** /**
* Options for the `NgForm` instance. Accepts the following properties: * @description
* Tracks options for the `NgForm` instance.
* *
* **updateOn**: Serves as the default `updateOn` value for all child `NgModels` below it * **updateOn**: Sets the default `updateOn` value for all child `NgModels` below it
* (unless a child has explicitly set its own value for this in `ngModelOptions`). * unless explicitly set by a child `NgModel` using `ngModelOptions`). Defaults to 'change'.
* Potential values: `'change'` | `'blur'` | `'submit'` * Possible values: `'change'` | `'blur'` | `'submit'`.
*
* ```html
* <form [ngFormOptions]="{updateOn: 'blur'}">
* <input name="one" ngModel> <!-- this ngModel will update on blur -->
* </form>
* ```
* *
*/ */
// TODO(issue/24571): remove '!'. // TODO(issue/24571): remove '!'.
@ -117,16 +142,44 @@ export class NgForm extends ControlContainer implements Form,
new FormGroup({}, composeValidators(validators), composeAsyncValidators(asyncValidators)); new FormGroup({}, composeValidators(validators), composeAsyncValidators(asyncValidators));
} }
/**
* @description
* Lifecycle method called after the view is initialized. For internal use only.
*/
ngAfterViewInit() { this._setUpdateStrategy(); } ngAfterViewInit() { this._setUpdateStrategy(); }
/**
* @description
* The directive instance.
*/
get formDirective(): Form { return this; } get formDirective(): Form { return this; }
/**
* @description
* The internal `FormGroup` instance.
*/
get control(): FormGroup { return this.form; } get control(): FormGroup { return this.form; }
/**
* @description
* Returns an array representing the path to this group. Because this directive
* always lives at the top level of a form, it is always an empty array.
*/
get path(): string[] { return []; } get path(): string[] { return []; }
/**
* @description
* Returns a map of the controls in this group.
*/
get controls(): {[key: string]: AbstractControl} { return this.form.controls; } get controls(): {[key: string]: AbstractControl} { return this.form.controls; }
/**
* @description
* Method that sets up the control directive in this group, re-calculates its value
* and validity, and adds the instance to the internal list of directives.
*
* @param dir The `NgModel` directive instance.
*/
addControl(dir: NgModel): void { addControl(dir: NgModel): void {
resolvedPromise.then(() => { resolvedPromise.then(() => {
const container = this._findContainer(dir.path); const container = this._findContainer(dir.path);
@ -138,8 +191,20 @@ export class NgForm extends ControlContainer implements Form,
}); });
} }
/**
* @description
* Retrieves the `FormControl` instance from the provided `NgModel` directive.
*
* @param dir The `NgModel` directive instance.
*/
getControl(dir: NgModel): FormControl { return <FormControl>this.form.get(dir.path); } getControl(dir: NgModel): FormControl { return <FormControl>this.form.get(dir.path); }
/**
* @description
* Removes the `NgModel` instance from the internal list of directives
*
* @param dir The `NgModel` directive instance.
*/
removeControl(dir: NgModel): void { removeControl(dir: NgModel): void {
resolvedPromise.then(() => { resolvedPromise.then(() => {
const container = this._findContainer(dir.path); const container = this._findContainer(dir.path);
@ -150,6 +215,12 @@ export class NgForm extends ControlContainer implements Form,
}); });
} }
/**
* @description
* Adds a new `NgModelGroup` directive instance to the form.
*
* @param dir The `NgModelGroup` directive instance.
*/
addFormGroup(dir: NgModelGroup): void { addFormGroup(dir: NgModelGroup): void {
resolvedPromise.then(() => { resolvedPromise.then(() => {
const container = this._findContainer(dir.path); const container = this._findContainer(dir.path);
@ -160,6 +231,12 @@ export class NgForm extends ControlContainer implements Form,
}); });
} }
/**
* @description
* Removes the `NgModelGroup` directive instance from the form.
*
* @param dir The `NgModelGroup` directive instance.
*/
removeFormGroup(dir: NgModelGroup): void { removeFormGroup(dir: NgModelGroup): void {
resolvedPromise.then(() => { resolvedPromise.then(() => {
const container = this._findContainer(dir.path); const container = this._findContainer(dir.path);
@ -169,8 +246,20 @@ export class NgForm extends ControlContainer implements Form,
}); });
} }
/**
* @description
* Retrieves the `FormGroup` for a provided `NgModelGroup` directive instance
*
* @param dir The `NgModelGroup` directive instance.
*/
getFormGroup(dir: NgModelGroup): FormGroup { return <FormGroup>this.form.get(dir.path); } getFormGroup(dir: NgModelGroup): FormGroup { return <FormGroup>this.form.get(dir.path); }
/**
* Sets the new value for the provided `NgControl` directive.
*
* @param dir The `NgControl` directive instance.
* @param value The new value for the directive's control.
*/
updateModel(dir: NgControl, value: any): void { updateModel(dir: NgControl, value: any): void {
resolvedPromise.then(() => { resolvedPromise.then(() => {
const ctrl = <FormControl>this.form.get(dir.path !); const ctrl = <FormControl>this.form.get(dir.path !);
@ -178,8 +267,21 @@ export class NgForm extends ControlContainer implements Form,
}); });
} }
/**
* @description
* Sets the value for this `FormGroup`.
*
* @param value The new value
*/
setValue(value: {[key: string]: any}): void { this.control.setValue(value); } setValue(value: {[key: string]: any}): void { this.control.setValue(value); }
/**
* @description
* Method called when the "submit" event is triggered on the form.
* Triggers the `ngSubmit` emitter to emit the "submit" event as its payload.
*
* @param $event The "submit" event object
*/
onSubmit($event: Event): boolean { onSubmit($event: Event): boolean {
(this as{submitted: boolean}).submitted = true; (this as{submitted: boolean}).submitted = true;
syncPendingControls(this.form, this._directives); syncPendingControls(this.form, this._directives);
@ -187,8 +289,18 @@ export class NgForm extends ControlContainer implements Form,
return false; return false;
} }
/**
* @description
* Method called when the "reset" event is triggered on the form.
*/
onReset(): void { this.resetForm(); } onReset(): void { this.resetForm(); }
/**
* @description
* Resets the form to an initial value and resets its submitted status.
*
* @param value The new value for the form.
*/
resetForm(value: any = undefined): void { resetForm(value: any = undefined): void {
this.form.reset(value); this.form.reset(value);
(this as{submitted: boolean}).submitted = false; (this as{submitted: boolean}).submitted = false;

View File

@ -10,7 +10,8 @@ import {Directive, Inject, InjectionToken, Optional} from '@angular/core';
import {TemplateDrivenErrors} from './template_driven_errors'; import {TemplateDrivenErrors} from './template_driven_errors';
/** /**
* Token to provide to turn off the warning when using 'ngForm' deprecated selector. * @description
* `InjectionToken` to provide to turn off the warning when using 'ngForm' deprecated selector.
*/ */
export const NG_FORM_SELECTOR_WARNING = new InjectionToken('NgFormSelectorWarning'); export const NG_FORM_SELECTOR_WARNING = new InjectionToken('NgFormSelectorWarning');

View File

@ -47,55 +47,83 @@ const resolvedPromise = Promise.resolve(null);
/** /**
* @description * @description
*
* Creates a `FormControl` instance from a domain model and binds it * Creates a `FormControl` instance from a domain model and binds it
* to a form control element. * to a form control element.
* *
* The `FormControl` instance will track the value, user interaction, and * The `FormControl` instance tracks the value, user interaction, and
* validation status of the control and keep the view synced with the model. If used * validation status of the control and keeps the view synced with the model. If used
* within a parent form, the directive will also register itself with the form as a child * within a parent form, the directive also registers itself with the form as a child
* control. * control.
* *
* This directive can be used by itself or as part of a larger form. All you need is the * This directive is used by itself or as part of a larger form. Use the
* `ngModel` selector to activate it. * `ngModel` selector to activate it.
* *
* It accepts a domain model as an optional `Input`. If you have a one-way binding * It accepts a domain model as an optional `Input`. If you have a one-way binding
* to `ngModel` with `[]` syntax, changing the value of the domain model in the component * to `ngModel` with `[]` syntax, changing the value of the domain model in the component
* class will set the value in the view. If you have a two-way binding with `[()]` syntax * class sets the value in the view. If you have a two-way binding with `[()]` syntax
* (also known as 'banana-box syntax'), the value in the UI will always be synced back to * (also known as 'banana-box syntax'), the value in the UI always syncs back to
* the domain model in your class as well. * the domain model in your class.
* *
* If you wish to inspect the properties of the associated `FormControl` (like * To inspect the properties of the associated `FormControl` (like validity state),
* validity state), you can also export the directive into a local template variable using * export the directive into a local template variable using `ngModel` as the key (ex: `#myVar="ngModel"`).
* `ngModel` as the key (ex: `#myVar="ngModel"`). You can then access the control using the * You then access the control using the directive's `control` property,
* directive's `control` property, but most properties you'll need (like `valid` and `dirty`) * but most properties used (like `valid` and `dirty`) fall through to the control anyway for direct access.
* will fall through to the control anyway, so you can access them directly. You can see a * See a full list of properties directly available in `AbstractControlDirective`.
* full list of properties directly available in `AbstractControlDirective`.
* *
* The following is an example of a simple standalone control using `ngModel`: * @see `RadioControlValueAccessor`
* @see `SelectControlValueAccessor`
*
* @usageNotes
*
* ### Using ngModel on a standalone control
*
* The following examples show a simple standalone control using `ngModel`:
* *
* {@example forms/ts/simpleNgModel/simple_ng_model_example.ts region='Component'} * {@example forms/ts/simpleNgModel/simple_ng_model_example.ts region='Component'}
* *
* When using the `ngModel` within `<form>` tags, you'll also need to supply a `name` attribute * When using the `ngModel` within `<form>` tags, you'll also need to supply a `name` attribute
* so that the control can be registered with the parent form under that name. * so that the control can be registered with the parent form under that name.
* *
* It's worth noting that in the context of a parent form, you often can skip one-way or * In the context of a parent form, it's often unnecessary to include one-way or two-way binding,
* two-way binding because the parent form will sync the value for you. You can access * as the parent form syncs the value for you. You access its properties by exporting it into a
* its properties by exporting it into a local template variable using `ngForm` (ex: * local template variable using `ngForm` such as (`#f="ngForm"`). Use the variable where
* `#f="ngForm"`). Then you can pass it where it needs to go on submit. * needed on form submission.
* *
* If you do need to populate initial values into your form, using a one-way binding for * If you do need to populate initial values into your form, using a one-way binding for
* `ngModel` tends to be sufficient as long as you use the exported form's value rather * `ngModel` tends to be sufficient as long as you use the exported form's value rather
* than the domain model's value on submit. * than the domain model's value on submit.
*
* ### Using ngModel within a form
* *
* Take a look at an example of using `ngModel` within a form: * The following example shows controls using `ngModel` within a form:
* *
* {@example forms/ts/simpleForm/simple_form_example.ts region='Component'} * {@example forms/ts/simpleForm/simple_form_example.ts region='Component'}
*
* ### Using a standalone ngModel within a group
*
* The following example shows you how to use a standalone ngModel control
* within a form. This controls the display of the form, but doesn't contain form data.
* *
* To see `ngModel` examples with different form control types, see: * ```html
* <form>
* <input name="login" ngModel placeholder="Login">
* <input type="checkbox" ngModel [ngModelOptions]="{standalone: true}"> Show more options?
* </form>
* <!-- form value: {login: ''} -->
* ```
*
* ### Setting the ngModel name attribute through options
*
* The following example shows you an alternate way to set the name attribute. The name attribute is used
* within a custom form component, and the name `@Input` property serves a different purpose.
* *
* * Radio buttons: `RadioControlValueAccessor` * ```html
* * Selects: `SelectControlValueAccessor` * <form>
* <my-person-control name="Nancy" ngModel [ngModelOptions]="{name: 'user'}">
* </my-person-control>
* </form>
* <!-- form value: {user: ''} -->
* ```
* *
* @ngModule FormsModule * @ngModule FormsModule
* @publicApi * @publicApi
@ -110,55 +138,58 @@ export class NgModel extends NgControl implements OnChanges,
public readonly control: FormControl = new FormControl(); public readonly control: FormControl = new FormControl();
/** @internal */ /** @internal */
_registered = false; _registered = false;
/**
* @description
* Internal reference to the view model value.
*/
viewModel: any; viewModel: any;
/**
* @description
* Tracks the name bound to the directive. The parent form
* uses this name as a key to retrieve this control's value.
*/
// TODO(issue/24571): remove '!'. // TODO(issue/24571): remove '!'.
@Input() name !: string; @Input() name !: string;
/**
* @description
* Tracks whether the control is disabled.
*/
// TODO(issue/24571): remove '!'. // TODO(issue/24571): remove '!'.
@Input('disabled') isDisabled !: boolean; @Input('disabled') isDisabled !: boolean;
/**
* @description
* Tracks the value bound to this directive.
*/
@Input('ngModel') model: any; @Input('ngModel') model: any;
/** /**
* Options object for this `ngModel` instance. You can configure the following properties: * @description
* Tracks the configuration options for this `ngModel` instance.
* *
* **name**: An alternative to setting the name attribute on the form control element. * **name**: An alternative to setting the name attribute on the form control element. See
* Sometimes, especially with custom form components, the name attribute might be used * the [example](api/forms/NgModel#using-ngmodel-on-a-standalone-control) for using `NgModel`
* as an `@Input` property for a different purpose. In cases like these, you can configure * as a standalone control.
* the `ngModel` name through this option.
* *
* ```html * **standalone**: When set to true, the `ngModel` will not register itself with its parent form,
* <form> * and acts as if it's not in the form. Defaults to false.
* <my-person-control name="Nancy" ngModel [ngModelOptions]="{name: 'user'}">
* </my-person-control>
* </form>
* <!-- form value: {user: ''} -->
* ```
* *
* **standalone**: Defaults to false. If this is set to true, the `ngModel` will not * **updateOn**: Defines the event upon which the form control value and validity update.
* register itself with its parent form, and will act as if it's not in the form. This * Defaults to 'change'. Possible values: `'change'` | `'blur'` | `'submit'`.
* can be handy if you have form meta-controls, a.k.a. form elements nested in
* the `<form>` tag that control the display of the form, but don't contain form data.
*
* ```html
* <form>
* <input name="login" ngModel placeholder="Login">
* <input type="checkbox" ngModel [ngModelOptions]="{standalone: true}"> Show more options?
* </form>
* <!-- form value: {login: ''} -->
* ```
*
* **updateOn**: Defaults to `'change'`. Defines the event upon which the form control
* value and validity will update. Also accepts `'blur'` and `'submit'`.
*
* ```html
* <input [(ngModel)]="firstName" [ngModelOptions]="{updateOn: 'blur'}">
* ```
* *
*/ */
// TODO(issue/24571): remove '!'. // TODO(issue/24571): remove '!'.
@Input('ngModelOptions') @Input('ngModelOptions')
options !: {name?: string, standalone?: boolean, updateOn?: FormHooks}; options !: {name?: string, standalone?: boolean, updateOn?: FormHooks};
/**
* @description
* Event emitter for producing the `ngModelChange` event after
* the view model updates.
*/
@Output('ngModelChange') update = new EventEmitter(); @Output('ngModelChange') update = new EventEmitter();
constructor(@Optional() @Host() parent: ControlContainer, constructor(@Optional() @Host() parent: ControlContainer,
@ -173,6 +204,13 @@ export class NgModel extends NgControl implements OnChanges,
this.valueAccessor = selectValueAccessor(this, valueAccessors); this.valueAccessor = selectValueAccessor(this, valueAccessors);
} }
/**
* @description
* A lifecycle method called when the directive's inputs change. For internal use
* only.
*
* @param changes A object of key/value pairs for the set of changed inputs.
*/
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
this._checkForErrors(); this._checkForErrors();
if (!this._registered) this._setUpControl(); if (!this._registered) this._setUpControl();
@ -186,20 +224,50 @@ export class NgModel extends NgControl implements OnChanges,
} }
} }
/**
* @description
* Lifecycle method called before the directive's instance is destroyed. For internal
* use only.
*/
ngOnDestroy(): void { this.formDirective && this.formDirective.removeControl(this); } ngOnDestroy(): void { this.formDirective && this.formDirective.removeControl(this); }
/**
* @description
* 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[] { get path(): string[] {
return this._parent ? controlPath(this.name, this._parent) : [this.name]; return this._parent ? controlPath(this.name, this._parent) : [this.name];
} }
/**
* @description
* The top-level directive for this control if present, otherwise null.
*/
get formDirective(): any { return this._parent ? this._parent.formDirective : null; } get formDirective(): any { return this._parent ? this._parent.formDirective : null; }
/**
* @description
* Synchronous validator function composed of all the synchronous validators
* registered with this directive.
*/
get validator(): ValidatorFn|null { return composeValidators(this._rawValidators); } get validator(): ValidatorFn|null { return composeValidators(this._rawValidators); }
/**
* @description
* Async validator function composed of all the async validators registered with this
* directive.
*/
get asyncValidator(): AsyncValidatorFn|null { get asyncValidator(): AsyncValidatorFn|null {
return composeAsyncValidators(this._rawAsyncValidators); return composeAsyncValidators(this._rawAsyncValidators);
} }
/**
* @description
* Sets the new value for the view model and emits an `ngModelChange` event.
*
* @param newValue The new value emitted by `ngModelChange`.
*/
viewToModelUpdate(newValue: any): void { viewToModelUpdate(newValue: any): void {
this.viewModel = newValue; this.viewModel = newValue;
this.update.emit(newValue); this.update.emit(newValue);

View File

@ -22,21 +22,25 @@ export const modelGroupProvider: any = {
/** /**
* @description * @description
*
* Creates and binds a `FormGroup` instance to a DOM element. * Creates and binds a `FormGroup` instance to a DOM element.
* *
* This directive can only be used as a child of `NgForm` (or in other words, * This directive can only be used as a child of `NgForm` (within `<form>` tags).
* within `<form>` tags).
* *
* Use this directive if you'd like to create a sub-group within a form. This can * Use this directive to validate a sub-group of your form separately from the
* come in handy if you want to validate a sub-group of your form separately from * rest of your form, or if some values in your domain model make more sense
* the rest of your form, or if some values in your domain model make more sense to * to consume together in a nested object.
* consume together in a nested object.
* *
* Pass in the name you'd like this sub-group to have and it will become the key * Provide a name for the sub-group and it will become the key
* for the sub-group in the form's full value. You can also export the directive into * for the sub-group in the form's full value. If you need direct access, export the directive into
* a local template variable using `ngModelGroup` (ex: `#myGroup="ngModelGroup"`). * a local template variable using `ngModelGroup` (ex: `#myGroup="ngModelGroup"`).
* *
* @usageNotes
*
* ### Consuming controls in a grouping
*
* The following example shows you how to combine controls together in a sub-group
* of the form.
*
* {@example forms/ts/ngModelGroup/ng_model_group_example.ts region='Component'} * {@example forms/ts/ngModelGroup/ng_model_group_example.ts region='Component'}
* *
* @ngModule FormsModule * @ngModule FormsModule
@ -44,6 +48,11 @@ export const modelGroupProvider: any = {
*/ */
@Directive({selector: '[ngModelGroup]', providers: [modelGroupProvider], exportAs: 'ngModelGroup'}) @Directive({selector: '[ngModelGroup]', providers: [modelGroupProvider], exportAs: 'ngModelGroup'})
export class NgModelGroup extends AbstractFormGroupDirective implements OnInit, OnDestroy { export class NgModelGroup extends AbstractFormGroupDirective implements OnInit, OnDestroy {
/**
* @description
* Tracks the name of the `NgModelGroup` bound to the directive. The name corresponds
* to a key in the parent `NgForm`.
*/
// TODO(issue/24571): remove '!'. // TODO(issue/24571): remove '!'.
@Input('ngModelGroup') name !: string; @Input('ngModelGroup') name !: string;

View File

@ -215,8 +215,6 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy {
/** /**
* @description * @description
* Lifecycle method called before the directive's instance is destroyed. For internal use only. * Lifecycle method called before the directive's instance is destroyed. For internal use only.
*
* @param changes A object of key/value pairs for the set of changed inputs.
*/ */
ngOnDestroy(): void { ngOnDestroy(): void {
if (this.formDirective) { if (this.formDirective) {