2015-05-30 11:56:00 -07:00
|
|
|
import {StringWrapper, isPresent} from 'angular2/src/facade/lang';
|
2015-04-14 14:34:41 -07:00
|
|
|
import {Observable, EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
2015-03-25 10:51:05 -07:00
|
|
|
import {StringMap, StringMapWrapper, ListWrapper, List} from 'angular2/src/facade/collection';
|
2015-03-19 14:21:40 -07:00
|
|
|
import {Validators} from './validators';
|
2015-02-11 11:10:31 -08:00
|
|
|
|
2015-03-31 22:47:11 +00:00
|
|
|
/**
|
2015-04-10 11:15:01 -07:00
|
|
|
* Indicates that a Control is valid, i.e. that no errors exist in the input value.
|
|
|
|
*
|
2015-04-10 12:45:02 +02:00
|
|
|
* @exportedAs angular2/forms
|
2015-03-31 22:47:11 +00:00
|
|
|
*/
|
2015-02-11 11:10:31 -08:00
|
|
|
export const VALID = "VALID";
|
2015-03-31 22:47:11 +00:00
|
|
|
|
|
|
|
/**
|
2015-04-10 11:15:01 -07:00
|
|
|
* Indicates that a Control is invalid, i.e. that an error exists in the input value.
|
|
|
|
*
|
2015-04-10 12:45:02 +02:00
|
|
|
* @exportedAs angular2/forms
|
2015-03-31 22:47:11 +00:00
|
|
|
*/
|
2015-02-11 11:10:31 -08:00
|
|
|
export const INVALID = "INVALID";
|
2015-02-03 07:27:09 -08:00
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
export function isControl(c: Object): boolean {
|
2015-05-11 12:00:56 -07:00
|
|
|
return c instanceof AbstractControl;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-03-31 22:47:11 +00:00
|
|
|
/**
|
2015-04-10 11:15:01 -07:00
|
|
|
* Omitting from external API doc as this is really an abstract internal concept.
|
2015-03-31 22:47:11 +00:00
|
|
|
*/
|
2015-05-20 18:10:30 -07:00
|
|
|
export class AbstractControl {
|
|
|
|
_value: any;
|
|
|
|
_status: string;
|
|
|
|
_errors: StringMap<string, any>;
|
|
|
|
_pristine: boolean;
|
2015-06-02 08:41:33 -07:00
|
|
|
_touched: boolean;
|
2015-05-20 18:10:30 -07:00
|
|
|
_parent: any; /* ControlGroup | ControlArray */
|
|
|
|
validator: Function;
|
2015-02-03 07:27:09 -08:00
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
_valueChanges: EventEmitter;
|
2015-03-25 10:51:05 -07:00
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
constructor(validator: Function) {
|
2015-02-11 11:10:31 -08:00
|
|
|
this.validator = validator;
|
2015-03-19 10:51:49 -07:00
|
|
|
this._pristine = true;
|
2015-06-02 08:41:33 -07:00
|
|
|
this._touched = false;
|
2015-02-11 11:10:31 -08:00
|
|
|
}
|
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
get value(): any { return this._value; }
|
2015-02-24 11:59:10 -08:00
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
get status(): string { return this._status; }
|
2015-02-24 11:59:10 -08:00
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
get valid(): boolean { return this._status === VALID; }
|
2015-02-24 11:59:10 -08:00
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
get errors(): StringMap<string, any> { return this._errors; }
|
2015-02-24 11:59:10 -08:00
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
get pristine(): boolean { return this._pristine; }
|
2015-03-19 10:51:49 -07:00
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
get dirty(): boolean { return !this.pristine; }
|
2015-03-19 10:51:49 -07:00
|
|
|
|
2015-06-02 08:41:33 -07:00
|
|
|
get touched(): boolean { return this._touched; }
|
|
|
|
|
|
|
|
get untouched(): boolean { return !this._touched; }
|
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
get valueChanges(): Observable { return this._valueChanges; }
|
2015-04-14 14:34:41 -07:00
|
|
|
|
2015-06-02 08:41:33 -07:00
|
|
|
touch(): void { this._touched = true; }
|
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
setParent(parent) { this._parent = parent; }
|
2015-02-11 11:10:31 -08:00
|
|
|
|
2015-06-01 10:41:50 -07:00
|
|
|
updateValidity({onlySelf}: {onlySelf?: boolean} = {}): void {
|
|
|
|
onlySelf = isPresent(onlySelf) ? onlySelf : false;
|
|
|
|
|
|
|
|
this._errors = this.validator(this);
|
|
|
|
this._status = isPresent(this._errors) ? INVALID : VALID;
|
|
|
|
if (isPresent(this._parent) && !onlySelf) {
|
|
|
|
this._parent.updateValidity({onlySelf: onlySelf});
|
2015-02-11 11:10:31 -08:00
|
|
|
}
|
2015-02-03 07:27:09 -08:00
|
|
|
}
|
2015-05-30 11:56:00 -07:00
|
|
|
|
2015-06-01 10:41:50 -07:00
|
|
|
updateValueAndValidity({onlySelf, emitEvent}: {onlySelf?: boolean,
|
|
|
|
emitEvent?: boolean} = {}): void {
|
|
|
|
onlySelf = isPresent(onlySelf) ? onlySelf : false;
|
|
|
|
emitEvent = isPresent(emitEvent) ? emitEvent : true;
|
|
|
|
|
|
|
|
this._updateValue();
|
|
|
|
this._pristine = false;
|
|
|
|
|
|
|
|
if (emitEvent) {
|
|
|
|
ObservableWrapper.callNext(this._valueChanges, this._value);
|
|
|
|
}
|
|
|
|
|
2015-05-30 11:56:00 -07:00
|
|
|
this._errors = this.validator(this);
|
|
|
|
this._status = isPresent(this._errors) ? INVALID : VALID;
|
2015-06-01 10:41:50 -07:00
|
|
|
if (isPresent(this._parent) && !onlySelf) {
|
|
|
|
this._parent.updateValueAndValidity({onlySelf: onlySelf, emitEvent: emitEvent});
|
2015-05-30 11:56:00 -07:00
|
|
|
}
|
|
|
|
}
|
2015-06-01 10:41:50 -07:00
|
|
|
|
|
|
|
_updateValue(): void {}
|
2015-02-03 07:27:09 -08:00
|
|
|
}
|
|
|
|
|
2015-03-31 22:47:11 +00:00
|
|
|
/**
|
2015-04-10 11:15:01 -07:00
|
|
|
* Defines a part of a form that cannot be divided into other controls.
|
|
|
|
*
|
2015-05-20 18:10:30 -07:00
|
|
|
* `Control` is one of the three fundamental building blocks used to define forms in Angular, along
|
|
|
|
* with
|
2015-04-17 13:01:07 -07:00
|
|
|
* {@link ControlGroup} and {@link ControlArray}.
|
2015-04-10 11:15:01 -07:00
|
|
|
*
|
2015-04-10 12:45:02 +02:00
|
|
|
* @exportedAs angular2/forms
|
2015-03-31 22:47:11 +00:00
|
|
|
*/
|
2015-02-25 15:10:27 -08:00
|
|
|
export class Control extends AbstractControl {
|
2015-06-01 10:41:50 -07:00
|
|
|
_onChange: Function;
|
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
constructor(value: any, validator: Function = Validators.nullValidator) {
|
2015-02-25 15:10:27 -08:00
|
|
|
super(validator);
|
2015-06-01 10:41:50 -07:00
|
|
|
this._value = value;
|
|
|
|
this.updateValidity({onlySelf: true});
|
2015-04-14 14:34:41 -07:00
|
|
|
this._valueChanges = new EventEmitter();
|
2015-02-03 07:27:09 -08:00
|
|
|
}
|
|
|
|
|
2015-06-01 10:41:50 -07:00
|
|
|
updateValue(value: any,
|
|
|
|
{onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
|
2015-03-24 13:45:47 -07:00
|
|
|
this._value = value;
|
2015-06-01 10:41:50 -07:00
|
|
|
if (isPresent(this._onChange)) this._onChange(this._value);
|
|
|
|
this.updateValueAndValidity({onlySelf: onlySelf, emitEvent: emitEvent});
|
2015-02-03 07:27:09 -08:00
|
|
|
}
|
2015-06-01 10:41:50 -07:00
|
|
|
|
|
|
|
registerOnChange(fn: Function): void { this._onChange = fn; }
|
2015-02-25 15:10:27 -08:00
|
|
|
}
|
2015-02-11 11:10:31 -08:00
|
|
|
|
2015-03-31 22:47:11 +00:00
|
|
|
/**
|
2015-04-10 11:15:01 -07:00
|
|
|
* Defines a part of a form, of fixed length, that can contain other controls.
|
|
|
|
*
|
2015-05-20 18:10:30 -07:00
|
|
|
* A ControlGroup aggregates the values and errors of each {@link Control} in the group. Thus, if
|
|
|
|
* one of the controls
|
|
|
|
* in a group is invalid, the entire group is invalid. Similarly, if a control changes its value,
|
|
|
|
* the entire group
|
2015-04-17 13:01:07 -07:00
|
|
|
* changes as well.
|
2015-04-10 11:15:01 -07:00
|
|
|
*
|
2015-05-20 18:10:30 -07:00
|
|
|
* `ControlGroup` is one of the three fundamental building blocks used to define forms in Angular,
|
|
|
|
* along with
|
|
|
|
* {@link Control} and {@link ControlArray}. {@link ControlArray} can also contain other controls,
|
|
|
|
* but is of variable
|
2015-04-17 13:01:07 -07:00
|
|
|
* length.
|
2015-04-10 11:15:01 -07:00
|
|
|
*
|
2015-04-10 12:45:02 +02:00
|
|
|
* @exportedAs angular2/forms
|
2015-03-31 22:47:11 +00:00
|
|
|
*/
|
2015-02-25 15:10:27 -08:00
|
|
|
export class ControlGroup extends AbstractControl {
|
2015-05-20 18:10:30 -07:00
|
|
|
controls: StringMap<string, AbstractControl>;
|
|
|
|
_optionals: StringMap<string, boolean>;
|
2015-02-24 11:59:10 -08:00
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
constructor(controls: StringMap<String, AbstractControl>,
|
|
|
|
optionals: StringMap<String, boolean> = null,
|
|
|
|
validator: Function = Validators.group) {
|
2015-02-25 15:10:27 -08:00
|
|
|
super(validator);
|
|
|
|
this.controls = controls;
|
2015-03-26 19:50:13 -07:00
|
|
|
this._optionals = isPresent(optionals) ? optionals : {};
|
2015-03-24 13:45:47 -07:00
|
|
|
|
2015-04-14 14:34:41 -07:00
|
|
|
this._valueChanges = new EventEmitter();
|
2015-03-24 13:45:47 -07:00
|
|
|
|
2015-02-25 15:10:27 -08:00
|
|
|
this._setParentForControls();
|
2015-06-01 10:41:50 -07:00
|
|
|
this._value = this._reduceValue();
|
|
|
|
this.updateValidity({onlySelf: true});
|
2015-02-11 11:10:31 -08:00
|
|
|
}
|
|
|
|
|
2015-05-30 11:56:00 -07:00
|
|
|
addControl(name: string, c: AbstractControl) { this.controls[name] = c; }
|
|
|
|
|
|
|
|
removeControl(name: string) { StringMapWrapper.delete(this.controls, name); }
|
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
include(controlName: string): void {
|
2015-03-26 19:50:13 -07:00
|
|
|
StringMapWrapper.set(this._optionals, controlName, true);
|
2015-06-01 10:41:50 -07:00
|
|
|
this.updateValueAndValidity();
|
2015-03-10 18:12:30 -07:00
|
|
|
}
|
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
exclude(controlName: string): void {
|
2015-03-26 19:50:13 -07:00
|
|
|
StringMapWrapper.set(this._optionals, controlName, false);
|
2015-06-01 10:41:50 -07:00
|
|
|
this.updateValueAndValidity();
|
2015-03-10 18:12:30 -07:00
|
|
|
}
|
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
contains(controlName: string): boolean {
|
2015-03-10 18:12:30 -07:00
|
|
|
var c = StringMapWrapper.contains(this.controls, controlName);
|
|
|
|
return c && this._included(controlName);
|
|
|
|
}
|
|
|
|
|
2015-05-30 11:56:00 -07:00
|
|
|
find(path: string | List<string>): AbstractControl {
|
|
|
|
if (!(path instanceof List)) {
|
|
|
|
path = StringWrapper.split(<string>path, new RegExp("/"));
|
|
|
|
}
|
|
|
|
|
|
|
|
return ListWrapper.reduce(
|
|
|
|
<List<string>>path, (v, name) => v instanceof ControlGroup && isPresent(v.controls[name]) ?
|
|
|
|
v.controls[name] :
|
|
|
|
null,
|
|
|
|
this);
|
|
|
|
}
|
|
|
|
|
2015-02-11 11:10:31 -08:00
|
|
|
_setParentForControls() {
|
2015-05-20 18:10:30 -07:00
|
|
|
StringMapWrapper.forEach(this.controls, (control, name) => { control.setParent(this); });
|
2015-02-11 11:10:31 -08:00
|
|
|
}
|
|
|
|
|
2015-06-01 10:41:50 -07:00
|
|
|
_updateValue() { this._value = this._reduceValue(); }
|
2015-02-24 11:59:10 -08:00
|
|
|
|
|
|
|
_reduceValue() {
|
2015-03-19 10:51:49 -07:00
|
|
|
return this._reduceChildren({}, (acc, control, name) => {
|
|
|
|
acc[name] = control.value;
|
|
|
|
return acc;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
_reduceChildren(initValue: any, fn: Function) {
|
2015-03-19 10:51:49 -07:00
|
|
|
var res = initValue;
|
2015-02-24 11:59:10 -08:00
|
|
|
StringMapWrapper.forEach(this.controls, (control, name) => {
|
2015-03-10 18:12:30 -07:00
|
|
|
if (this._included(name)) {
|
2015-03-19 10:51:49 -07:00
|
|
|
res = fn(res, control, name);
|
2015-02-24 11:59:10 -08:00
|
|
|
}
|
|
|
|
});
|
2015-03-19 10:51:49 -07:00
|
|
|
return res;
|
2015-02-11 11:10:31 -08:00
|
|
|
}
|
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
_included(controlName: string): boolean {
|
2015-03-26 19:50:13 -07:00
|
|
|
var isOptional = StringMapWrapper.contains(this._optionals, controlName);
|
|
|
|
return !isOptional || StringMapWrapper.get(this._optionals, controlName);
|
2015-02-24 11:59:10 -08:00
|
|
|
}
|
2015-03-25 10:51:05 -07:00
|
|
|
}
|
|
|
|
|
2015-03-31 22:47:11 +00:00
|
|
|
/**
|
2015-04-10 11:15:01 -07:00
|
|
|
* Defines a part of a form, of variable length, that can contain other controls.
|
|
|
|
*
|
2015-05-20 18:10:30 -07:00
|
|
|
* A `ControlArray` aggregates the values and errors of each {@link Control} in the group. Thus, if
|
|
|
|
* one of the controls
|
|
|
|
* in a group is invalid, the entire group is invalid. Similarly, if a control changes its value,
|
|
|
|
* the entire group
|
2015-04-17 13:01:07 -07:00
|
|
|
* changes as well.
|
2015-04-10 11:15:01 -07:00
|
|
|
*
|
2015-05-20 18:10:30 -07:00
|
|
|
* `ControlArray` is one of the three fundamental building blocks used to define forms in Angular,
|
|
|
|
* along with
|
|
|
|
* {@link Control} and {@link ControlGroup}. {@link ControlGroup} can also contain other controls,
|
|
|
|
* but is of fixed
|
2015-04-17 13:01:07 -07:00
|
|
|
* length.
|
2015-04-10 11:15:01 -07:00
|
|
|
*
|
2015-04-10 12:45:02 +02:00
|
|
|
* @exportedAs angular2/forms
|
2015-03-31 22:47:11 +00:00
|
|
|
*/
|
2015-03-25 10:51:05 -07:00
|
|
|
export class ControlArray extends AbstractControl {
|
2015-05-20 18:10:30 -07:00
|
|
|
controls: List<AbstractControl>;
|
2015-03-25 10:51:05 -07:00
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
constructor(controls: List<AbstractControl>, validator: Function = Validators.array) {
|
2015-03-25 10:51:05 -07:00
|
|
|
super(validator);
|
|
|
|
this.controls = controls;
|
|
|
|
|
2015-04-14 14:34:41 -07:00
|
|
|
this._valueChanges = new EventEmitter();
|
2015-03-25 10:51:05 -07:00
|
|
|
|
|
|
|
this._setParentForControls();
|
2015-06-01 10:41:50 -07:00
|
|
|
this._updateValue();
|
|
|
|
this.updateValidity({onlySelf: true});
|
2015-03-25 10:51:05 -07:00
|
|
|
}
|
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
at(index: number): AbstractControl { return this.controls[index]; }
|
2015-03-25 10:51:05 -07:00
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
push(control: AbstractControl): void {
|
2015-03-25 10:51:05 -07:00
|
|
|
ListWrapper.push(this.controls, control);
|
|
|
|
control.setParent(this);
|
2015-06-01 10:41:50 -07:00
|
|
|
this.updateValueAndValidity();
|
2015-03-25 10:51:05 -07:00
|
|
|
}
|
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
insert(index: number, control: AbstractControl): void {
|
2015-03-25 10:51:05 -07:00
|
|
|
ListWrapper.insert(this.controls, index, control);
|
|
|
|
control.setParent(this);
|
2015-06-01 10:41:50 -07:00
|
|
|
this.updateValueAndValidity();
|
2015-03-25 10:51:05 -07:00
|
|
|
}
|
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
removeAt(index: number): void {
|
2015-03-25 10:51:05 -07:00
|
|
|
ListWrapper.removeAt(this.controls, index);
|
2015-06-01 10:41:50 -07:00
|
|
|
this.updateValueAndValidity();
|
2015-03-25 10:51:05 -07:00
|
|
|
}
|
|
|
|
|
2015-05-20 18:10:30 -07:00
|
|
|
get length(): number { return this.controls.length; }
|
2015-03-25 10:51:05 -07:00
|
|
|
|
2015-06-01 10:41:50 -07:00
|
|
|
_updateValue() { this._value = ListWrapper.map(this.controls, (c) => c.value); }
|
2015-03-25 10:51:05 -07:00
|
|
|
|
|
|
|
_setParentForControls() {
|
2015-05-20 18:10:30 -07:00
|
|
|
ListWrapper.forEach(this.controls, (control) => { control.setParent(this); });
|
2015-03-25 10:51:05 -07:00
|
|
|
}
|
2015-03-24 11:01:26 -04:00
|
|
|
}
|