| 
									
										
										
										
											2016-06-23 09:47:54 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							|  |  |  |  * Copyright Google Inc. All Rights Reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Use of this source code is governed by an MIT-style license that can be | 
					
						
							|  |  |  |  * found in the LICENSE file at https://angular.io/license
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-08 14:37:29 -07:00
										 |  |  | import {AfterViewInit, Directive, EventEmitter, Inject, Input, Optional, Self, forwardRef} from '@angular/core'; | 
					
						
							| 
									
										
										
										
											2016-06-08 16:38:52 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-08 14:37:29 -07:00
										 |  |  | import {AbstractControl, FormControl, FormGroup, FormHooks} from '../model'; | 
					
						
							| 
									
										
										
										
											2016-06-08 16:38:52 -07:00
										 |  |  | import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import {ControlContainer} from './control_container'; | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  | import {Form} from './form_interface'; | 
					
						
							| 
									
										
										
										
											2016-06-08 16:38:52 -07:00
										 |  |  | import {NgControl} from './ng_control'; | 
					
						
							| 
									
										
										
										
											2016-06-15 15:15:41 -07:00
										 |  |  | import {NgModel} from './ng_model'; | 
					
						
							| 
									
										
										
										
											2016-06-12 16:37:42 -07:00
										 |  |  | import {NgModelGroup} from './ng_model_group'; | 
					
						
							| 
									
										
										
										
											2017-08-07 21:11:46 -07:00
										 |  |  | import {composeAsyncValidators, composeValidators, removeDir, setUpControl, setUpFormContainer, syncPendingControls} from './shared'; | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-30 19:18:14 -07:00
										 |  |  | export const formDirectiveProvider: any = { | 
					
						
							|  |  |  |   provide: ControlContainer, | 
					
						
							|  |  |  |   useExisting: forwardRef(() => NgForm) | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-02 15:53:34 -07:00
										 |  |  | const resolvedPromise = Promise.resolve(null); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2018-04-05 10:40:58 +01:00
										 |  |  |  * @description | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2018-04-05 10:58:12 +01:00
										 |  |  |  * Creates a top-level `FormGroup` instance and binds it to a form | 
					
						
							| 
									
										
										
										
											2018-04-05 11:02:42 +01:00
										 |  |  |  * to track aggregate form value and validation status. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2016-09-12 17:01:04 -07:00
										 |  |  |  * 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. | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2016-09-12 17:01:04 -07:00
										 |  |  |  * You can 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 | 
					
						
							| 
									
										
										
										
											2018-04-05 10:58:12 +01:00
										 |  |  |  * `FormGroup` instance are duplicated on the directive itself, so a reference to it | 
					
						
							| 
									
										
										
										
											2016-09-12 17:01:04 -07:00
										 |  |  |  * will give you access to the aggregate value and validity status of the form, as well as | 
					
						
							|  |  |  |  * user interaction properties like `dirty` and `touched`. | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2018-04-05 10:58:12 +01:00
										 |  |  |  * To register child controls with the form, you'll want to use `NgModel` with a | 
					
						
							|  |  |  |  * `name` attribute.  You can also use `NgModelGroup` if you'd like to create | 
					
						
							| 
									
										
										
										
											2016-09-12 17:01:04 -07:00
										 |  |  |  * sub-groups within the form. | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2016-09-12 17:01:04 -07:00
										 |  |  |  * You can listen to the directive's `ngSubmit` event to be notified when the user has | 
					
						
							| 
									
										
										
										
											2016-10-11 23:49:36 +01:00
										 |  |  |  * triggered a form submission. The `ngSubmit` event will be emitted with the original form | 
					
						
							|  |  |  |  * submission event. | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2017-10-02 16:51:58 -04:00
										 |  |  |  * 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, | 
					
						
							|  |  |  |  * for example, to use native HTML5 validation, you can add `ngNoForm` and the `<form>` | 
					
						
							|  |  |  |  * 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 | 
					
						
							|  |  |  |  * refrain from using the `formGroup` directive. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2016-09-12 17:01:04 -07:00
										 |  |  |  * {@example forms/ts/simpleForm/simple_form_example.ts region='Component'} | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2016-09-12 17:01:04 -07:00
										 |  |  |  * * **npm package**: `@angular/forms` | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2016-09-12 17:01:04 -07:00
										 |  |  |  * * **NgModule**: `FormsModule` | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2016-08-17 07:44:39 -07:00
										 |  |  |  *  @stable | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |  */ | 
					
						
							|  |  |  | @Directive({ | 
					
						
							| 
									
										
										
										
											2016-06-10 19:10:17 -07:00
										 |  |  |   selector: 'form:not([ngNoForm]):not([formGroup]),ngForm,[ngForm]', | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |   providers: [formDirectiveProvider], | 
					
						
							| 
									
										
										
										
											2016-10-11 23:49:36 +01:00
										 |  |  |   host: {'(submit)': 'onSubmit($event)', '(reset)': 'onReset()'}, | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |   outputs: ['ngSubmit'], | 
					
						
							|  |  |  |   exportAs: 'ngForm' | 
					
						
							|  |  |  | }) | 
					
						
							| 
									
										
										
										
											2017-08-08 14:37:29 -07:00
										 |  |  | export class NgForm extends ControlContainer implements Form, | 
					
						
							|  |  |  |     AfterViewInit { | 
					
						
							| 
									
										
										
										
											2017-09-13 14:00:10 -07:00
										 |  |  |   public readonly submitted: boolean = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-07 21:11:46 -07:00
										 |  |  |   private _directives: NgModel[] = []; | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-10 11:15:59 -07:00
										 |  |  |   form: FormGroup; | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |   ngSubmit = new EventEmitter(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-08 14:37:29 -07:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * Options for the `NgForm` instance. Accepts the following properties: | 
					
						
							|  |  |  |    * | 
					
						
							|  |  |  |    * **updateOn**: Serves as the default `updateOn` value for all child `NgModels` below it | 
					
						
							|  |  |  |    * (unless a child has explicitly set its own value for this in `ngModelOptions`). | 
					
						
							|  |  |  |    * Potential values: `'change'` | `'blur'` | `'submit'` | 
					
						
							|  |  |  |    * | 
					
						
							|  |  |  |    * ```html
 | 
					
						
							|  |  |  |    * <form [ngFormOptions]="{updateOn: 'blur'}"> | 
					
						
							|  |  |  |    *    <input name="one" ngModel>  <!-- this ngModel will update on blur --> | 
					
						
							|  |  |  |    * </form> | 
					
						
							|  |  |  |    * ```
 | 
					
						
							|  |  |  |    * | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   @Input('ngFormOptions') options: {updateOn?: FormHooks}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-08 16:38:52 -07:00
										 |  |  |   constructor( | 
					
						
							|  |  |  |       @Optional() @Self() @Inject(NG_VALIDATORS) validators: any[], | 
					
						
							|  |  |  |       @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: any[]) { | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |     super(); | 
					
						
							| 
									
										
										
										
											2016-08-24 16:58:43 -07:00
										 |  |  |     this.form = | 
					
						
							|  |  |  |         new FormGroup({}, composeValidators(validators), composeAsyncValidators(asyncValidators)); | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-08 14:37:29 -07:00
										 |  |  |   ngAfterViewInit() { this._setUpdateStrategy(); } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |   get formDirective(): Form { return this; } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-10 11:15:59 -07:00
										 |  |  |   get control(): FormGroup { return this.form; } | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   get path(): string[] { return []; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   get controls(): {[key: string]: AbstractControl} { return this.form.controls; } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-23 09:55:26 -07:00
										 |  |  |   addControl(dir: NgModel): void { | 
					
						
							| 
									
										
										
										
											2016-08-02 15:53:34 -07:00
										 |  |  |     resolvedPromise.then(() => { | 
					
						
							| 
									
										
										
										
											2016-06-10 10:09:50 -07:00
										 |  |  |       const container = this._findContainer(dir.path); | 
					
						
							| 
									
										
										
										
											2017-09-13 14:00:10 -07:00
										 |  |  |       (dir as{control: FormControl}).control = | 
					
						
							|  |  |  |           <FormControl>container.registerControl(dir.name, dir.control); | 
					
						
							| 
									
										
										
										
											2016-06-15 15:15:41 -07:00
										 |  |  |       setUpControl(dir.control, dir); | 
					
						
							|  |  |  |       dir.control.updateValueAndValidity({emitEvent: false}); | 
					
						
							| 
									
										
										
										
											2017-08-07 21:11:46 -07:00
										 |  |  |       this._directives.push(dir); | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-05 13:35:17 -07:00
										 |  |  |   getControl(dir: NgModel): FormControl { return <FormControl>this.form.get(dir.path); } | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-15 15:15:41 -07:00
										 |  |  |   removeControl(dir: NgModel): void { | 
					
						
							| 
									
										
										
										
											2016-08-02 15:53:34 -07:00
										 |  |  |     resolvedPromise.then(() => { | 
					
						
							| 
									
										
										
										
											2016-10-11 23:49:36 +01:00
										 |  |  |       const container = this._findContainer(dir.path); | 
					
						
							| 
									
										
										
										
											2016-11-11 10:47:34 -08:00
										 |  |  |       if (container) { | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |         container.removeControl(dir.name); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-08-07 21:11:46 -07:00
										 |  |  |       removeDir<NgModel>(this._directives, dir); | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-12 16:37:42 -07:00
										 |  |  |   addFormGroup(dir: NgModelGroup): void { | 
					
						
							| 
									
										
										
										
											2016-08-02 15:53:34 -07:00
										 |  |  |     resolvedPromise.then(() => { | 
					
						
							| 
									
										
										
										
											2016-10-11 23:49:36 +01:00
										 |  |  |       const container = this._findContainer(dir.path); | 
					
						
							|  |  |  |       const group = new FormGroup({}); | 
					
						
							| 
									
										
										
										
											2016-06-25 13:27:29 -07:00
										 |  |  |       setUpFormContainer(group, dir); | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |       container.registerControl(dir.name, group); | 
					
						
							|  |  |  |       group.updateValueAndValidity({emitEvent: false}); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-12 16:37:42 -07:00
										 |  |  |   removeFormGroup(dir: NgModelGroup): void { | 
					
						
							| 
									
										
										
										
											2016-08-02 15:53:34 -07:00
										 |  |  |     resolvedPromise.then(() => { | 
					
						
							| 
									
										
										
										
											2016-10-11 23:49:36 +01:00
										 |  |  |       const container = this._findContainer(dir.path); | 
					
						
							| 
									
										
										
										
											2016-11-11 10:47:34 -08:00
										 |  |  |       if (container) { | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |         container.removeControl(dir.name); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-05 13:35:17 -07:00
										 |  |  |   getFormGroup(dir: NgModelGroup): FormGroup { return <FormGroup>this.form.get(dir.path); } | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   updateModel(dir: NgControl, value: any): void { | 
					
						
							| 
									
										
										
										
											2016-08-02 15:53:34 -07:00
										 |  |  |     resolvedPromise.then(() => { | 
					
						
							| 
									
										
										
										
											2017-04-17 11:13:30 -07:00
										 |  |  |       const ctrl = <FormControl>this.form.get(dir.path !); | 
					
						
							| 
									
										
										
										
											2016-08-05 13:35:17 -07:00
										 |  |  |       ctrl.setValue(value); | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-05 13:35:17 -07:00
										 |  |  |   setValue(value: {[key: string]: any}): void { this.control.setValue(value); } | 
					
						
							| 
									
										
										
										
											2016-07-08 13:04:25 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-11 23:49:36 +01:00
										 |  |  |   onSubmit($event: Event): boolean { | 
					
						
							| 
									
										
										
										
											2017-09-13 14:00:10 -07:00
										 |  |  |     (this as{submitted: boolean}).submitted = true; | 
					
						
							| 
									
										
										
										
											2017-08-07 21:11:46 -07:00
										 |  |  |     syncPendingControls(this.form, this._directives); | 
					
						
							| 
									
										
										
										
											2016-10-11 23:49:36 +01:00
										 |  |  |     this.ngSubmit.emit($event); | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-11 23:27:33 -07:00
										 |  |  |   onReset(): void { this.resetForm(); } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   resetForm(value: any = undefined): void { | 
					
						
							|  |  |  |     this.form.reset(value); | 
					
						
							| 
									
										
										
										
											2017-09-13 14:00:10 -07:00
										 |  |  |     (this as{submitted: boolean}).submitted = false; | 
					
						
							| 
									
										
										
										
											2016-08-11 23:27:33 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-07-12 15:02:25 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-08 14:37:29 -07:00
										 |  |  |   private _setUpdateStrategy() { | 
					
						
							|  |  |  |     if (this.options && this.options.updateOn != null) { | 
					
						
							|  |  |  |       this.form._updateOn = this.options.updateOn; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |   /** @internal */ | 
					
						
							| 
									
										
										
										
											2016-06-10 11:15:59 -07:00
										 |  |  |   _findContainer(path: string[]): FormGroup { | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |     path.pop(); | 
					
						
							| 
									
										
										
										
											2016-10-21 15:14:44 -07:00
										 |  |  |     return path.length ? <FormGroup>this.form.get(path) : this.form; | 
					
						
							| 
									
										
										
										
											2016-06-08 15:36:24 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | } |