feat(forms): migrated forms to typescript
This commit is contained in:
parent
fed86fc8ac
commit
00c3693daa
|
@ -2,11 +2,14 @@
|
|||
* @module
|
||||
* @public
|
||||
* @description
|
||||
* This module is used for handling user input, by defining and building a {@link ControlGroup} that consists of
|
||||
* {@link Control} objects, and mapping them onto the DOM. {@link Control} objects can then be used to read information
|
||||
* This module is used for handling user input, by defining and building a {@link ControlGroup} that
|
||||
* consists of
|
||||
* {@link Control} objects, and mapping them onto the DOM. {@link Control} objects can then be used
|
||||
* to read information
|
||||
* from the form DOM elements.
|
||||
*
|
||||
* This module is not included in the `angular2` module; you must import the forms module explicitly.
|
||||
* This module is not included in the `angular2` module; you must import the forms module
|
||||
* explicitly.
|
||||
*
|
||||
*/
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
library angular2.core.decorators;
|
||||
|
||||
export '../annotations_impl/annotations.dart';
|
||||
export '../annotations_impl/visibility.dart';
|
||||
|
||||
/* This file is empty because, Dart does not have decorators. */
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
import {ComponentAnnotation, DirectiveAnnotation} from './annotations';
|
||||
import {ViewAnnotation} from './view';
|
||||
import {AncestorAnnotation, ParentAnnotation} from './visibility';
|
||||
import {
|
||||
SelfAnnotation,
|
||||
ParentAnnotation,
|
||||
AncestorAnnotation,
|
||||
UnboundedAnnotation
|
||||
} from './visibility';
|
||||
import {AttributeAnnotation, QueryAnnotation} from './di';
|
||||
import {makeDecorator, makeParamDecorator} from '../../util/decorators';
|
||||
|
||||
|
@ -12,8 +17,10 @@ export var Directive = makeDecorator(DirectiveAnnotation);
|
|||
export var View = makeDecorator(ViewAnnotation);
|
||||
|
||||
/* from visibility */
|
||||
export var Ancestor = makeParamDecorator(AncestorAnnotation);
|
||||
export var Self = makeParamDecorator(SelfAnnotation);
|
||||
export var Parent = makeParamDecorator(ParentAnnotation);
|
||||
export var Ancestor = makeParamDecorator(AncestorAnnotation);
|
||||
export var Unbounded = makeParamDecorator(UnboundedAnnotation);
|
||||
|
||||
/* from di */
|
||||
export var Attribute = makeParamDecorator(AttributeAnnotation);
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
export {
|
||||
Self as SelfAnnotation,
|
||||
Ancestor as AncestorAnnotation,
|
||||
Parent as ParentAnnotation,
|
||||
Unbounded as UnboundedAnnotation
|
||||
} from '../annotations_impl/visibility';
|
||||
|
|
|
@ -10,7 +10,6 @@ export class DirectiveResolver {
|
|||
if (isPresent(annotations)) {
|
||||
for (var i = 0; i < annotations.length; i++) {
|
||||
var annotation = annotations[i];
|
||||
|
||||
if (annotation instanceof Directive) {
|
||||
return annotation;
|
||||
}
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
import {Directive} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {Ancestor} from 'angular2/src/core/annotations_impl/visibility';
|
||||
import {Directive, Ancestor} from 'angular2/src/core/annotations/decorators';
|
||||
import {Optional} from 'angular2/src/di/decorators';
|
||||
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
||||
import {Optional} from 'angular2/src/di/annotations_impl';
|
||||
import {Renderer} from 'angular2/src/render/api';
|
||||
import {isPresent, isString, CONST_EXPR, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||
import {
|
||||
isPresent,
|
||||
isString,
|
||||
CONST_EXPR,
|
||||
isBlank,
|
||||
BaseException,
|
||||
Type
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {ControlGroup, Control, isControl} from './model';
|
||||
import {Validators} from './validators';
|
||||
|
||||
//export interface ControlValueAccessor {
|
||||
// writeValue(value):void{}
|
||||
// set onChange(fn){}
|
||||
//}
|
||||
|
||||
function _lookupControl(groupDirective:ControlGroupDirective, controlOrName:any):any {
|
||||
function _lookupControl(groupDirective: ControlGroupDirective, controlOrName: any): any {
|
||||
if (isControl(controlOrName)) {
|
||||
return controlOrName;
|
||||
}
|
||||
|
@ -31,8 +32,10 @@ function _lookupControl(groupDirective:ControlGroupDirective, controlOrName:any)
|
|||
return control;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The default accessor for writing a value and listening to changes that is used by a {@link Control} directive.
|
||||
* The default accessor for writing a value and listening to changes that is used by a {@link
|
||||
* Control} directive.
|
||||
*
|
||||
* This is the default strategy that Angular uses when no other accessor is applied.
|
||||
*
|
||||
|
@ -45,143 +48,17 @@ function _lookupControl(groupDirective:ControlGroupDirective, controlOrName:any)
|
|||
*/
|
||||
@Directive({
|
||||
selector: '[control]',
|
||||
hostListeners: {
|
||||
'change' : 'onChange($event.target.value)',
|
||||
'input' : 'onChange($event.target.value)'
|
||||
},
|
||||
hostProperties: {
|
||||
'value' : 'value'
|
||||
}
|
||||
hostListeners:
|
||||
{'change': 'onChange($event.target.value)', 'input': 'onChange($event.target.value)'},
|
||||
hostProperties: {'value': 'value'}
|
||||
})
|
||||
export class DefaultValueAccessor {
|
||||
value;
|
||||
onChange:Function;
|
||||
onChange: Function;
|
||||
|
||||
constructor() {
|
||||
this.onChange = (_) => {};
|
||||
}
|
||||
constructor() { this.onChange = (_) => {}; }
|
||||
|
||||
writeValue(value) {
|
||||
this.value = value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The accessor for writing a value and listening to changes on a checkbox input element.
|
||||
*
|
||||
*
|
||||
* # Example
|
||||
* ```
|
||||
* <input type="checkbox" [control]="rememberLogin">
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/forms
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'input[type=checkbox][control]',
|
||||
hostListeners: {
|
||||
'change' : 'onChange($event.target.checked)'
|
||||
},
|
||||
hostProperties: {
|
||||
'checked' : 'checked'
|
||||
}
|
||||
})
|
||||
export class CheckboxControlValueAccessor {
|
||||
_elementRef:ElementRef;
|
||||
_renderer:Renderer;
|
||||
|
||||
checked:boolean;
|
||||
onChange:Function;
|
||||
|
||||
constructor(cd:ControlDirective, elementRef:ElementRef, renderer:Renderer) {
|
||||
this.onChange = (_) => {};
|
||||
this._elementRef = elementRef;
|
||||
this._renderer = renderer;
|
||||
cd.valueAccessor = this; //ControlDirective should inject CheckboxControlDirective
|
||||
}
|
||||
|
||||
writeValue(value) {
|
||||
this._renderer.setElementProperty(this._elementRef.parentView.render, this._elementRef.boundElementIndex,
|
||||
'checked', value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a control to a DOM element.
|
||||
*
|
||||
* # Example
|
||||
*
|
||||
* In this example, we bind the control to an input element. When the value of the input element changes, the value of
|
||||
* the control will reflect that change. Likewise, if the value of the control changes, the input element reflects that
|
||||
* change.
|
||||
*
|
||||
* Here we use {@link formDirectives}, rather than importing each form directive individually, e.g.
|
||||
* `ControlDirective`, `ControlGroupDirective`. This is just a shorthand for the same end result.
|
||||
*
|
||||
* ```
|
||||
* @Component({selector: "login-comp"})
|
||||
* @View({
|
||||
* directives: [formDirectives],
|
||||
* inline: "<input type='text' [control]='loginControl'>"
|
||||
* })
|
||||
* class LoginComp {
|
||||
* loginControl:Control;
|
||||
*
|
||||
* constructor() {
|
||||
* this.loginControl = new Control('');
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/forms
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[control]',
|
||||
properties: {
|
||||
'controlOrName' : 'control'
|
||||
}
|
||||
})
|
||||
export class ControlDirective {
|
||||
_groupDirective:ControlGroupDirective;
|
||||
|
||||
_controlOrName:any;
|
||||
valueAccessor:any; //ControlValueAccessor
|
||||
|
||||
validator:Function;
|
||||
|
||||
constructor(@Optional() @Ancestor() groupDirective:ControlGroupDirective, valueAccessor:DefaultValueAccessor) {
|
||||
this._groupDirective = groupDirective;
|
||||
this._controlOrName = null;
|
||||
this.valueAccessor = valueAccessor;
|
||||
this.validator = Validators.nullValidator;
|
||||
}
|
||||
|
||||
set controlOrName(controlOrName) {
|
||||
this._controlOrName = controlOrName;
|
||||
|
||||
if(isPresent(this._groupDirective)) {
|
||||
this._groupDirective.addDirective(this);
|
||||
}
|
||||
|
||||
var c = this._control();
|
||||
c.validator = Validators.compose([c.validator, this.validator]);
|
||||
|
||||
this._updateDomValue();
|
||||
this._setUpUpdateControlValue();
|
||||
}
|
||||
|
||||
_updateDomValue() {
|
||||
this.valueAccessor.writeValue(this._control().value);
|
||||
}
|
||||
|
||||
_setUpUpdateControlValue() {
|
||||
this.valueAccessor.onChange = (newValue) => this._control().updateValue(newValue);
|
||||
}
|
||||
|
||||
_control() {
|
||||
return _lookupControl(this._groupDirective, this._controlOrName);
|
||||
}
|
||||
writeValue(value) { this.value = value }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -189,7 +66,8 @@ export class ControlDirective {
|
|||
*
|
||||
* # Example
|
||||
*
|
||||
* In this example, we bind the control group to the form element, and we bind the login and password controls to the
|
||||
* In this example, we bind the control group to the form element, and we bind the login and
|
||||
* password controls to the
|
||||
* login and password elements.
|
||||
*
|
||||
* Here we use {@link formDirectives}, rather than importing each form directive individually, e.g.
|
||||
|
@ -224,18 +102,13 @@ export class ControlDirective {
|
|||
*
|
||||
* @exportedAs angular2/forms
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[control-group]',
|
||||
properties: {
|
||||
'controlOrName' : 'control-group'
|
||||
}
|
||||
})
|
||||
@Directive({selector: '[control-group]', properties: {'controlOrName': 'control-group'}})
|
||||
export class ControlGroupDirective {
|
||||
_groupDirective:ControlGroupDirective;
|
||||
_directives:List<ControlDirective>;
|
||||
_controlOrName:any;
|
||||
_groupDirective: ControlGroupDirective;
|
||||
_directives: List<ControlDirective>;
|
||||
_controlOrName: any;
|
||||
|
||||
constructor(@Optional() @Ancestor() groupDirective:ControlGroupDirective) {
|
||||
constructor(@Optional() @Ancestor() groupDirective: ControlGroupDirective) {
|
||||
this._groupDirective = groupDirective;
|
||||
this._directives = ListWrapper.create();
|
||||
}
|
||||
|
@ -245,23 +118,126 @@ export class ControlGroupDirective {
|
|||
this._updateDomValue();
|
||||
}
|
||||
|
||||
_updateDomValue() {
|
||||
ListWrapper.forEach(this._directives, (cd) => cd._updateDomValue());
|
||||
}
|
||||
_updateDomValue() { ListWrapper.forEach(this._directives, (cd) => cd._updateDomValue()); }
|
||||
|
||||
addDirective(c:ControlDirective) {
|
||||
ListWrapper.push(this._directives, c);
|
||||
}
|
||||
addDirective(c: ControlDirective) { ListWrapper.push(this._directives, c); }
|
||||
|
||||
findControl(name:string):any {
|
||||
return this._getControlGroup().controls[name];
|
||||
}
|
||||
findControl(name: string): any { return this._getControlGroup().controls[name]; }
|
||||
|
||||
_getControlGroup():ControlGroup {
|
||||
_getControlGroup(): ControlGroup {
|
||||
return _lookupControl(this._groupDirective, this._controlOrName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Binds a control to a DOM element.
|
||||
*
|
||||
* # Example
|
||||
*
|
||||
* In this example, we bind the control to an input element. When the value of the input element
|
||||
* changes, the value of
|
||||
* the control will reflect that change. Likewise, if the value of the control changes, the input
|
||||
* element reflects that
|
||||
* change.
|
||||
*
|
||||
* Here we use {@link formDirectives}, rather than importing each form directive individually, e.g.
|
||||
* `ControlDirective`, `ControlGroupDirective`. This is just a shorthand for the same end result.
|
||||
*
|
||||
* ```
|
||||
* @Component({selector: "login-comp"})
|
||||
* @View({
|
||||
* directives: [formDirectives],
|
||||
* inline: "<input type='text' [control]='loginControl'>"
|
||||
* })
|
||||
* class LoginComp {
|
||||
* loginControl:Control;
|
||||
*
|
||||
* constructor() {
|
||||
* this.loginControl = new Control('');
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/forms
|
||||
*/
|
||||
@Directive({selector: '[control]', properties: {'controlOrName': 'control'}})
|
||||
export class ControlDirective {
|
||||
_groupDirective: ControlGroupDirective;
|
||||
|
||||
_controlOrName: any;
|
||||
valueAccessor: any; // ControlValueAccessor
|
||||
|
||||
validator: Function;
|
||||
|
||||
constructor(@Optional() @Ancestor() groupDirective: ControlGroupDirective,
|
||||
valueAccessor: DefaultValueAccessor) {
|
||||
this._groupDirective = groupDirective;
|
||||
this._controlOrName = null;
|
||||
this.valueAccessor = valueAccessor;
|
||||
this.validator = Validators.nullValidator;
|
||||
}
|
||||
|
||||
set controlOrName(controlOrName) {
|
||||
this._controlOrName = controlOrName;
|
||||
|
||||
if (isPresent(this._groupDirective)) {
|
||||
this._groupDirective.addDirective(this);
|
||||
}
|
||||
|
||||
var c = this._control();
|
||||
c.validator = Validators.compose([c.validator, this.validator]);
|
||||
|
||||
this._updateDomValue();
|
||||
this._setUpUpdateControlValue();
|
||||
}
|
||||
|
||||
_updateDomValue() { this.valueAccessor.writeValue(this._control().value); }
|
||||
|
||||
_setUpUpdateControlValue() {
|
||||
this.valueAccessor.onChange = (newValue) => this._control().updateValue(newValue);
|
||||
}
|
||||
|
||||
_control() { return _lookupControl(this._groupDirective, this._controlOrName); }
|
||||
}
|
||||
|
||||
/**
|
||||
* The accessor for writing a value and listening to changes on a checkbox input element.
|
||||
*
|
||||
*
|
||||
* # Example
|
||||
* ```
|
||||
* <input type="checkbox" [control]="rememberLogin">
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/forms
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'input[type=checkbox][control]',
|
||||
hostListeners: {'change': 'onChange($event.target.checked)'},
|
||||
hostProperties: {'checked': 'checked'}
|
||||
})
|
||||
export class CheckboxControlValueAccessor {
|
||||
_elementRef: ElementRef;
|
||||
_renderer: Renderer;
|
||||
|
||||
checked: boolean;
|
||||
onChange: Function;
|
||||
|
||||
constructor(cd: ControlDirective, elementRef: ElementRef, renderer: Renderer) {
|
||||
this.onChange = (_) => {};
|
||||
this._elementRef = elementRef;
|
||||
this._renderer = renderer;
|
||||
cd.valueAccessor = this; // ControlDirective should inject CheckboxControlDirective
|
||||
}
|
||||
|
||||
writeValue(value) {
|
||||
this._renderer.setElementProperty(this._elementRef.parentView.render,
|
||||
this._elementRef.boundElementIndex, 'checked', value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* A list of all the form directives used as part of a `@View` annotation.
|
||||
|
@ -270,6 +246,5 @@ export class ControlGroupDirective {
|
|||
*
|
||||
* @exportedAs angular2/forms
|
||||
*/
|
||||
export const formDirectives:List = CONST_EXPR([
|
||||
ControlGroupDirective, ControlDirective, CheckboxControlValueAccessor, DefaultValueAccessor
|
||||
]);
|
||||
export const formDirectives: List<Type> = CONST_EXPR(
|
||||
[ControlGroupDirective, ControlDirective, CheckboxControlValueAccessor, DefaultValueAccessor]);
|
|
@ -51,7 +51,8 @@ import * as modelModule from './model';
|
|||
* bootstrap(LoginComp)
|
||||
* ```
|
||||
*
|
||||
* This example creates a {@link ControlGroup} that consists of a `login` {@link Control}, and a nested
|
||||
* This example creates a {@link ControlGroup} that consists of a `login` {@link Control}, and a
|
||||
* nested
|
||||
* {@link ControlGroup} that defines a `password` and a `passwordConfirmation` {@link Control}:
|
||||
*
|
||||
* ```
|
||||
|
@ -68,7 +69,8 @@ import * as modelModule from './model';
|
|||
* @exportedAs angular2/forms
|
||||
*/
|
||||
export class FormBuilder {
|
||||
group(controlsConfig, extra = null):modelModule.ControlGroup {
|
||||
group(controlsConfig: StringMap<string, any>,
|
||||
extra: StringMap<string, any> = null): modelModule.ControlGroup {
|
||||
var controls = this._reduceControls(controlsConfig);
|
||||
var optionals = isPresent(extra) ? StringMapWrapper.get(extra, "optionals") : null;
|
||||
var validator = isPresent(extra) ? StringMapWrapper.get(extra, "validator") : null;
|
||||
|
@ -80,7 +82,7 @@ export class FormBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
control(value, validator:Function = null):modelModule.Control {
|
||||
control(value: Object, validator: Function = null): modelModule.Control {
|
||||
if (isPresent(validator)) {
|
||||
return new modelModule.Control(value, validator);
|
||||
} else {
|
||||
|
@ -88,7 +90,7 @@ export class FormBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
array(controlsConfig:List, validator:Function = null):modelModule.ControlArray {
|
||||
array(controlsConfig: List<any>, validator: Function = null): modelModule.ControlArray {
|
||||
var controls = ListWrapper.map(controlsConfig, (c) => this._createControl(c));
|
||||
if (isPresent(validator)) {
|
||||
return new modelModule.ControlArray(controls, validator);
|
||||
|
@ -97,7 +99,7 @@ export class FormBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
_reduceControls(controlsConfig) {
|
||||
_reduceControls(controlsConfig: any): StringMap<string, modelModule.AbstractControl> {
|
||||
var controls = {};
|
||||
StringMapWrapper.forEach(controlsConfig, (controlConfig, controlName) => {
|
||||
controls[controlName] = this._createControl(controlConfig);
|
||||
|
@ -105,10 +107,10 @@ export class FormBuilder {
|
|||
return controls;
|
||||
}
|
||||
|
||||
_createControl(controlConfig) {
|
||||
if (controlConfig instanceof modelModule.Control ||
|
||||
controlConfig instanceof modelModule.ControlGroup ||
|
||||
controlConfig instanceof modelModule.ControlArray) {
|
||||
_createControl(controlConfig: any): modelModule.AbstractControl {
|
||||
if (controlConfig instanceof modelModule.Control || controlConfig instanceof
|
||||
modelModule.ControlGroup || controlConfig instanceof
|
||||
modelModule.ControlArray) {
|
||||
return controlConfig;
|
||||
|
||||
} else if (ListWrapper.isList(controlConfig)) {
|
|
@ -17,19 +17,7 @@ export const VALID = "VALID";
|
|||
*/
|
||||
export const INVALID = "INVALID";
|
||||
|
||||
//interface IControl {
|
||||
// get value():any;
|
||||
// validator:Function;
|
||||
// get status():string;
|
||||
// get valid():boolean;
|
||||
// get errors():Map;
|
||||
// get pristine():boolean;
|
||||
// get dirty():boolean;
|
||||
// updateValue(value:any){}
|
||||
// setParent(parent){}
|
||||
//}
|
||||
|
||||
export function isControl(c:Object):boolean {
|
||||
export function isControl(c: Object): boolean {
|
||||
return c instanceof AbstractControl;
|
||||
}
|
||||
|
||||
|
@ -37,55 +25,39 @@ export function isControl(c:Object):boolean {
|
|||
/**
|
||||
* Omitting from external API doc as this is really an abstract internal concept.
|
||||
*/
|
||||
class AbstractControl {
|
||||
_value:any;
|
||||
_status:string;
|
||||
_errors:StringMap;
|
||||
_pristine:boolean;
|
||||
_parent:any; /* ControlGroup | ControlArray */
|
||||
validator:Function;
|
||||
export class AbstractControl {
|
||||
_value: any;
|
||||
_status: string;
|
||||
_errors: StringMap<string, any>;
|
||||
_pristine: boolean;
|
||||
_parent: any; /* ControlGroup | ControlArray */
|
||||
validator: Function;
|
||||
|
||||
_valueChanges:EventEmitter;
|
||||
_valueChanges: EventEmitter;
|
||||
|
||||
constructor(validator:Function) {
|
||||
constructor(validator: Function) {
|
||||
this.validator = validator;
|
||||
this._pristine = true;
|
||||
}
|
||||
|
||||
get value():any {
|
||||
return this._value;
|
||||
}
|
||||
get value(): any { return this._value; }
|
||||
|
||||
get status():string {
|
||||
return this._status;
|
||||
}
|
||||
get status(): string { return this._status; }
|
||||
|
||||
get valid():boolean {
|
||||
return this._status === VALID;
|
||||
}
|
||||
get valid(): boolean { return this._status === VALID; }
|
||||
|
||||
get errors():StringMap {
|
||||
return this._errors;
|
||||
}
|
||||
get errors(): StringMap<string, any> { return this._errors; }
|
||||
|
||||
get pristine():boolean {
|
||||
return this._pristine;
|
||||
}
|
||||
get pristine(): boolean { return this._pristine; }
|
||||
|
||||
get dirty():boolean {
|
||||
return ! this.pristine;
|
||||
}
|
||||
get dirty(): boolean { return !this.pristine; }
|
||||
|
||||
get valueChanges():Observable {
|
||||
return this._valueChanges;
|
||||
}
|
||||
get valueChanges(): Observable { return this._valueChanges; }
|
||||
|
||||
setParent(parent){
|
||||
this._parent = parent;
|
||||
}
|
||||
setParent(parent) { this._parent = parent; }
|
||||
|
||||
_updateParent() {
|
||||
if (isPresent(this._parent)){
|
||||
if (isPresent(this._parent)) {
|
||||
this._parent._updateValue();
|
||||
}
|
||||
}
|
||||
|
@ -94,19 +66,20 @@ class AbstractControl {
|
|||
/**
|
||||
* Defines a part of a form that cannot be divided into other controls.
|
||||
*
|
||||
* `Control` is one of the three fundamental building blocks used to define forms in Angular, along with
|
||||
* `Control` is one of the three fundamental building blocks used to define forms in Angular, along
|
||||
* with
|
||||
* {@link ControlGroup} and {@link ControlArray}.
|
||||
*
|
||||
* @exportedAs angular2/forms
|
||||
*/
|
||||
export class Control extends AbstractControl {
|
||||
constructor(value:any, validator:Function = Validators.nullValidator) {
|
||||
constructor(value: any, validator: Function = Validators.nullValidator) {
|
||||
super(validator);
|
||||
this._setValueErrorsStatus(value);
|
||||
this._valueChanges = new EventEmitter();
|
||||
}
|
||||
|
||||
updateValue(value:any):void {
|
||||
updateValue(value: any): void {
|
||||
this._setValueErrorsStatus(value);
|
||||
this._pristine = false;
|
||||
|
||||
|
@ -115,7 +88,7 @@ export class Control extends AbstractControl {
|
|||
this._updateParent();
|
||||
}
|
||||
|
||||
_setValueErrorsStatus(value) {
|
||||
_setValueErrorsStatus(value) {
|
||||
this._value = value;
|
||||
this._errors = this.validator(this);
|
||||
this._status = isPresent(this._errors) ? INVALID : VALID;
|
||||
|
@ -125,21 +98,27 @@ export class Control extends AbstractControl {
|
|||
/**
|
||||
* Defines a part of a form, of fixed length, that can contain other controls.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* `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
|
||||
* `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.
|
||||
*
|
||||
* @exportedAs angular2/forms
|
||||
*/
|
||||
export class ControlGroup extends AbstractControl {
|
||||
controls:StringMap;
|
||||
_optionals:StringMap;
|
||||
controls: StringMap<string, AbstractControl>;
|
||||
_optionals: StringMap<string, boolean>;
|
||||
|
||||
constructor(controls:StringMap, optionals:StringMap = null, validator:Function = Validators.group) {
|
||||
constructor(controls: StringMap<String, AbstractControl>,
|
||||
optionals: StringMap<String, boolean> = null,
|
||||
validator: Function = Validators.group) {
|
||||
super(validator);
|
||||
this.controls = controls;
|
||||
this._optionals = isPresent(optionals) ? optionals : {};
|
||||
|
@ -150,25 +129,23 @@ export class ControlGroup extends AbstractControl {
|
|||
this._setValueErrorsStatus();
|
||||
}
|
||||
|
||||
include(controlName:string):void {
|
||||
include(controlName: string): void {
|
||||
StringMapWrapper.set(this._optionals, controlName, true);
|
||||
this._updateValue();
|
||||
}
|
||||
|
||||
exclude(controlName:string):void {
|
||||
exclude(controlName: string): void {
|
||||
StringMapWrapper.set(this._optionals, controlName, false);
|
||||
this._updateValue();
|
||||
}
|
||||
|
||||
contains(controlName:string):boolean {
|
||||
contains(controlName: string): boolean {
|
||||
var c = StringMapWrapper.contains(this.controls, controlName);
|
||||
return c && this._included(controlName);
|
||||
}
|
||||
|
||||
_setParentForControls() {
|
||||
StringMapWrapper.forEach(this.controls, (control, name) => {
|
||||
control.setParent(this);
|
||||
});
|
||||
StringMapWrapper.forEach(this.controls, (control, name) => { control.setParent(this); });
|
||||
}
|
||||
|
||||
_updateValue() {
|
||||
|
@ -180,7 +157,7 @@ export class ControlGroup extends AbstractControl {
|
|||
this._updateParent();
|
||||
}
|
||||
|
||||
_setValueErrorsStatus() {
|
||||
_setValueErrorsStatus() {
|
||||
this._value = this._reduceValue();
|
||||
this._errors = this.validator(this);
|
||||
this._status = isPresent(this._errors) ? INVALID : VALID;
|
||||
|
@ -193,7 +170,7 @@ export class ControlGroup extends AbstractControl {
|
|||
});
|
||||
}
|
||||
|
||||
_reduceChildren(initValue:any, fn:Function) {
|
||||
_reduceChildren(initValue: any, fn: Function) {
|
||||
var res = initValue;
|
||||
StringMapWrapper.forEach(this.controls, (control, name) => {
|
||||
if (this._included(name)) {
|
||||
|
@ -203,7 +180,7 @@ export class ControlGroup extends AbstractControl {
|
|||
return res;
|
||||
}
|
||||
|
||||
_included(controlName:string):boolean {
|
||||
_included(controlName: string): boolean {
|
||||
var isOptional = StringMapWrapper.contains(this._optionals, controlName);
|
||||
return !isOptional || StringMapWrapper.get(this._optionals, controlName);
|
||||
}
|
||||
|
@ -212,20 +189,24 @@ export class ControlGroup extends AbstractControl {
|
|||
/**
|
||||
* Defines a part of a form, of variable length, that can contain other controls.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* `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
|
||||
* `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.
|
||||
*
|
||||
* @exportedAs angular2/forms
|
||||
*/
|
||||
export class ControlArray extends AbstractControl {
|
||||
controls:List;
|
||||
controls: List<AbstractControl>;
|
||||
|
||||
constructor(controls:List<AbstractControl>, validator:Function = Validators.array) {
|
||||
constructor(controls: List<AbstractControl>, validator: Function = Validators.array) {
|
||||
super(validator);
|
||||
this.controls = controls;
|
||||
|
||||
|
@ -235,30 +216,26 @@ export class ControlArray extends AbstractControl {
|
|||
this._setValueErrorsStatus();
|
||||
}
|
||||
|
||||
at(index:number):AbstractControl {
|
||||
return this.controls[index];
|
||||
}
|
||||
at(index: number): AbstractControl { return this.controls[index]; }
|
||||
|
||||
push(control:AbstractControl):void {
|
||||
push(control: AbstractControl): void {
|
||||
ListWrapper.push(this.controls, control);
|
||||
control.setParent(this);
|
||||
this._updateValue();
|
||||
}
|
||||
|
||||
insert(index:number, control:AbstractControl):void {
|
||||
insert(index: number, control: AbstractControl): void {
|
||||
ListWrapper.insert(this.controls, index, control);
|
||||
control.setParent(this);
|
||||
this._updateValue();
|
||||
}
|
||||
|
||||
removeAt(index:number):void {
|
||||
removeAt(index: number): void {
|
||||
ListWrapper.removeAt(this.controls, index);
|
||||
this._updateValue();
|
||||
}
|
||||
|
||||
get length():number {
|
||||
return this.controls.length;
|
||||
}
|
||||
get length(): number { return this.controls.length; }
|
||||
|
||||
_updateValue() {
|
||||
this._setValueErrorsStatus();
|
||||
|
@ -270,12 +247,10 @@ export class ControlArray extends AbstractControl {
|
|||
}
|
||||
|
||||
_setParentForControls() {
|
||||
ListWrapper.forEach(this.controls, (control) => {
|
||||
control.setParent(this);
|
||||
});
|
||||
ListWrapper.forEach(this.controls, (control) => { control.setParent(this); });
|
||||
}
|
||||
|
||||
_setValueErrorsStatus() {
|
||||
_setValueErrorsStatus() {
|
||||
this._value = ListWrapper.map(this.controls, (c) => c.value);
|
||||
this._errors = this.validator(this);
|
||||
this._status = isPresent(this._errors) ? INVALID : VALID;
|
|
@ -1,13 +1,11 @@
|
|||
import {Directive} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {Directive} from 'angular2/src/core/annotations/decorators';
|
||||
|
||||
import {Validators} from './validators';
|
||||
import {ControlDirective} from './directives';
|
||||
|
||||
@Directive({
|
||||
selector: '[required]'
|
||||
})
|
||||
@Directive({selector: '[required]'})
|
||||
export class RequiredValidatorDirective {
|
||||
constructor(c:ControlDirective) {
|
||||
constructor(c: ControlDirective) {
|
||||
c.validator = Validators.compose([c.validator, Validators.required]);
|
||||
}
|
||||
}
|
|
@ -15,16 +15,14 @@ import * as modelModule from './model';
|
|||
* @exportedAs angular2/forms
|
||||
*/
|
||||
export class Validators {
|
||||
static required(c:modelModule.Control) {
|
||||
static required(c: modelModule.Control): StringMap<string, boolean> {
|
||||
return isBlank(c.value) || c.value == "" ? {"required": true} : null;
|
||||
}
|
||||
|
||||
static nullValidator(c:any) {
|
||||
return null;
|
||||
}
|
||||
static nullValidator(c: any): StringMap<string, boolean> { return null; }
|
||||
|
||||
static compose(validators:List<Function>):Function {
|
||||
return function (c:modelModule.Control) {
|
||||
static compose(validators: List<Function>): Function {
|
||||
return function(c: modelModule.Control) {
|
||||
var res = ListWrapper.reduce(validators, (res, validator) => {
|
||||
var errors = validator(c);
|
||||
return isPresent(errors) ? StringMapWrapper.merge(res, errors) : res;
|
||||
|
@ -33,7 +31,7 @@ export class Validators {
|
|||
}
|
||||
}
|
||||
|
||||
static group(c:modelModule.ControlGroup) {
|
||||
static group(c: modelModule.ControlGroup): StringMap<string, boolean> {
|
||||
var res = {};
|
||||
StringMapWrapper.forEach(c.controls, (control, name) => {
|
||||
if (c.contains(name) && isPresent(control.errors)) {
|
||||
|
@ -43,7 +41,7 @@ export class Validators {
|
|||
return StringMapWrapper.isEmpty(res) ? null : res;
|
||||
}
|
||||
|
||||
static array(c:modelModule.ControlArray) {
|
||||
static array(c: modelModule.ControlArray): StringMap<string, boolean> {
|
||||
var res = {};
|
||||
ListWrapper.forEach(c.controls, (control) => {
|
||||
if (isPresent(control.errors)) {
|
||||
|
@ -53,7 +51,7 @@ export class Validators {
|
|||
return StringMapWrapper.isEmpty(res) ? null : res;
|
||||
}
|
||||
|
||||
static _mergeErrors(control, res) {
|
||||
static _mergeErrors(control: modelModule.AbstractControl, res: StringMap<string, any>): void {
|
||||
StringMapWrapper.forEach(control.errors, (value, error) => {
|
||||
if (!StringMapWrapper.contains(res, error)) {
|
||||
res[error] = [];
|
|
@ -1,8 +1,7 @@
|
|||
import {global} from 'angular2/src/facade/lang';
|
||||
|
||||
export function makeDecorator(annotationCls) {
|
||||
return function() {
|
||||
var args = arguments;
|
||||
return function(... args) {
|
||||
var Reflect = global.Reflect;
|
||||
if (!(Reflect && Reflect.getMetadata)) {
|
||||
throw 'reflect-metadata shim is required when using class decorators';
|
||||
|
@ -19,7 +18,7 @@ export function makeDecorator(annotationCls) {
|
|||
}
|
||||
}
|
||||
|
||||
export function makeParamDecorator(annotationCls) {
|
||||
export function makeParamDecorator(annotationCls): any {
|
||||
return function(... args) {
|
||||
var Reflect = global.Reflect;
|
||||
if (!(Reflect && Reflect.getMetadata)) {
|
||||
|
@ -28,14 +27,19 @@ export function makeParamDecorator(annotationCls) {
|
|||
var annotationInstance = Object.create(annotationCls.prototype);
|
||||
annotationCls.apply(annotationInstance, args);
|
||||
return function(cls, unusedKey, index) {
|
||||
var parameters = Reflect.getMetadata('parameters', cls);
|
||||
var parameters: Array<Array<any>> = Reflect.getMetadata('parameters', cls);
|
||||
parameters = parameters || [];
|
||||
|
||||
// there might be gaps if some in between parameters do not have annotations.
|
||||
// we pad with nulls.
|
||||
while (parameters.length <= index) {
|
||||
parameters.push(null);
|
||||
}
|
||||
parameters[index] = annotationInstance;
|
||||
|
||||
parameters[index] = parameters[index] || [];
|
||||
var annotationsForParam: Array<any> = parameters[index];
|
||||
annotationsForParam.push(annotationInstance);
|
||||
|
||||
Reflect.defineMetadata('parameters', parameters, cls);
|
||||
return cls;
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ export function main() {
|
|||
});
|
||||
}));
|
||||
|
||||
|
||||
it("should update the control group values on DOM change",
|
||||
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
var form = new ControlGroup({
|
||||
|
|
Loading…
Reference in New Issue