feat(forms): add support for validations

This commit is contained in:
vsavkin
2015-02-11 11:10:31 -08:00
parent 65ebff056a
commit ded83e589b
10 changed files with 283 additions and 8 deletions

View File

@ -67,6 +67,14 @@ class StringMapWrapper {
static void forEach(Map m, fn(v, k)) {
m.forEach((k, v) => fn(v, k));
}
static HashMap merge(HashMap a, HashMap b) {
var m = {};
a.forEach((k, v) => m[k] = v);
b.forEach((k, v) => m[k] = v);
return m;
}
static bool isEmpty(Map m) => m.isEmpty;
}

View File

@ -3,6 +3,7 @@ import {DOM} from 'angular2/src/facade/dom';
import {isBlank, isPresent, CONST} from 'angular2/src/facade/lang';
import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {ControlGroup, Control} from './model';
import * as validators from './validators';
class ControlGroupDirectiveBase {
addDirective(directive):void {}
@ -68,16 +69,25 @@ export class ControlDirectiveBase {
type:string;
valueAccessor:ControlValueAccessor;
validator:Function;
constructor(groupDecorator, el:NgElement) {
this._groupDecorator = groupDecorator;
this._el = el;
this.validator = validators.nullValidator;
}
_initialize() {
this._groupDecorator.addDirective(this);
if (isPresent(this.validator)) {
var c = this._control();
c.validator = validators.compose([c.validator, this.validator]);
}
if (isBlank(this.valueAccessor)) {
this.valueAccessor = controlValueAccessorFor(this.type);
}
this._groupDecorator.addDirective(this);
this._updateDomValue();
DOM.on(this._el.domElement, "change", (_) => this._updateControlValue());
}
@ -87,7 +97,7 @@ export class ControlDirectiveBase {
}
_updateControlValue() {
this._control().value = this.valueAccessor.readValue(this._el.domElement);
this._control().updateValue(this.valueAccessor.readValue(this._el.domElement));
}
_control() {
@ -205,6 +215,14 @@ export class NewControlGroupDirective extends ControlGroupDirectiveBase {
get value() {
return this._controlGroup.value;
}
get errors() {
return this._controlGroup.errors;
}
get valid() {
return this._controlGroup.valid;
}
}
export var FormDirectives = [

View File

@ -1,18 +1,56 @@
import {StringMapWrapper, StringMap} from 'angular2/src/facade/collection';
import {isPresent} from 'angular2/src/facade/lang';
import {StringMap, StringMapWrapper} from 'angular2/src/facade/collection';
import {nullValidator, controlGroupValidator} from './validators';
export const VALID = "VALID";
export const INVALID = "INVALID";
export class Control {
value:any;
validator:Function;
status:string;
errors;
_parent:ControlGroup;
constructor(value:any) {
constructor(value:any, validator:Function = nullValidator) {
this.value = value;
this.validator = validator;
this._updateStatus();
}
updateValue(value:any) {
this.value = value;
this._updateStatus();
this._updateParent();
}
get valid() {
return this.status === VALID;
}
_updateStatus() {
this.errors = this.validator(this);
this.status = isPresent(this.errors) ? INVALID : VALID;
}
_updateParent() {
if (isPresent(this._parent)){
this._parent._controlChanged();
}
}
}
export class ControlGroup {
controls: StringMap;
controls;
validator:Function;
status:string;
errors;
constructor(controls:StringMap) {
constructor(controls, validator:Function = controlGroupValidator) {
this.controls = controls;
this.validator = validator;
this._setParentForControls();
this._updateStatus();
}
get value() {
@ -22,4 +60,23 @@ export class ControlGroup {
});
return res;
}
get valid() {
return this.status === VALID;
}
_setParentForControls() {
StringMapWrapper.forEach(this.controls, (control, name) => {
control._parent = this;
});
}
_updateStatus() {
this.errors = this.validator(this);
this.status = isPresent(this.errors) ? INVALID : VALID;
}
_controlChanged() {
this._updateStatus();
}
}

View File

@ -0,0 +1,15 @@
import {isBlank, isPresent} from 'angular2/src/facade/lang';
import {List, ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {Decorator} from 'angular2/core';
import {ControlGroup, Control, ControlDirective} from 'angular2/forms';
import * as validators from 'angular2/forms';
@Decorator({
selector: '[required]'
})
export class RequiredValidatorDirective {
constructor(c:ControlDirective) {
c.validator = validators.compose([c.validator, validators.required]);
}
}

View File

@ -0,0 +1,31 @@
import {isBlank, isPresent} from 'angular2/src/facade/lang';
import {List, ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {ControlGroup, Control} from 'angular2/forms';
export function required(c:Control) {
return isBlank(c.value) || c.value === "" ? {"required" : true} : null;
}
export function nullValidator(c:Control) {
return null;
}
export function compose(validators:List<Function>):Function {
return function(c:Control) {
return ListWrapper.reduce(validators, (res, validator) => {
var errors = validator(c);
return isPresent(errors) ? StringMapWrapper.merge(res, errors) : res;
}, {});
}
}
export function controlGroupValidator(c:ControlGroup) {
var res = {};
StringMapWrapper.forEach(c.controls, (control, name) => {
if (isPresent(control.errors)) {
res[name] = control.errors;
}
});
return StringMapWrapper.isEmpty(res) ? null : res;
}