feat(forms): remove support for declaring forms in html
This commit is contained in:
parent
ded83e589b
commit
a73c643322
|
@ -1,4 +1,3 @@
|
||||||
export * from './src/forms/model';
|
export * from './src/forms/model';
|
||||||
export * from './src/forms/directives';
|
export * from './src/forms/directives';
|
||||||
export * from './src/forms/validators';
|
export * from './src/forms/validators';
|
||||||
export * from './src/forms/validator_directives';
|
|
||||||
|
|
|
@ -5,11 +5,6 @@ import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {ControlGroup, Control} from './model';
|
import {ControlGroup, Control} from './model';
|
||||||
import * as validators from './validators';
|
import * as validators from './validators';
|
||||||
|
|
||||||
class ControlGroupDirectiveBase {
|
|
||||||
addDirective(directive):void {}
|
|
||||||
findControl(name:string):Control { return null; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@CONST()
|
@CONST()
|
||||||
export class ControlValueAccessor {
|
export class ControlValueAccessor {
|
||||||
readValue(el){}
|
readValue(el){}
|
||||||
|
@ -60,9 +55,16 @@ function controlValueAccessorFor(controlType:string):ControlValueAccessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Decorator({
|
||||||
export class ControlDirectiveBase {
|
lifecycle: [onChange],
|
||||||
_groupDecorator:ControlGroupDirectiveBase;
|
selector: '[control]',
|
||||||
|
bind: {
|
||||||
|
'controlName' : 'control',
|
||||||
|
'type' : 'type'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
export class ControlDirective {
|
||||||
|
_groupDecorator:ControlGroupDirective;
|
||||||
_el:NgElement;
|
_el:NgElement;
|
||||||
|
|
||||||
controlName:string;
|
controlName:string;
|
||||||
|
@ -71,12 +73,18 @@ export class ControlDirectiveBase {
|
||||||
|
|
||||||
validator:Function;
|
validator:Function;
|
||||||
|
|
||||||
constructor(groupDecorator, el:NgElement) {
|
constructor(@Ancestor() groupDecorator:ControlGroupDirective, el:NgElement) {
|
||||||
this._groupDecorator = groupDecorator;
|
this._groupDecorator = groupDecorator;
|
||||||
this._el = el;
|
this._el = el;
|
||||||
this.validator = validators.nullValidator;
|
this.validator = validators.nullValidator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: vsavkin this should be moved into the constructor once static bindings
|
||||||
|
// are implemented
|
||||||
|
onChange(_) {
|
||||||
|
this._initialize();
|
||||||
|
}
|
||||||
|
|
||||||
_initialize() {
|
_initialize() {
|
||||||
this._groupDecorator.addDirective(this);
|
this._groupDecorator.addDirective(this);
|
||||||
|
|
||||||
|
@ -105,51 +113,15 @@ export class ControlDirectiveBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Decorator({
|
|
||||||
lifecycle: [onChange],
|
|
||||||
selector: '[control-name]',
|
|
||||||
bind: {
|
|
||||||
'controlName' : 'control-name',
|
|
||||||
'type' : 'type'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
export class ControlNameDirective extends ControlDirectiveBase {
|
|
||||||
constructor(@Ancestor() groupDecorator:ControlGroupDirective, el:NgElement) {
|
|
||||||
super(groupDecorator, el);
|
|
||||||
}
|
|
||||||
|
|
||||||
onChange(_) {
|
|
||||||
this._initialize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Decorator({
|
|
||||||
lifecycle: [onChange],
|
|
||||||
selector: '[control]',
|
|
||||||
bind: {
|
|
||||||
'controlName' : 'control',
|
|
||||||
'type' : 'type'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
export class ControlDirective extends ControlDirectiveBase {
|
|
||||||
constructor(@Ancestor() groupDecorator:NewControlGroupDirective, el:NgElement) {
|
|
||||||
super(groupDecorator, el);
|
|
||||||
}
|
|
||||||
|
|
||||||
onChange(_) {
|
|
||||||
this._initialize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Decorator({
|
@Decorator({
|
||||||
selector: '[control-group]',
|
selector: '[control-group]',
|
||||||
bind: {
|
bind: {
|
||||||
'controlGroup' : 'control-group'
|
'controlGroup' : 'control-group'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
export class ControlGroupDirective extends ControlGroupDirectiveBase {
|
export class ControlGroupDirective {
|
||||||
_controlGroup:ControlGroup;
|
_controlGroup:ControlGroup;
|
||||||
_directives:List<ControlNameDirective>;
|
_directives:List<ControlDirective>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
@ -161,71 +133,15 @@ export class ControlGroupDirective extends ControlGroupDirectiveBase {
|
||||||
ListWrapper.forEach(this._directives, (cd) => cd._updateDomValue());
|
ListWrapper.forEach(this._directives, (cd) => cd._updateDomValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
addDirective(c:ControlNameDirective) {
|
|
||||||
ListWrapper.push(this._directives, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
findControl(name:string):Control {
|
|
||||||
return this._controlGroup.controls[name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: '[new-control-group]',
|
|
||||||
bind: {
|
|
||||||
'initData' : 'new-control-group'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@Template({inline: '<content>'})
|
|
||||||
export class NewControlGroupDirective extends ControlGroupDirectiveBase {
|
|
||||||
_initData:any;
|
|
||||||
_controlGroup:ControlGroup;
|
|
||||||
_directives:List<ControlNameDirective>;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this._directives = ListWrapper.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
set initData(value) {
|
|
||||||
this._initData = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
addDirective(c:ControlDirective) {
|
addDirective(c:ControlDirective) {
|
||||||
ListWrapper.push(this._directives, c);
|
ListWrapper.push(this._directives, c);
|
||||||
this._controlGroup = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
findControl(name:string):Control {
|
findControl(name:string):Control {
|
||||||
if (isBlank(this._controlGroup)) {
|
|
||||||
this._controlGroup = this._createControlGroup();
|
|
||||||
}
|
|
||||||
return this._controlGroup.controls[name];
|
return this._controlGroup.controls[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
_createControlGroup():ControlGroup {
|
|
||||||
var controls = ListWrapper.reduce(this._directives, (memo, cd) => {
|
|
||||||
var initControlValue = this._initData[cd.controlName];
|
|
||||||
memo[cd.controlName] = new Control(initControlValue);
|
|
||||||
return memo;
|
|
||||||
}, {});
|
|
||||||
return new ControlGroup(controls);
|
|
||||||
}
|
|
||||||
|
|
||||||
get value() {
|
|
||||||
return this._controlGroup.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
get errors() {
|
|
||||||
return this._controlGroup.errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
get valid() {
|
|
||||||
return this._controlGroup.valid;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export var FormDirectives = [
|
export var FormDirectives = [
|
||||||
ControlGroupDirective, ControlNameDirective,
|
ControlGroupDirective, ControlDirective
|
||||||
ControlDirective, NewControlGroupDirective
|
|
||||||
];
|
];
|
||||||
|
|
|
@ -15,10 +15,10 @@ import {MockTemplateResolver} from 'angular2/src/mock/template_resolver_mock';
|
||||||
import {Injector} from 'angular2/di';
|
import {Injector} from 'angular2/di';
|
||||||
|
|
||||||
import {Component, Decorator, Template} from 'angular2/core';
|
import {Component, Decorator, Template} from 'angular2/core';
|
||||||
import {ControlGroupDirective, ControlNameDirective,
|
import {ControlGroupDirective, ControlDirective, Control, ControlGroup,
|
||||||
ControlDirective, NewControlGroupDirective,
|
ControlValueAccessor, RequiredValidatorDirective} from 'angular2/forms';
|
||||||
Control, ControlGroup, ControlValueAccessor,
|
|
||||||
RequiredValidatorDirective} from 'angular2/forms';
|
import * as validators from 'angular2/src/forms/validators';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
function detectChanges(view) {
|
function detectChanges(view) {
|
||||||
|
@ -42,8 +42,7 @@ export function main() {
|
||||||
|
|
||||||
tplResolver.setTemplate(componentType, new Template({
|
tplResolver.setTemplate(componentType, new Template({
|
||||||
inline: template,
|
inline: template,
|
||||||
directives: [ControlGroupDirective, ControlNameDirective, ControlDirective,
|
directives: [ControlGroupDirective, ControlDirective, WrappedValue]
|
||||||
NewControlGroupDirective, WrappedValue]
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
compiler.compile(componentType).then((pv) => {
|
compiler.compile(componentType).then((pv) => {
|
||||||
|
@ -61,7 +60,7 @@ export function main() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
var t = `<div [control-group]="form">
|
var t = `<div [control-group]="form">
|
||||||
<input type="text" control-name="login">
|
<input type="text" control="login">
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
compile(MyComp, t, ctx, (view) => {
|
compile(MyComp, t, ctx, (view) => {
|
||||||
|
@ -78,7 +77,7 @@ export function main() {
|
||||||
var ctx = new MyComp(form);
|
var ctx = new MyComp(form);
|
||||||
|
|
||||||
var t = `<div [control-group]="form">
|
var t = `<div [control-group]="form">
|
||||||
<input type="text" control-name="login">
|
<input type="text" control="login">
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
compile(MyComp, t, ctx, (view) => {
|
compile(MyComp, t, ctx, (view) => {
|
||||||
|
@ -99,7 +98,7 @@ export function main() {
|
||||||
var ctx = new MyComp(form);
|
var ctx = new MyComp(form);
|
||||||
|
|
||||||
var t = `<div [control-group]="form">
|
var t = `<div [control-group]="form">
|
||||||
<input type="text" control-name="login">
|
<input type="text" control="login">
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
compile(MyComp, t, ctx, (view) => {
|
compile(MyComp, t, ctx, (view) => {
|
||||||
|
@ -121,7 +120,7 @@ export function main() {
|
||||||
}), "one");
|
}), "one");
|
||||||
|
|
||||||
var t = `<div [control-group]="form">
|
var t = `<div [control-group]="form">
|
||||||
<input type="text" [control-name]="name">
|
<input type="text" [control]="name">
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
compile(MyComp, t, ctx, (view) => {
|
compile(MyComp, t, ctx, (view) => {
|
||||||
|
@ -141,7 +140,7 @@ export function main() {
|
||||||
var ctx = new MyComp(new ControlGroup({"checkbox": new Control(true)}));
|
var ctx = new MyComp(new ControlGroup({"checkbox": new Control(true)}));
|
||||||
|
|
||||||
var t = `<div [control-group]="form">
|
var t = `<div [control-group]="form">
|
||||||
<input type="checkbox" control-name="checkbox">
|
<input type="checkbox" control="checkbox">
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
compile(MyComp, t, ctx, (view) => {
|
compile(MyComp, t, ctx, (view) => {
|
||||||
|
@ -160,7 +159,7 @@ export function main() {
|
||||||
var ctx = new MyComp(new ControlGroup({"name": new Control("aa")}));
|
var ctx = new MyComp(new ControlGroup({"name": new Control("aa")}));
|
||||||
|
|
||||||
var t = `<div [control-group]="form">
|
var t = `<div [control-group]="form">
|
||||||
<input type="text" control-name="name" wrapped-value>
|
<input type="text" control="name" wrapped-value>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
compile(MyComp, t, ctx, (view) => {
|
compile(MyComp, t, ctx, (view) => {
|
||||||
|
@ -176,48 +175,37 @@ export function main() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("declarative forms", () => {
|
describe("validations", () => {
|
||||||
it("should initialize dom elements", (done) => {
|
it("should use validators defined in html",(done) => {
|
||||||
var t = `<div [new-control-group]="{'login': 'loginValue', 'password':'passValue'}">
|
var form = new ControlGroup({"login": new Control("aa")});
|
||||||
<input type="text" id="login" control="login">
|
var ctx = new MyComp(form);
|
||||||
<input type="password" id="password" control="password">
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
compile(MyComp, t, new MyComp(), (view) => {
|
var t = `<div [control-group]="form">
|
||||||
var loginInput = queryView(view, "#login")
|
|
||||||
expect(loginInput.value).toEqual("loginValue");
|
|
||||||
|
|
||||||
var passInput = queryView(view, "#password")
|
|
||||||
expect(passInput.value).toEqual("passValue");
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should update the control group values on DOM change", (done) => {
|
|
||||||
var t = `<div #form [new-control-group]="{'login': 'loginValue'}">
|
|
||||||
<input type="text" control="login">
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
compile(MyComp, t, new MyComp(), (view) => {
|
|
||||||
var input = queryView(view, "input")
|
|
||||||
|
|
||||||
input.value = "updatedValue";
|
|
||||||
dispatchEvent(input, "change");
|
|
||||||
|
|
||||||
var form = view.contextWithLocals.get("form");
|
|
||||||
expect(form.value).toEqual({'login': 'updatedValue'});
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should support validators",(done) => {
|
|
||||||
var t = `<div #form [new-control-group]="{'login': 'loginValue'}">
|
|
||||||
<input type="text" control="login" required>
|
<input type="text" control="login" required>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
compile(MyComp, t, new MyComp(), (view) => {
|
compile(MyComp, t, ctx, (view) => {
|
||||||
var form = view.contextWithLocals.get("form");
|
expect(form.valid).toEqual(true);
|
||||||
|
|
||||||
|
var input = queryView(view, "input");
|
||||||
|
|
||||||
|
input.value = "";
|
||||||
|
dispatchEvent(input, "change");
|
||||||
|
|
||||||
|
expect(form.valid).toEqual(false);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should use validators defined in the model",(done) => {
|
||||||
|
var form = new ControlGroup({"login": new Control("aa", validators.required)});
|
||||||
|
var ctx = new MyComp(form);
|
||||||
|
|
||||||
|
var t = `<div [control-group]="form">
|
||||||
|
<input type="text" control="login">
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
compile(MyComp, t, ctx, (view) => {
|
||||||
expect(form.valid).toEqual(true);
|
expect(form.valid).toEqual(true);
|
||||||
|
|
||||||
var input = queryView(view, "input");
|
var input = queryView(view, "input");
|
||||||
|
@ -236,12 +224,6 @@ export function main() {
|
||||||
@Component({
|
@Component({
|
||||||
selector: "my-comp"
|
selector: "my-comp"
|
||||||
})
|
})
|
||||||
@Template({
|
|
||||||
inline: "",
|
|
||||||
directives: [ControlGroupDirective, ControlNameDirective,
|
|
||||||
ControlDirective, NewControlGroupDirective, RequiredValidatorDirective,
|
|
||||||
WrappedValue]
|
|
||||||
})
|
|
||||||
class MyComp {
|
class MyComp {
|
||||||
form:ControlGroup;
|
form:ControlGroup;
|
||||||
name:string;
|
name:string;
|
||||||
|
@ -266,7 +248,7 @@ class WrappedValueAccessor extends ControlValueAccessor {
|
||||||
selector:'[wrapped-value]'
|
selector:'[wrapped-value]'
|
||||||
})
|
})
|
||||||
class WrappedValue {
|
class WrappedValue {
|
||||||
constructor(cd:ControlNameDirective) {
|
constructor(cd:ControlDirective) {
|
||||||
cd.valueAccessor = new WrappedValueAccessor();
|
cd.valueAccessor = new WrappedValueAccessor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue