2015-02-11 11:10:31 -08:00
|
|
|
import {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-02-24 11:59:10 -08:00
|
|
|
//interface IControl {
|
|
|
|
// get value():any;
|
|
|
|
// validator:Function;
|
|
|
|
// get status():string;
|
2015-02-25 12:54:27 -08:00
|
|
|
// get valid():boolean;
|
2015-02-24 11:59:10 -08:00
|
|
|
// get errors():Map;
|
2015-03-19 10:51:49 -07:00
|
|
|
// get pristine():boolean;
|
|
|
|
// get dirty():boolean;
|
2015-02-24 11:59:10 -08:00
|
|
|
// updateValue(value:any){}
|
|
|
|
// setParent(parent){}
|
|
|
|
//}
|
|
|
|
|
2015-05-11 12:00:56 -07:00
|
|
|
export function isControl(c:Object):boolean {
|
|
|
|
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-04-24 13:23:17 -07:00
|
|
|
class AbstractControl {
|
2015-02-24 11:59:10 -08:00
|
|
|
_value:any;
|
|
|
|
_status:string;
|
2015-03-26 19:50:13 -07:00
|
|
|
_errors:StringMap;
|
2015-03-19 10:51:49 -07:00
|
|
|
_pristine:boolean;
|
2015-03-25 10:51:05 -07:00
|
|
|
_parent:any; /* ControlGroup | ControlArray */
|
2015-02-24 11:59:10 -08:00
|
|
|
validator:Function;
|
2015-02-03 07:27:09 -08:00
|
|
|
|
2015-04-14 14:34:41 -07:00
|
|
|
_valueChanges:EventEmitter;
|
2015-03-25 10:51:05 -07:00
|
|
|
|
2015-03-19 14:21:40 -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-02-11 11:10:31 -08:00
|
|
|
}
|
|
|
|
|
2015-03-26 19:50:13 -07:00
|
|
|
get value():any {
|
2015-02-24 11:59:10 -08:00
|
|
|
return this._value;
|
|
|
|
}
|
|
|
|
|
2015-03-26 19:50:13 -07:00
|
|
|
get status():string {
|
2015-02-24 11:59:10 -08:00
|
|
|
return this._status;
|
|
|
|
}
|
|
|
|
|
2015-03-26 19:50:13 -07:00
|
|
|
get valid():boolean {
|
2015-02-24 11:59:10 -08:00
|
|
|
return this._status === VALID;
|
|
|
|
}
|
|
|
|
|
2015-03-26 19:50:13 -07:00
|
|
|
get errors():StringMap {
|
2015-02-24 11:59:10 -08:00
|
|
|
return this._errors;
|
|
|
|
}
|
|
|
|
|
2015-03-26 19:50:13 -07:00
|
|
|
get pristine():boolean {
|
2015-03-19 10:51:49 -07:00
|
|
|
return this._pristine;
|
|
|
|
}
|
|
|
|
|
2015-03-26 19:50:13 -07:00
|
|
|
get dirty():boolean {
|
2015-03-19 10:51:49 -07:00
|
|
|
return ! this.pristine;
|
|
|
|
}
|
|
|
|
|
2015-04-14 14:34:41 -07:00
|
|
|
get valueChanges():Observable {
|
|
|
|
return this._valueChanges;
|
|
|
|
}
|
|
|
|
|
2015-02-24 11:59:10 -08:00
|
|
|
setParent(parent){
|
|
|
|
this._parent = parent;
|
2015-02-11 11:10:31 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
_updateParent() {
|
|
|
|
if (isPresent(this._parent)){
|
2015-03-24 13:45:47 -07:00
|
|
|
this._parent._updateValue();
|
2015-02-11 11:10:31 -08:00
|
|
|
}
|
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-04-17 13:01:07 -07:00
|
|
|
* `Control` is one of the three fundamental building blocks used to define forms in Angular, along with
|
|
|
|
* {@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-03-19 14:21:40 -07:00
|
|
|
constructor(value:any, validator:Function = Validators.nullValidator) {
|
2015-02-25 15:10:27 -08:00
|
|
|
super(validator);
|
2015-03-24 13:45:47 -07:00
|
|
|
this._setValueErrorsStatus(value);
|
2015-04-14 14:34:41 -07:00
|
|
|
this._valueChanges = new EventEmitter();
|
2015-02-03 07:27:09 -08:00
|
|
|
}
|
|
|
|
|
2015-03-26 19:50:13 -07:00
|
|
|
updateValue(value:any):void {
|
2015-03-24 13:45:47 -07:00
|
|
|
this._setValueErrorsStatus(value);
|
2015-03-19 10:51:49 -07:00
|
|
|
this._pristine = false;
|
2015-03-24 13:45:47 -07:00
|
|
|
|
2015-04-14 14:34:41 -07:00
|
|
|
ObservableWrapper.callNext(this._valueChanges, this._value);
|
2015-03-24 13:45:47 -07:00
|
|
|
|
2015-02-25 15:10:27 -08:00
|
|
|
this._updateParent();
|
2015-02-24 11:59:10 -08:00
|
|
|
}
|
|
|
|
|
2015-03-24 13:45:47 -07:00
|
|
|
_setValueErrorsStatus(value) {
|
|
|
|
this._value = value;
|
|
|
|
this._errors = this.validator(this);
|
|
|
|
this._status = isPresent(this._errors) ? INVALID : VALID;
|
2015-02-03 07:27:09 -08:00
|
|
|
}
|
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-04-17 13:01:07 -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
|
|
|
|
* changes as well.
|
2015-04-10 11:15:01 -07:00
|
|
|
*
|
2015-04-17 13:01:07 -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
|
|
|
|
* 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-03-26 19:50:13 -07:00
|
|
|
controls:StringMap;
|
|
|
|
_optionals:StringMap;
|
2015-02-24 11:59:10 -08:00
|
|
|
|
2015-03-26 19:50:13 -07:00
|
|
|
constructor(controls:StringMap, optionals:StringMap = 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-03-24 13:45:47 -07:00
|
|
|
this._setValueErrorsStatus();
|
2015-02-11 11:10:31 -08:00
|
|
|
}
|
|
|
|
|
2015-03-26 19:50:13 -07:00
|
|
|
include(controlName:string):void {
|
|
|
|
StringMapWrapper.set(this._optionals, controlName, true);
|
2015-03-24 13:45:47 -07:00
|
|
|
this._updateValue();
|
2015-03-10 18:12:30 -07:00
|
|
|
}
|
|
|
|
|
2015-03-26 19:50:13 -07:00
|
|
|
exclude(controlName:string):void {
|
|
|
|
StringMapWrapper.set(this._optionals, controlName, false);
|
2015-03-24 13:45:47 -07:00
|
|
|
this._updateValue();
|
2015-03-10 18:12:30 -07:00
|
|
|
}
|
|
|
|
|
2015-03-26 19:50:13 -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-02-11 11:10:31 -08:00
|
|
|
_setParentForControls() {
|
|
|
|
StringMapWrapper.forEach(this.controls, (control, name) => {
|
2015-02-24 11:59:10 -08:00
|
|
|
control.setParent(this);
|
2015-02-11 11:10:31 -08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-03-24 13:45:47 -07:00
|
|
|
_updateValue() {
|
|
|
|
this._setValueErrorsStatus();
|
|
|
|
this._pristine = false;
|
|
|
|
|
2015-04-14 14:34:41 -07:00
|
|
|
ObservableWrapper.callNext(this._valueChanges, this._value);
|
2015-03-24 13:45:47 -07:00
|
|
|
|
|
|
|
this._updateParent();
|
|
|
|
}
|
|
|
|
|
|
|
|
_setValueErrorsStatus() {
|
|
|
|
this._value = this._reduceValue();
|
|
|
|
this._errors = this.validator(this);
|
|
|
|
this._status = isPresent(this._errors) ? INVALID : VALID;
|
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-03-26 19:50:13 -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-03-10 18:12: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-04-17 13:01:07 -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
|
|
|
|
* changes as well.
|
2015-04-10 11:15:01 -07:00
|
|
|
*
|
2015-04-17 13:01:07 -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
|
|
|
|
* 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 {
|
|
|
|
controls:List;
|
|
|
|
|
2015-03-26 19:50:13 -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();
|
|
|
|
this._setValueErrorsStatus();
|
|
|
|
}
|
|
|
|
|
2015-03-26 19:50:13 -07:00
|
|
|
at(index:number):AbstractControl {
|
2015-03-25 10:51:05 -07:00
|
|
|
return this.controls[index];
|
|
|
|
}
|
|
|
|
|
2015-03-26 19:50:13 -07:00
|
|
|
push(control:AbstractControl):void {
|
2015-03-25 10:51:05 -07:00
|
|
|
ListWrapper.push(this.controls, control);
|
|
|
|
control.setParent(this);
|
|
|
|
this._updateValue();
|
|
|
|
}
|
|
|
|
|
2015-03-26 19:50:13 -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);
|
|
|
|
this._updateValue();
|
|
|
|
}
|
|
|
|
|
2015-03-26 19:50:13 -07:00
|
|
|
removeAt(index:number):void {
|
2015-03-25 10:51:05 -07:00
|
|
|
ListWrapper.removeAt(this.controls, index);
|
|
|
|
this._updateValue();
|
|
|
|
}
|
|
|
|
|
2015-03-26 19:50:13 -07:00
|
|
|
get length():number {
|
2015-03-25 10:51:05 -07:00
|
|
|
return this.controls.length;
|
|
|
|
}
|
|
|
|
|
|
|
|
_updateValue() {
|
|
|
|
this._setValueErrorsStatus();
|
|
|
|
this._pristine = false;
|
|
|
|
|
2015-04-14 14:34:41 -07:00
|
|
|
ObservableWrapper.callNext(this._valueChanges, this._value);
|
2015-03-25 10:51:05 -07:00
|
|
|
|
|
|
|
this._updateParent();
|
|
|
|
}
|
|
|
|
|
|
|
|
_setParentForControls() {
|
|
|
|
ListWrapper.forEach(this.controls, (control) => {
|
|
|
|
control.setParent(this);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
_setValueErrorsStatus() {
|
|
|
|
this._value = ListWrapper.map(this.controls, (c) => c.value);
|
|
|
|
this._errors = this.validator(this);
|
|
|
|
this._status = isPresent(this._errors) ? INVALID : VALID;
|
|
|
|
}
|
2015-03-24 11:01:26 -04:00
|
|
|
}
|