feat(forms): migrated forms to typescript

This commit is contained in:
vsavkin 2015-05-20 18:10:30 -07:00
parent fed86fc8ac
commit 00c3693daa
12 changed files with 254 additions and 287 deletions

View File

@ -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.
*
*/

View File

@ -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. */

View File

@ -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);

View File

@ -1,4 +1,6 @@
export {
Self as SelfAnnotation,
Ancestor as AncestorAnnotation,
Parent as ParentAnnotation,
Unbounded as UnboundedAnnotation
} from '../annotations_impl/visibility';

View File

@ -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;
}

View File

@ -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]);

View File

@ -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)) {

View File

@ -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;

View File

@ -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]);
}
}

View File

@ -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] = [];

View File

@ -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;
}

View File

@ -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({