2016-06-08 19:38:52 -04:00
|
|
|
import {Directive, Inject, Optional, Self, forwardRef} from '@angular/core';
|
|
|
|
|
2016-06-14 21:23:40 -04:00
|
|
|
import {EventEmitter, ObservableWrapper, PromiseWrapper} from '../facade/async';
|
|
|
|
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-12 19:37:42 -04:00
|
|
|
import {NgModelGroup} from './ng_model_group';
|
2016-06-10 14:15:59 -04:00
|
|
|
import {composeAsyncValidators, composeValidators, setUpControl, setUpFormGroup} from './shared';
|
2016-06-08 18:36:24 -04:00
|
|
|
|
|
|
|
export const formDirectiveProvider: any =
|
|
|
|
/*@ts2dart_const*/ {provide: ControlContainer, useExisting: forwardRef(() => NgForm)};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If `NgForm` is bound in a component, `<form>` elements in that component will be
|
|
|
|
* upgraded to use the Angular form system.
|
|
|
|
*
|
|
|
|
* ### Typical Use
|
|
|
|
*
|
|
|
|
* Include `FORM_DIRECTIVES` in the `directives` section of a {@link View} annotation
|
|
|
|
* 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);
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @experimental
|
|
|
|
*/
|
|
|
|
@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],
|
|
|
|
host: {
|
|
|
|
'(submit)': 'onSubmit()',
|
|
|
|
},
|
|
|
|
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-06-10 14:15:59 -04:00
|
|
|
this.form = new FormGroup(
|
2016-06-08 19:38:52 -04:00
|
|
|
{}, null, 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-10 14:15:59 -04:00
|
|
|
addControl(dir: NgControl): FormControl {
|
|
|
|
const ctrl = new FormControl();
|
2016-06-08 18:36:24 -04:00
|
|
|
PromiseWrapper.scheduleMicrotask(() => {
|
2016-06-10 13:09:50 -04:00
|
|
|
const container = this._findContainer(dir.path);
|
2016-06-08 18:36:24 -04:00
|
|
|
setUpControl(ctrl, dir);
|
|
|
|
container.registerControl(dir.name, ctrl);
|
|
|
|
ctrl.updateValueAndValidity({emitEvent: false});
|
|
|
|
});
|
2016-06-10 13:09:50 -04:00
|
|
|
return ctrl;
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
|
2016-06-10 14:15:59 -04:00
|
|
|
getControl(dir: NgControl): FormControl { return <FormControl>this.form.find(dir.path); }
|
2016-06-08 18:36:24 -04:00
|
|
|
|
|
|
|
removeControl(dir: NgControl): void {
|
|
|
|
PromiseWrapper.scheduleMicrotask(() => {
|
|
|
|
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-06-08 18:36:24 -04:00
|
|
|
PromiseWrapper.scheduleMicrotask(() => {
|
|
|
|
var container = this._findContainer(dir.path);
|
2016-06-10 14:15:59 -04:00
|
|
|
var group = new FormGroup({});
|
|
|
|
setUpFormGroup(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-06-08 18:36:24 -04:00
|
|
|
PromiseWrapper.scheduleMicrotask(() => {
|
|
|
|
var container = this._findContainer(dir.path);
|
|
|
|
if (isPresent(container)) {
|
|
|
|
container.removeControl(dir.name);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-06-12 19:37:42 -04:00
|
|
|
getFormGroup(dir: NgModelGroup): FormGroup { return <FormGroup>this.form.find(dir.path); }
|
2016-06-08 18:36:24 -04:00
|
|
|
|
|
|
|
updateModel(dir: NgControl, value: any): void {
|
|
|
|
PromiseWrapper.scheduleMicrotask(() => {
|
2016-06-10 14:15:59 -04:00
|
|
|
var ctrl = <FormControl>this.form.find(dir.path);
|
2016-06-08 18:36:24 -04:00
|
|
|
ctrl.updateValue(value);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
onSubmit(): boolean {
|
|
|
|
this._submitted = true;
|
|
|
|
ObservableWrapper.callEmit(this.ngSubmit, null);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @internal */
|
2016-06-10 14:15:59 -04:00
|
|
|
_findContainer(path: string[]): FormGroup {
|
2016-06-08 18:36:24 -04:00
|
|
|
path.pop();
|
2016-06-10 14:15:59 -04:00
|
|
|
return ListWrapper.isEmpty(path) ? this.form : <FormGroup>this.form.find(path);
|
2016-06-08 18:36:24 -04:00
|
|
|
}
|
|
|
|
}
|