refactor(formed): changed forms to use event and property setters instead of NgElement

This commit is contained in:
vsavkin 2015-03-19 13:12:16 -07:00
parent a12dc7d75a
commit 514529b5d9
3 changed files with 90 additions and 71 deletions

View File

@ -2,6 +2,7 @@ export * from './src/core/annotations/visibility';
export * from './src/core/compiler/interfaces'; export * from './src/core/compiler/interfaces';
export * from './src/core/annotations/template'; export * from './src/core/annotations/template';
export * from './src/core/application'; export * from './src/core/application';
export * from './src/core/annotations/di';
export * from './src/core/compiler/compiler'; export * from './src/core/compiler/compiler';

View File

@ -1,4 +1,4 @@
import {Template, Component, Decorator, NgElement, Ancestor, onChange} from 'angular2/angular2'; import {Template, Component, Decorator, Ancestor, onChange, PropertySetter} from 'angular2/angular2';
import {Optional} from 'angular2/di'; import {Optional} from 'angular2/di';
import {DOM} from 'angular2/src/dom/dom_adapter'; import {DOM} from 'angular2/src/dom/dom_adapter';
import {isBlank, isPresent, isString, CONST} from 'angular2/src/facade/lang'; import {isBlank, isPresent, isString, CONST} from 'angular2/src/facade/lang';
@ -6,53 +6,50 @@ import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {ControlGroup, Control} from './model'; import {ControlGroup, Control} from './model';
import {Validators} from './validators'; import {Validators} from './validators';
@CONST()
export class ControlValueAccessor { export class ControlValueAccessor {
readValue(el){} writeValue(value):void{}
writeValue(el, value):void {} set onChange(fn){}
} }
@CONST() @Decorator({
class DefaultControlValueAccessor extends ControlValueAccessor { selector: '[control]',
constructor() { events: {
super(); 'change' : 'onChange($event.target.value)'
}
})
export class DefaultControlDecorator extends ControlValueAccessor {
_setValueProperty:Function;
onChange:Function;
constructor(@PropertySetter('value') setValueProperty:Function) {
this._setValueProperty = setValueProperty;
this.onChange = (_) => {};
} }
readValue(el) { writeValue(value) {
return DOM.getValue(el); this._setValueProperty(value);
}
writeValue(el, value):void {
DOM.setValue(el,value);
} }
} }
@CONST() @Decorator({
class CheckboxControlValueAccessor extends ControlValueAccessor { selector: 'input[type=checkbox]', //should be input[type=checkbox][control]
constructor() { events: {
super(); 'change' : 'onChange($event.target.checked)'
}
})
export class CheckboxControlDecorator extends ControlValueAccessor {
_setCheckedProperty:Function;
onChange:Function;
constructor(cd:ControlDirective, @PropertySetter('checked') setCheckedProperty:Function) {
this._setCheckedProperty = setCheckedProperty;
this.onChange = (_) => {};
//TODO: vsavkin ControlDirective should inject CheckboxControlDirective
cd.valueAccessor = this;
} }
readValue(el):boolean { writeValue(value) {
return DOM.getChecked(el); this._setCheckedProperty(value);
}
writeValue(el, value:boolean):void {
DOM.setChecked(el, value);
}
}
var controlValueAccessors = {
"checkbox" : new CheckboxControlValueAccessor(),
"text" : new DefaultControlValueAccessor()
};
function controlValueAccessorFor(controlType:string):ControlValueAccessor {
var accessor = StringMapWrapper.get(controlValueAccessors, controlType);
if (isPresent(accessor)) {
return accessor;
} else {
return StringMapWrapper.get(controlValueAccessors, "text");
} }
} }
@ -60,25 +57,21 @@ function controlValueAccessorFor(controlType:string):ControlValueAccessor {
lifecycle: [onChange], lifecycle: [onChange],
selector: '[control]', selector: '[control]',
bind: { bind: {
'controlName' : 'control', 'controlName' : 'control'
'type' : 'type'
} }
}) })
export class ControlDirective { export class ControlDirective {
_groupDirective:ControlGroupDirective; _groupDirective:ControlGroupDirective;
_el:NgElement;
controlName:string; controlName:string;
type:string;
valueAccessor:ControlValueAccessor; valueAccessor:ControlValueAccessor;
validator:Function; validator:Function;
constructor(@Ancestor() groupDirective:ControlGroupDirective, el:NgElement) { constructor(@Ancestor() groupDirective:ControlGroupDirective, valueAccessor:DefaultControlDecorator) {
this._groupDirective = groupDirective; this._groupDirective = groupDirective;
this._el = el;
this.controlName = null; this.controlName = null;
this.type = null; this.valueAccessor = valueAccessor;
this.validator = Validators.nullValidator; this.validator = Validators.nullValidator;
} }
@ -94,20 +87,20 @@ export class ControlDirective {
var c = this._control(); var c = this._control();
c.validator = Validators.compose([c.validator, this.validator]); c.validator = Validators.compose([c.validator, this.validator]);
if (isBlank(this.valueAccessor)) {
this.valueAccessor = controlValueAccessorFor(this.type);
}
this._updateDomValue(); this._updateDomValue();
DOM.on(this._el.domElement, "change", (_) => this._updateControlValue()); this._setUpUpdateControlValue();
} }
_updateDomValue() { _updateDomValue() {
this.valueAccessor.writeValue(this._el.domElement, this._control().value); this.valueAccessor.writeValue(this._control().value);
} }
_updateControlValue() { _setUpUpdateControlValue() {
this._control().updateValue(this.valueAccessor.readValue(this._el.domElement)); this.valueAccessor.onChange = (newValue) => this._control().updateValue(newValue);
}
_updateControlValue(newValue) {
this._control().updateValue(newValue);
} }
_control() { _control() {
@ -165,5 +158,5 @@ export class ControlGroupDirective {
} }
export var FormDirectives = [ export var FormDirectives = [
ControlGroupDirective, ControlDirective ControlGroupDirective, ControlDirective, CheckboxControlDecorator, DefaultControlDecorator
]; ];

View File

@ -24,14 +24,17 @@ import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mappe
import {UrlResolver} from 'angular2/src/core/compiler/url_resolver'; import {UrlResolver} from 'angular2/src/core/compiler/url_resolver';
import {StyleUrlResolver} from 'angular2/src/core/compiler/style_url_resolver'; import {StyleUrlResolver} from 'angular2/src/core/compiler/style_url_resolver';
import {CssProcessor} from 'angular2/src/core/compiler/css_processor'; import {CssProcessor} from 'angular2/src/core/compiler/css_processor';
import {EventManager, DomEventsPlugin} from 'angular2/src/core/events/event_manager';
import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone';
import {MockTemplateResolver} from 'angular2/src/mock/template_resolver_mock'; 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/angular2'; import {Component, Decorator, Template, PropertySetter} from 'angular2/angular2';
import {ControlGroupDirective, ControlDirective, Control, ControlGroup, OptionalControl, import {ControlGroupDirective, ControlDirective, Control, ControlGroup, OptionalControl,
ControlValueAccessor, RequiredValidatorDirective, Validators} from 'angular2/forms'; ControlValueAccessor, RequiredValidatorDirective, CheckboxControlDecorator,
DefaultControlDecorator, Validators} from 'angular2/forms';
export function main() { export function main() {
function detectChanges(view) { function detectChanges(view) {
@ -56,11 +59,13 @@ export function main() {
tplResolver.setTemplate(componentType, new Template({ tplResolver.setTemplate(componentType, new Template({
inline: template, inline: template,
directives: [ControlGroupDirective, ControlDirective, WrappedValue, RequiredValidatorDirective] directives: [ControlGroupDirective, ControlDirective, WrappedValue, RequiredValidatorDirective,
CheckboxControlDecorator, DefaultControlDecorator]
})); }));
compiler.compile(componentType).then((pv) => { compiler.compile(componentType).then((pv) => {
var view = pv.instantiate(null, null); var eventManager = new EventManager([new DomEventsPlugin()], new FakeVmTurnZone());
var view = pv.instantiate(null, eventManager);
view.hydrate(new Injector([]), null, null, context, null); view.hydrate(new Injector([]), null, null, context, null);
detectChanges(view); detectChanges(view);
callback(view); callback(view);
@ -346,21 +351,41 @@ class MyComp {
} }
} }
class WrappedValueAccessor extends ControlValueAccessor {
readValue(el){
return el.value.substring(1, el.value.length - 1);
}
writeValue(el, value):void {
el.value = `!${value}!`;
}
}
@Decorator({ @Decorator({
selector:'[wrapped-value]' selector:'[wrapped-value]',
events: {
'change' : 'handleOnChange($event.target.value)'
}
}) })
class WrappedValue { class WrappedValue extends ControlValueAccessor {
constructor(cd:ControlDirective) { _setProperty:Function;
cd.valueAccessor = new WrappedValueAccessor(); onChange:Function;
constructor(cd:ControlDirective, @PropertySetter('value') setProperty:Function) {
this._setProperty = setProperty;
cd.valueAccessor = this;
}
writeValue(value) {
this._setProperty(`!${value}!`);
}
handleOnChange(value) {
this.onChange(value.substring(1, value.length - 1));
} }
} }
class FakeVmTurnZone extends VmTurnZone {
constructor() {
super({enableLongStackTrace: false});
}
run(fn) {
fn();
}
runOutsideAngular(fn) {
fn();
}
}