feat(forms): add support for validations
This commit is contained in:
@ -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;
|
||||
}
|
||||
|
||||
|
22
modules/angular2/src/forms/directives.js
vendored
22
modules/angular2/src/forms/directives.js
vendored
@ -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 = [
|
||||
|
65
modules/angular2/src/forms/model.js
vendored
65
modules/angular2/src/forms/model.js
vendored
@ -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();
|
||||
}
|
||||
}
|
||||
|
15
modules/angular2/src/forms/validator_directives.js
vendored
Normal file
15
modules/angular2/src/forms/validator_directives.js
vendored
Normal 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]);
|
||||
}
|
||||
}
|
31
modules/angular2/src/forms/validators.js
vendored
Normal file
31
modules/angular2/src/forms/validators.js
vendored
Normal 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;
|
||||
}
|
Reference in New Issue
Block a user