2016-06-23 12:47:54 -04: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
|
|
|
|
*/
|
|
|
|
|
2016-06-08 19:38:52 -04:00
|
|
|
import {Directive, Inject, Optional, Self, forwardRef} from '@angular/core';
|
|
|
|
|
2016-08-02 18:53:34 -04:00
|
|
|
import {EventEmitter} from '../facade/async';
|
2016-06-14 21:23:40 -04:00
|
|
|
import {ListWrapper} from '../facade/collection';
|
|
|
|
import {isPresent} from '../facade/lang';
|
2016-06-10 14:15:59 -04:00
|
|
|
import {AbstractControl, FormControl, FormGroup} from '../model';
|
2016-06-08 19:38:52 -04:00
|
|
|
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';
|
|
|
|
|
|
|
|
import {ControlContainer} from './control_container';
|
2016-06-08 18:36:24 -04:00
|
|
|
import {Form} from './form_interface';
|
2016-06-08 19:38:52 -04:00
|
|
|
import {NgControl} from './ng_control';
|
2016-06-15 18:15:41 -04:00
|
|
|
import {NgModel} from './ng_model';
|
2016-06-12 19:37:42 -04:00
|
|
|
import {NgModelGroup} from './ng_model_group';
|
2016-06-25 16:27:29 -04:00
|
|
|
import {composeAsyncValidators, composeValidators, setUpControl, setUpFormContainer} from './shared';
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2016-07-30 22:18:14 -04:00
|
|
|
export const formDirectiveProvider: any = {
|
|
|
|
provide: ControlContainer,
|
|
|
|
useExisting: forwardRef(() => NgForm)
|
|
|
|
};
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2016-08-02 18:53:34 -04:00
|
|
|
const resolvedPromise = Promise.resolve(null);
|
|
|
|
|
2016-06-08 18:36:24 -04:00
|
|
|
/**
|
|
|
|
* If `NgForm` is bound in a component, `<form>` elements in that component will be
|
|
|
|
* upgraded to use the Angular form system.
|
|
|
|
*
|
|
|
|
* ### Typical Use
|
|
|
|
*
|
2016-07-08 02:02:35 -04:00
|
|
|
* Include `FORM_DIRECTIVES` in the `directives` section of a {@link Component} annotation
|
2016-06-08 18:36:24 -04:00
|
|
|
* to use `NgForm` and its associated controls.
|
|
|
|
*
|
|
|
|
* ### Structure
|
|
|
|
*
|
2016-06-10 14:15:59 -04:00
|
|
|
* An Angular form is a collection of `FormControl`s in some hierarchy.
|
|
|
|
* `FormControl`s can be at the top level or can be organized in `FormGroup`s
|
|
|
|
* or `FormArray`s. This hierarchy is reflected in the form's `value`, a
|
2016-06-08 18:36:24 -04:00
|
|
|
* JSON object that mirrors the form structure.
|
|
|
|
*
|
|
|
|
* ### Submission
|
|
|
|
*
|
|
|
|
* The `ngSubmit` event signals when the user triggers a form submission.
|
|
|
|
*
|
|
|
|
* ```typescript
|
|
|
|
* @Component({
|
|
|
|
* selector: 'my-app',
|
|
|
|
* template: `
|
|
|
|
* <div>
|
|
|
|
* <p>Submit the form to see the data object Angular builds</p>
|
|
|
|
* <h2>NgForm demo</h2>
|
|
|
|
* <form #f="ngForm" (ngSubmit)="onSubmit(f.value)">
|
|
|
|
* <h3>Control group: credentials</h3>
|
2016-06-12 19:37:42 -04:00
|
|
|
* <div ngModelGroup="credentials">
|
2016-06-12 16:17:07 -04:00
|
|
|
* <p>Login: <input type="text" name="login" ngModel></p>
|
|
|
|
* <p>Password: <input type="password" name="password" ngModel></p>
|
2016-06-08 18:36:24 -04:00
|
|
|
* </div>
|
|
|
|
* <h3>Control group: person</h3>
|
2016-06-12 19:37:42 -04:00
|
|
|
* <div ngModelGroup="person">
|
2016-06-12 16:17:07 -04:00
|
|
|
* <p>First name: <input type="text" name="firstName" ngModel></p>
|
|
|
|
* <p>Last name: <input type="text" name="lastName" ngModel></p>
|
2016-06-08 18:36:24 -04:00
|
|
|
* </div>
|
|
|
|
* <button type="submit">Submit Form</button>
|
|
|
|
* <p>Form data submitted:</p>
|
|
|
|
* </form>
|
|
|
|
* <pre>{{data}}</pre>
|
|
|
|
* </div>
|
|
|
|
* `,
|
2016-06-12 16:17:07 -04:00
|
|
|
* directives: []
|
2016-06-08 18:36:24 -04:00
|
|
|
* })
|
|
|
|
* export class App {
|
|
|
|
* constructor() {}
|
|
|
|
*
|
|
|
|
* data: string;
|
|
|
|
*
|
|
|
|
* onSubmit(data) {
|
|
|
|
* this.data = JSON.stringify(data, null, 2);
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* ```
|
|
|
|
*
|
2016-08-17 10:44:39 -04:00
|
|
|
* @stable
|
2016-06-08 18:36:24 -04:00
|
|
|
*/
|
|
|
|
@Directive({
|
2016-06-10 22:10:17 -04:00
|
|
|
selector: 'form:not([ngNoForm]):not([formGroup]),ngForm,[ngForm]',
|
2016-06-08 18:36:24 -04:00
|
|
|
providers: [formDirectiveProvider],
|
2016-07-12 18:02:25 -04:00
|
|
|
host: {'(submit)': 'onSubmit()', '(reset)': 'onReset()'},
|
2016-06-08 18:36:24 -04:00
|
|
|
outputs: ['ngSubmit'],
|
|
|
|
exportAs: 'ngForm'
|
|
|
|
})
|
|
|
|
export class NgForm extends ControlContainer implements Form {
|
|
|
|
private _submitted: boolean = false;
|
|
|
|
|
2016-06-10 14:15:59 -04:00
|
|
|
form: FormGroup;
|
2016-06-08 18:36:24 -04:00
|
|
|
ngSubmit = new EventEmitter();
|
|
|
|
|
2016-06-08 19:38:52 -04:00
|
|
|
constructor(
|
|
|
|
@Optional() @Self() @Inject(NG_VALIDATORS) validators: any[],
|
|
|
|
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: any[]) {
|
2016-06-08 18:36:24 -04:00
|
|
|
super();
|
2016-08-24 19:58:43 -04:00
|
|
|
this.form =
|
|
|
|
new FormGroup({}, composeValidators(validators), composeAsyncValidators(asyncValidators));
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
get submitted(): boolean { return this._submitted; }
|
|
|
|
|
|
|
|
get formDirective(): Form { return this; }
|
|
|
|
|
2016-06-10 14:15:59 -04:00
|
|
|
get control(): FormGroup { return this.form; }
|
2016-06-08 18:36:24 -04:00
|
|
|
|
|
|
|
get path(): string[] { return []; }
|
|
|
|
|
|
|
|
get controls(): {[key: string]: AbstractControl} { return this.form.controls; }
|
|
|
|
|
2016-06-23 12:55:26 -04:00
|
|
|
addControl(dir: NgModel): void {
|
2016-08-02 18:53:34 -04:00
|
|
|
resolvedPromise.then(() => {
|
2016-06-10 13:09:50 -04:00
|
|
|
const container = this._findContainer(dir.path);
|
2016-06-23 12:55:26 -04:00
|
|
|
dir._control = <FormControl>container.registerControl(dir.name, dir.control);
|
2016-06-15 18:15:41 -04:00
|
|
|
setUpControl(dir.control, dir);
|
|
|
|
dir.control.updateValueAndValidity({emitEvent: false});
|
2016-06-08 18:36:24 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-08-05 16:35:17 -04:00
|
|
|
getControl(dir: NgModel): FormControl { return <FormControl>this.form.get(dir.path); }
|
2016-06-08 18:36:24 -04:00
|
|
|
|
2016-06-15 18:15:41 -04:00
|
|
|
removeControl(dir: NgModel): void {
|
2016-08-02 18:53:34 -04:00
|
|
|
resolvedPromise.then(() => {
|
2016-06-08 18:36:24 -04:00
|
|
|
var container = this._findContainer(dir.path);
|
|
|
|
if (isPresent(container)) {
|
|
|
|
container.removeControl(dir.name);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-06-12 19:37:42 -04:00
|
|
|
addFormGroup(dir: NgModelGroup): void {
|
2016-08-02 18:53:34 -04:00
|
|
|
resolvedPromise.then(() => {
|
2016-06-08 18:36:24 -04:00
|
|
|
var container = this._findContainer(dir.path);
|
2016-06-10 14:15:59 -04:00
|
|
|
var group = new FormGroup({});
|
2016-06-25 16:27:29 -04:00
|
|
|
setUpFormContainer(group, dir);
|
2016-06-08 18:36:24 -04:00
|
|
|
container.registerControl(dir.name, group);
|
|
|
|
group.updateValueAndValidity({emitEvent: false});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-06-12 19:37:42 -04:00
|
|
|
removeFormGroup(dir: NgModelGroup): void {
|
2016-08-02 18:53:34 -04:00
|
|
|
resolvedPromise.then(() => {
|
2016-06-08 18:36:24 -04:00
|
|
|
var container = this._findContainer(dir.path);
|
|
|
|
if (isPresent(container)) {
|
|
|
|
container.removeControl(dir.name);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-08-05 16:35:17 -04:00
|
|
|
getFormGroup(dir: NgModelGroup): FormGroup { return <FormGroup>this.form.get(dir.path); }
|
2016-06-08 18:36:24 -04:00
|
|
|
|
|
|
|
updateModel(dir: NgControl, value: any): void {
|
2016-08-02 18:53:34 -04:00
|
|
|
resolvedPromise.then(() => {
|
2016-08-05 16:35:17 -04:00
|
|
|
var ctrl = <FormControl>this.form.get(dir.path);
|
|
|
|
ctrl.setValue(value);
|
2016-06-08 18:36:24 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-08-05 16:35:17 -04:00
|
|
|
setValue(value: {[key: string]: any}): void { this.control.setValue(value); }
|
2016-07-08 16:04:25 -04:00
|
|
|
|
2016-06-08 18:36:24 -04:00
|
|
|
onSubmit(): boolean {
|
|
|
|
this._submitted = true;
|
2016-08-02 18:53:34 -04:00
|
|
|
this.ngSubmit.emit(null);
|
2016-06-08 18:36:24 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-08-12 02:27:33 -04:00
|
|
|
onReset(): void { this.resetForm(); }
|
|
|
|
|
|
|
|
resetForm(value: any = undefined): void {
|
|
|
|
this.form.reset(value);
|
|
|
|
this._submitted = false;
|
|
|
|
}
|
2016-07-12 18:02:25 -04:00
|
|
|
|
2016-06-08 18:36:24 -04:00
|
|
|
/** @internal */
|
2016-06-10 14:15:59 -04:00
|
|
|
_findContainer(path: string[]): FormGroup {
|
2016-06-08 18:36:24 -04:00
|
|
|
path.pop();
|
2016-08-05 16:35:17 -04:00
|
|
|
return ListWrapper.isEmpty(path) ? this.form : <FormGroup>this.form.get(path);
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
}
|