docs(forms): update reactive form directives API reference (#26823)

PR Close #26823
This commit is contained in:
Brandon Roberts 2018-10-19 13:24:01 -05:00 committed by Kara Erickson
parent 6744b19297
commit 099d1a67a0
4 changed files with 310 additions and 125 deletions

View File

@ -30,38 +30,17 @@ export const formControlBinding: any = {
/**
* @description
*
* Syncs a standalone `FormControl` instance to a form control element.
*
* This directive ensures that any values written to the `FormControl`
* instance programmatically will be written to the DOM element (model -> view). Conversely,
* any values written to the DOM element through user input will be reflected in the
* `FormControl` instance (view -> model).
* * Syncs a standalone `FormControl` instance to a form control element.
*
* @see [Reactive Forms Guide](guide/reactive-forms)
* @see `FormControl`
* @see `AbstractControl`
*
* @usageNotes
* Use this directive if you'd like to create and manage a `FormControl` instance directly.
* Simply create a `FormControl`, save it to your component class, and pass it into the
* `FormControlDirective`.
*
* This directive is designed to be used as a standalone control. Unlike `FormControlName`,
* it does not require that your `FormControl` instance be part of any parent
* `FormGroup`, and it won't be registered to any `FormGroupDirective` that
* exists above it.
*
* **Get the value**: the `value` property is always synced and available on the
* `FormControl` instance. See a full list of available properties in
* `AbstractControl`.
*
* **Set the value**: You can pass in an initial value when instantiating the `FormControl`,
* or you can set it programmatically later using {@link AbstractControl#setValue setValue} or
* {@link AbstractControl#patchValue patchValue}.
*
* **Listen to value**: If you want to listen to changes in the value of the control, you can
* subscribe to the {@link AbstractControl#valueChanges valueChanges} event. You can also listen to
* {@link AbstractControl#statusChanges statusChanges} to be notified when the validation status is
* re-calculated.
*
* ### Example
* ### Registering a single form control
*
* The following examples shows how to register a standalone control and set its value.
*
* {@example forms/ts/simpleFormControl/simple_form_control_example.ts region='Component'}
*
@ -138,11 +117,23 @@ export const formControlBinding: any = {
@Directive({selector: '[formControl]', providers: [formControlBinding], exportAs: 'ngForm'})
export class FormControlDirective extends NgControl implements OnChanges {
/**
* @description
* Internal reference to the view model value.
*/
viewModel: any;
/**
* @description
* Tracks the `FormControl` instance bound to the directive.
*/
// TODO(issue/24571): remove '!'.
@Input('formControl') form !: FormControl;
/**
* @description
* Triggers a warning that this input should not be used with reactive forms.
*/
@Input('disabled')
set isDisabled(isDisabled: boolean) { ReactiveErrors.disabledAttrWarning(); }
@ -155,6 +146,7 @@ export class FormControlDirective extends NgControl implements OnChanges {
@Output('ngModelChange') update = new EventEmitter();
/**
* @description
* Static property used to track whether any ngModel warnings have been sent across
* all instances of FormControlDirective. Used to support warning config of "once".
*
@ -163,8 +155,9 @@ export class FormControlDirective extends NgControl implements OnChanges {
static _ngModelWarningSentOnce = false;
/**
* @description
* Instance property used to track whether an ngModel warning has been sent out for this
* particular FormControlDirective instance. Used to support warning config of "always".
* particular `FormControlDirective` instance. Used to support warning config of "always".
*
* @internal
*/
@ -181,6 +174,13 @@ export class FormControlDirective extends NgControl implements OnChanges {
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): void {
if (this._isControlChanged(changes)) {
setUpControl(this.form, this);
@ -197,16 +197,41 @@ export class FormControlDirective extends NgControl implements OnChanges {
}
}
/**
* @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[] { return []; }
/**
* @description
* Synchronous validator function composed of all the synchronous validators
* registered with this directive.
*/
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 {
return composeAsyncValidators(this._rawAsyncValidators);
}
/**
* @description
* The `FormControl` bound to this directive.
*/
get control(): FormControl { return this.form; }
/**
* @description
* Sets the new value for the view model and emits an `ngModelChange` event.
*
* @param newValue The new value for the view model.
*/
viewToModelUpdate(newValue: any): void {
this.viewModel = newValue;
this.update.emit(newValue);

View File

@ -29,42 +29,19 @@ export const controlNameBinding: any = {
/**
* @description
*
* Syncs a `FormControl` in an existing `FormGroup` to a form control
* element by name.
*
* This directive ensures that any values written to the `FormControl`
* instance programmatically will be written to the DOM element (model -> view). Conversely,
* any values written to the DOM element through user input will be reflected in the
* `FormControl` instance (view -> model).
*
* @see [Reactive Forms Guide](guide/reactive-forms)
* @see `FormControl`
* @see `AbstractControl`
*
* @usageNotes
* This directive is designed to be used with a parent `FormGroupDirective` (selector:
* `[formGroup]`).
*
* ### Register `FormControl` within a group
*
* It accepts the string name of the `FormControl` instance you want to
* link, and will look for a `FormControl` registered with that name in the
* closest `FormGroup` or `FormArray` above it.
*
* **Access the control**: You can access the `FormControl` associated with
* this directive by using the {@link AbstractControl#get get} method.
* Ex: `this.form.get('first');`
*
* **Get value**: the `value` property is always synced and available on the `FormControl`.
* See a full list of available properties in `AbstractControl`.
*
* **Set value**: You can set an initial value for the control when instantiating the
* `FormControl`, or you can set it programmatically later using
* {@link AbstractControl#setValue setValue} or {@link AbstractControl#patchValue patchValue}.
*
* **Listen to value**: If you want to listen to changes in the value of the control, you can
* subscribe to the {@link AbstractControl#valueChanges valueChanges} event. You can also listen to
* {@link AbstractControl#statusChanges statusChanges} to be notified when the validation status is
* re-calculated.
*
* ### Example
*
* In this example, we create form controls for first name and last name.
* The following example shows how to register multiple form controls within a form group
* and set their value.
*
* {@example forms/ts/simpleFormGroup/simple_form_group_example.ts region='Component'}
*
@ -150,14 +127,32 @@ export const controlNameBinding: any = {
@Directive({selector: '[formControlName]', providers: [controlNameBinding]})
export class FormControlName extends NgControl implements OnChanges, OnDestroy {
private _added = false;
/** @internal */
/**
* @description
* Internal reference to the view model value.
* @internal
*/
viewModel: any;
/**
* @description
* Tracks the `FormControl` instance bound to the directive.
*/
// TODO(issue/24571): remove '!'.
readonly control !: FormControl;
/**
* @description
* Tracks the name of the `FormControl` bound to the directive. The name corresponds
* to a key in the parent `FormGroup` or `FormArray`.
*/
// TODO(issue/24571): remove '!'.
@Input('formControlName') name !: string;
/**
* @description
* Triggers a warning that this input should not be used with reactive forms.
*/
@Input('disabled')
set isDisabled(isDisabled: boolean) { ReactiveErrors.disabledAttrWarning(); }
@ -170,6 +165,7 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy {
@Output('ngModelChange') update = new EventEmitter();
/**
* @description
* Static property used to track whether any ngModel warnings have been sent across
* all instances of FormControlName. Used to support warning config of "once".
*
@ -178,6 +174,7 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy {
static _ngModelWarningSentOnce = false;
/**
* @description
* Instance property used to track whether an ngModel warning has been sent out for this
* particular FormControlName instance. Used to support warning config of "always".
*
@ -200,6 +197,12 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy {
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) {
if (!this._added) this._setUpControl();
if (isPropertyUpdated(changes, this.viewModel)) {
@ -209,23 +212,54 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy {
}
}
/**
* @description
* 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 {
if (this.formDirective) {
this.formDirective.removeControl(this);
}
}
/**
* @description
* Sets the new value for the view model and emits an `ngModelChange` event.
*
* @param newValue The new value for the view model.
*/
viewToModelUpdate(newValue: any): void {
this.viewModel = newValue;
this.update.emit(newValue);
}
/**
* @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[] { return controlPath(this.name, this._parent !); }
/**
* @description
* The top-level directive for this group if present, otherwise 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); }
/**
* @description
* Async validator function composed of all the async validators registered with this
* directive.
*/
get asyncValidator(): AsyncValidatorFn {
return composeAsyncValidators(this._rawAsyncValidators) !;
}

View File

@ -31,25 +31,14 @@ export const formDirectiveProvider: any = {
* `FormGroup` instance to match any child `FormControl`, `FormGroup`,
* and `FormArray` instances to child `FormControlName`, `FormGroupName`,
* and `FormArrayName` directives.
*
* @see [Reactive Forms Guide](guide/reactive-forms)
* @see `AbstractControl`
*
* @usageNotes
* **Set value**: You can set the form's initial value when instantiating the
* `FormGroup`, or you can set it programmatically later using the `FormGroup`'s
* {@link AbstractControl#setValue setValue} or {@link AbstractControl#patchValue patchValue}
* methods.
* ### Register Form Group
*
* **Listen to value**: If you want to listen to changes in the value of the form, you can subscribe
* to the `FormGroup`'s {@link AbstractControl#valueChanges valueChanges} event. You can also
* listen to its {@link AbstractControl#statusChanges statusChanges} event to be notified when the
* validation status is re-calculated.
*
* Furthermore, you can 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
* submission event.
*
* ### Example
*
* In this example, we create form controls for first name and last name.
* The following example registers a `FormGroup` with first name and last name controls,
* and listens for the *ngSubmit* event when the button is clicked.
*
* {@example forms/ts/simpleFormGroup/simple_form_group_example.ts region='Component'}
*
@ -64,13 +53,31 @@ export const formDirectiveProvider: any = {
})
export class FormGroupDirective extends ControlContainer implements Form,
OnChanges {
/**
* @description
* Reports whether the form submission has been triggered.
*/
public readonly submitted: boolean = false;
// TODO(issue/24571): remove '!'.
private _oldForm !: FormGroup;
/**
* @description
* Tracks the list of added `FormControlName` instances
*/
directives: FormControlName[] = [];
/**
* @description
* Tracks the `FormGroup` bound to this directive.
*/
@Input('formGroup') form: FormGroup = null !;
/**
* @description
* Emits an event when the form submission has been triggered.
*/
@Output() ngSubmit = new EventEmitter();
constructor(
@ -79,6 +86,12 @@ export class FormGroupDirective extends ControlContainer implements Form,
super();
}
/**
* @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): void {
this._checkFormPresent();
if (changes.hasOwnProperty('form')) {
@ -88,12 +101,32 @@ export class FormGroupDirective extends ControlContainer implements Form,
}
}
/**
* @description
* Returns this directive's instance.
*/
get formDirective(): Form { return this; }
/**
* @description
* Returns the `FormGroup` bound to this directive.
*/
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 always an empty array.
*/
get path(): string[] { return []; }
/**
* @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 `FormControlName` directive instance.
*/
addControl(dir: FormControlName): FormControl {
const ctrl: any = this.form.get(dir.path);
setUpControl(ctrl, dir);
@ -102,35 +135,92 @@ export class FormGroupDirective extends ControlContainer implements Form,
return ctrl;
}
/**
* @description
* Retrieves the `FormControl` instance from the provided `FormControlName` directive
*
* @param dir The `FormControlName` directive instance.
*/
getControl(dir: FormControlName): FormControl { return <FormControl>this.form.get(dir.path); }
/**
* @description
* Removes the `FormControlName` instance from the internal list of directives
*
* @param dir The `FormControlName` directive instance.
*/
removeControl(dir: FormControlName): void { removeDir<FormControlName>(this.directives, dir); }
/**
* Adds a new `FormGroupName` directive instance to the form.
*
* @param dir The `FormGroupName` directive instance.
*/
addFormGroup(dir: FormGroupName): void {
const ctrl: any = this.form.get(dir.path);
setUpFormContainer(ctrl, dir);
ctrl.updateValueAndValidity({emitEvent: false});
}
/**
* No-op method to remove the form group.
*
* @param dir The `FormGroupName` directive instance.
*/
removeFormGroup(dir: FormGroupName): void {}
/**
* @description
* Retrieves the `FormGroup` for a provided `FormGroupName` directive instance
*
* @param dir The `FormGroupName` directive instance.
*/
getFormGroup(dir: FormGroupName): FormGroup { return <FormGroup>this.form.get(dir.path); }
/**
* Adds a new `FormArrayName` directive instance to the form.
*
* @param dir The `FormArrayName` directive instance.
*/
addFormArray(dir: FormArrayName): void {
const ctrl: any = this.form.get(dir.path);
setUpFormContainer(ctrl, dir);
ctrl.updateValueAndValidity({emitEvent: false});
}
/**
* No-op method to remove the form array.
*
* @param dir The `FormArrayName` directive instance.
*/
removeFormArray(dir: FormArrayName): void {}
/**
* @description
* Retrieves the `FormArray` for a provided `FormArrayName` directive instance.
*
* @param dir The `FormArrayName` directive instance.
*/
getFormArray(dir: FormArrayName): FormArray { return <FormArray>this.form.get(dir.path); }
/**
* Sets the new value for the provided `FormControlName` directive.
*
* @param dir The `FormControlName` directive instance.
* @param value The new value for the directive's control.
*/
updateModel(dir: FormControlName, value: any): void {
const ctrl  = <FormControl>this.form.get(dir.path);
ctrl.setValue(value);
}
/**
* @description
* Method called with 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 {
(this as{submitted: boolean}).submitted = true;
syncPendingControls(this.form, this.directives);
@ -138,8 +228,18 @@ export class FormGroupDirective extends ControlContainer implements Form,
return false;
}
/**
* @description
* Method called when the "reset" event is triggered on the form.
*/
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 {
this.form.reset(value);
(this as{submitted: boolean}).submitted = false;

View File

@ -28,37 +28,42 @@ export const formGroupNameProvider: any = {
*
* Syncs a nested `FormGroup` to a DOM element.
*
* This directive can only be used with a parent `FormGroupDirective` (selector:
* `[formGroup]`).
* This directive can only be used with a parent `FormGroupDirective`.
*
* It accepts the string name of the nested `FormGroup` you want to link, and
* will look for a `FormGroup` registered with that name in the parent
* It accepts the string name of the nested `FormGroup` to link, and
* looks for a `FormGroup` registered with that name in the parent
* `FormGroup` instance you passed into `FormGroupDirective`.
*
* Nested form groups can come in handy when you want to validate a sub-group of a
* form separately from the rest or when you'd like to group the values of certain
* Use nested form groups to validate a sub-group of a
* form separately from the rest or to group the values of certain
* controls into their own nested object.
*
* @see [Reactive Forms Guide](guide/reactive-forms)
*
* @usageNotes
* **Access the group**: You can access the associated `FormGroup` using the
* {@link AbstractControl#get get} method. Ex: `this.form.get('name')`.
*
* ### Access the group by name
*
* The following example uses the {@link AbstractControl#get get} method to access the
* associated `FormGroup`
*
* You can also access individual controls within the group using dot syntax.
* Ex: `this.form.get('name.first')`
* ```ts
* this.form.get('name');
* ```
*
* ### Access individual controls in the group
*
* The following example uses the {@link AbstractControl#get get} method to access
* individual controls within the group using dot syntax.
*
* **Get the value**: the `value` property is always synced and available on the
* `FormGroup`. See a full list of available properties in `AbstractControl`.
* ```ts
* this.form.get('name.first');
* ```
*
* **Set the value**: You can set an initial value for each child control when instantiating
* the `FormGroup`, or you can set it programmatically later using
* {@link AbstractControl#setValue setValue} or {@link AbstractControl#patchValue patchValue}.
*
* **Listen to value**: If you want to listen to changes in the value of the group, you can
* subscribe to the {@link AbstractControl#valueChanges valueChanges} event. You can also listen to
* {@link AbstractControl#statusChanges statusChanges} to be notified when the validation status is
* re-calculated.
*
* ### Example
* ### Register a nested `FormGroup`.
*
* The following example registers a nested *name* `FormGroup` within an existing `FormGroup`,
* and provides methods to retrieve the nested `FormGroup` and individual controls.
*
* {@example forms/ts/nestedFormGroup/nested_form_group_example.ts region='Component'}
*
@ -67,6 +72,11 @@ export const formGroupNameProvider: any = {
*/
@Directive({selector: '[formGroupName]', providers: [formGroupNameProvider]})
export class FormGroupName extends AbstractFormGroupDirective implements OnInit, OnDestroy {
/**
* @description
* Tracks the name of the `FormGroup` bound to the directive. The name corresponds
* to a key in the parent `FormGroup` or `FormArray`.
*/
// TODO(issue/24571): remove '!'.
@Input('formGroupName') name !: string;
@ -104,32 +114,11 @@ export const formArrayNameProvider: any = {
* It accepts the string name of the nested `FormArray` you want to link, and
* will look for a `FormArray` registered with that name in the parent
* `FormGroup` instance you passed into `FormGroupDirective`.
*
* Nested form arrays can come in handy when you have a group of form controls but
* you're not sure how many there will be. Form arrays allow you to create new
* form controls dynamically.
*
* @see [Reactive Forms Guide](guide/reactive-forms)
* @see `AbstractControl`
*
* @usageNotes
* **Access the array**: You can access the associated `FormArray` using the
* {@link AbstractControl#get get} method on the parent `FormGroup`.
* Ex: `this.form.get('cities')`.
*
* **Get the value**: the `value` property is always synced and available on the
* `FormArray`. See a full list of available properties in `AbstractControl`.
*
* **Set the value**: You can set an initial value for each child control when instantiating
* the `FormArray`, or you can set the value programmatically later using the
* `FormArray`'s {@link AbstractControl#setValue setValue} or
* {@link AbstractControl#patchValue patchValue} methods.
*
* **Listen to value**: If you want to listen to changes in the value of the array, you can
* subscribe to the `FormArray`'s {@link AbstractControl#valueChanges valueChanges} event.
* You can also listen to its {@link AbstractControl#statusChanges statusChanges} event to be
* notified when the validation status is re-calculated.
*
* **Add new controls**: You can add new controls to the `FormArray` dynamically by calling
* its {@link FormArray#push push} method.
* Ex: `this.form.get('cities').push(new FormControl());`
*
* ### Example
*
@ -149,6 +138,11 @@ export class FormArrayName extends ControlContainer implements OnInit, OnDestroy
/** @internal */
_asyncValidators: any[];
/**
* @description
* Tracks the name of the `FormArray` bound to the directive. The name corresponds
* to a key in the parent `FormGroup` or `FormArray`.
*/
// TODO(issue/24571): remove '!'.
@Input('formArrayName') name !: string;
@ -162,27 +156,59 @@ export class FormArrayName extends ControlContainer implements OnInit, OnDestroy
this._asyncValidators = asyncValidators;
}
/**
* @description
* A lifecycle method called when the directive's inputs are initialized. For internal use only.
*
* @throws If the directive does not have a valid parent.
*/
ngOnInit(): void {
this._checkParentType();
this.formDirective !.addFormArray(this);
}
/**
* @description
* A lifecycle method called before the directive's instance is destroyed. For internal use only.
*/
ngOnDestroy(): void {
if (this.formDirective) {
this.formDirective.removeFormArray(this);
}
}
/**
* @description
* The `FormArray` bound to this directive.
*/
get control(): FormArray { return this.formDirective !.getFormArray(this); }
/**
* @description
* The top-level directive for this group if present, otherwise null.
*/
get formDirective(): FormGroupDirective|null {
return this._parent ? <FormGroupDirective>this._parent.formDirective : null;
}
/**
* @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[] { return controlPath(this.name, this._parent); }
/**
* @description
* Synchronous validator function composed of all the synchronous validators registered with this
* directive.
*/
get validator(): ValidatorFn|null { return composeValidators(this._validators); }
/**
* @description
* Async validator function composed of all the async validators registered with this directive.
*/
get asyncValidator(): AsyncValidatorFn|null {
return composeAsyncValidators(this._asyncValidators);
}