feat(view): reimplemented property setters using change detection

This commit is contained in:
vsavkin
2015-04-21 11:47:53 -07:00
parent 8a92a1f13e
commit 8ccafb0524
36 changed files with 510 additions and 469 deletions

View File

@ -22,7 +22,7 @@ import {AppProtoView} from 'angular2/src/core/compiler/view';
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
import {Component, DynamicComponent, Viewport, Decorator} from 'angular2/src/core/annotations/annotations';
import {PropertySetter, Attribute} from 'angular2/src/core/annotations/di';
import {Attribute} from 'angular2/src/core/annotations/di';
import {View} from 'angular2/src/core/annotations/view';
import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver';
@ -185,18 +185,20 @@ export function main() {
});
}));
it('should set directive.bind', inject([AsyncTestCompleter], (async) => {
captureDirective(DirectiveWithBind).then( (renderDir) => {
expect(renderDir.properties).toEqual(MapWrapper.createFromStringMap({
'a': 'b'
it('should set directive.hostProperties', inject([AsyncTestCompleter], (async) => {
captureDirective(DirectiveWithProperties).then( (renderDir) => {
expect(renderDir.hostProperties).toEqual(MapWrapper.createFromStringMap({
'someField': 'someProp'
}));
async.done();
});
}));
it('should read @PropertySetter', inject([AsyncTestCompleter], (async) => {
captureDirective(DirectiveWithPropertySetters).then( (renderDir) => {
expect(renderDir.setters).toEqual(['someProp']);
it('should set directive.bind', inject([AsyncTestCompleter], (async) => {
captureDirective(DirectiveWithBind).then( (renderDir) => {
expect(renderDir.properties).toEqual(MapWrapper.createFromStringMap({
'a': 'b'
}));
async.done();
});
}));
@ -500,16 +502,16 @@ class IgnoreChildrenDecoratorDirective {}
})
class DirectiveWithEvents {}
@Decorator({
hostProperties: {'someField': 'someProp'}
})
class DirectiveWithProperties {}
@Decorator({
properties: {'a': 'b'}
})
class DirectiveWithBind {}
@Decorator()
class DirectiveWithPropertySetters {
constructor(@PropertySetter('someProp') someProp) {}
}
@Decorator()
class DirectiveWithAttributes {
constructor(@Attribute('someAttr') someAttr:string) {}
@ -568,7 +570,8 @@ class FakeProtoViewFactory extends ProtoViewFactory {
this._results = results;
}
createProtoView(componentBinding:DirectiveBinding, renderProtoView: renderApi.ProtoViewDto, directives:List<DirectiveBinding>):AppProtoView {
createProtoView(componentBinding:DirectiveBinding, renderProtoView: renderApi.ProtoViewDto,
directives:List<DirectiveBinding>):AppProtoView {
ListWrapper.push(this.requests, [componentBinding, renderProtoView, directives]);
return ListWrapper.removeAt(this._results, 0);
}

View File

@ -4,7 +4,7 @@ import {ListWrapper, MapWrapper, List, StringMapWrapper, iterateListLike} from '
import {ProtoElementInjector, PreBuiltObjects, DirectiveBinding, TreeNode, ElementRef}
from 'angular2/src/core/compiler/element_injector';
import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility';
import {PropertySetter, Attribute, Query} from 'angular2/src/core/annotations/di';
import {Attribute, Query} from 'angular2/src/core/annotations/di';
import {onDestroy} from 'angular2/src/core/annotations/annotations';
import {Optional, Injector, Inject, bind} from 'angular2/di';
import {AppProtoView, AppView} from 'angular2/src/core/compiler/view';
@ -87,51 +87,6 @@ class HasEventEmitter {
}
}
class NeedsPropertySetter {
propSetter;
roleSetter;
classSetter;
classWithDashSetter;
styleSetter;
unitSetter;
constructor(@PropertySetter('title') propSetter: Function, @PropertySetter('attr.role') roleSetter: Function,
@PropertySetter('class.active') classSetter: Function, @PropertySetter('class.foo-bar') classWithDashSetter: Function,
@PropertySetter('style.width') styleSetter: Function, @PropertySetter('style.height.px') unitSetter: Function) {
this.propSetter = propSetter;
this.roleSetter = roleSetter;
this.classSetter = classSetter;
this.classWithDashSetter = classWithDashSetter;
this.styleSetter = styleSetter;
this.unitSetter = unitSetter;
}
setProp(value) {
this.propSetter(value);
}
setRole(value) {
this.roleSetter(value);
}
setClass(value) {
this.classSetter(value);
}
setStyle(value) {
this.styleSetter(value);
}
setStyleWithUnit(value) {
this.unitSetter(value);
}
}
class NeedsPropertySetterNoType {
propSetter;
constructor(@PropertySetter('title') propSetter) {
this.propSetter = propSetter;
}
setProp(value) {
this.propSetter(value);
}
}
class NeedsAttribute {
typeAttribute;
titleAttribute;
@ -708,45 +663,6 @@ export function main() {
});
});
describe('property setter', () => {
var renderer, view;
beforeEach( () => {
renderer = new FakeRenderer();
var protoView = new AppProtoView(null, null);
view = new AppView(renderer, null, protoView, MapWrapper.create());
view.render = new ViewRef();
});
it('should be injectable and callable', () => {
var preBuildObject = new PreBuiltObjects(view, null, null);
var inj = injector([NeedsPropertySetter], null, null, preBuildObject);
var component = inj.get(NeedsPropertySetter);
component.setProp('foobar');
component.setRole('button');
component.setClass(true);
component.classWithDashSetter(true);
component.setStyle('40px');
component.setStyleWithUnit(50);
expect(renderer.log[0]).toEqual([view.render, 0, 'title', 'foobar']);
expect(renderer.log[1]).toEqual([view.render, 0, 'attr.role', 'button']);
expect(renderer.log[2]).toEqual([view.render, 0, 'class.active', true]);
expect(renderer.log[3]).toEqual([view.render, 0, 'class.foo-bar', true]);
expect(renderer.log[4]).toEqual([view.render, 0, 'style.width', '40px']);
expect(renderer.log[5]).toEqual([view.render, 0, 'style.height.px', 50]);
});
it('should be injectable and callable without specifying param type annotation', () => {
var preBuildObject = new PreBuiltObjects(view, null, null);
var inj = injector([NeedsPropertySetterNoType], null, null, preBuildObject);
var component = inj.get(NeedsPropertySetterNoType);
component.setProp('foobar');
expect(renderer.log[0]).toEqual([view.render, 0, 'title', 'foobar']);
});
});
describe('static attributes', () => {
it('should be injectable', () => {
var attributes = MapWrapper.create();

View File

@ -190,7 +190,6 @@ export function main() {
}));
tb.createView(MyComp, {context: ctx}).then((view) => {
ctx.ctxProp = 'a';
view.detectChanges();
@ -591,6 +590,26 @@ export function main() {
});
}));
it('should support updating host element via hostProperties', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideView(MyComp, new View({
template: '<div update-host-properties></div>',
directives: [DecoratorUpdatingHostProperties]
}));
tb.createView(MyComp, {context: ctx}).then((view) => {
var injector = view.rawView.elementInjectors[0];
var updateHost = injector.get(DecoratorUpdatingHostProperties);
updateHost.id = "newId";
view.detectChanges();
expect(view.rootNodes[0].id).toEqual("newId");
async.done();
});
}));
if (DOM.supportsDOMEvents()) {
it('should support preventing default on render events', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideView(MyComp, new View({
@ -1047,6 +1066,20 @@ class DecoratorEmitingEvent {
}
}
@Decorator({
selector: '[update-host-properties]',
hostProperties: {
'id' : 'id'
}
})
class DecoratorUpdatingHostProperties {
id:string;
constructor() {
this.id = "one";
}
}
@Decorator({
selector: '[listener]',
hostListeners: {'event': 'onEvent($event)'}