Skip to content

Commit b349c35

Browse files
committed
feat(ElementInjector): implement @PropertySetter
relates to angular#621
1 parent c3873be commit b349c35

21 files changed

Lines changed: 192 additions & 82 deletions

modules/angular2/src/core/annotations/events.js renamed to modules/angular2/src/core/annotations/di.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,16 @@ export class EventEmitter extends DependencyAnnotation {
1313
this.eventName = eventName;
1414
}
1515
}
16+
17+
/**
18+
* The directive can inject a property setter that would allow setting this property on the
19+
* host element
20+
*/
21+
export class PropertySetter extends DependencyAnnotation {
22+
propName: string;
23+
@CONST()
24+
constructor(propName) {
25+
super();
26+
this.propName = propName;
27+
}
28+
}

modules/angular2/src/core/application.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ function _injectorBindings(appComponentType): List<Binding> {
5959
}, [appComponentAnnotatedTypeToken, appDocumentToken]),
6060

6161
bind(appViewToken).toAsyncFactory((changeDetection, compiler, injector, appElement,
62-
appComponentAnnotatedType, strategy, eventManager) => {
62+
appComponentAnnotatedType, strategy, eventManager, reflector) => {
6363
return compiler.compile(appComponentAnnotatedType.type).then(
6464
(protoView) => {
6565
var appProtoView = ProtoView.createRootProtoView(protoView, appElement,
@@ -68,12 +68,12 @@ function _injectorBindings(appComponentType): List<Binding> {
6868
// The light Dom of the app element is not considered part of
6969
// the angular application. Thus the context and lightDomInjector are
7070
// empty.
71-
var view = appProtoView.instantiate(null, eventManager);
71+
var view = appProtoView.instantiate(null, eventManager, reflector);
7272
view.hydrate(injector, null, new Object());
7373
return view;
7474
});
7575
}, [ChangeDetection, Compiler, Injector, appElementToken, appComponentAnnotatedTypeToken,
76-
ShadowDomStrategy, EventManager]),
76+
ShadowDomStrategy, EventManager, Reflector]),
7777

7878
bind(appChangeDetectorToken).toFactory((rootView) => rootView.changeDetector,
7979
[appViewToken]),

modules/angular2/src/core/compiler/element_injector.js

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ import {Math} from 'angular2/src/facade/math';
33
import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
44
import {Injector, Key, Dependency, bind, Binding, NoProviderError, ProviderError, CyclicDependencyError} from 'angular2/di';
55
import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility';
6-
import {EventEmitter} from 'angular2/src/core/annotations/events';
6+
import {EventEmitter, PropertySetter} from 'angular2/src/core/annotations/di';
77
import {View, ProtoView} from 'angular2/src/core/compiler/view';
88
import {LightDom, SourceLightDom, DestinationLightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom';
99
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
1010
import {NgElement} from 'angular2/src/core/dom/element';
1111
import {Directive, onChange, onDestroy} from 'angular2/src/core/annotations/annotations'
1212
import {BindingPropagationConfig} from 'angular2/src/core/compiler/binding_propagation_config'
13+
import {Reflector} from 'angular2/src/reflection/reflection';
1314

1415
var _MAX_DIRECTIVE_CONSTRUCTION_COUNTER = 10;
1516

@@ -90,18 +91,22 @@ class TreeNode {
9091
export class DirectiveDependency extends Dependency {
9192
depth:int;
9293
eventEmitterName:string;
94+
propSetterName:string;
9395

9496
constructor(key:Key, asPromise:boolean, lazy:boolean, optional:boolean,
95-
properties:List, depth:int, eventEmitterName: string) {
97+
properties:List, depth:int, eventEmitterName: string, propSetterName: string) {
9698
super(key, asPromise, lazy, optional, properties);
9799
this.depth = depth;
98100
this.eventEmitterName = eventEmitterName;
101+
this.propSetterName = propSetterName;
99102
}
100103

101104
static createFrom(d:Dependency):Dependency {
102105
return new DirectiveDependency(d.key, d.asPromise, d.lazy, d.optional,
103106
d.properties, DirectiveDependency._depth(d.properties),
104-
DirectiveDependency._eventEmitterName(d.properties));
107+
DirectiveDependency._eventEmitterName(d.properties),
108+
DirectiveDependency._propSetterName(d.properties)
109+
);
105110
}
106111

107112
static _depth(properties):int {
@@ -119,6 +124,15 @@ export class DirectiveDependency extends Dependency {
119124
}
120125
return null;
121126
}
127+
128+
static _propSetterName(properties):string {
129+
for (var i = 0; i < properties.length; i++) {
130+
if (properties[i] instanceof PropertySetter) {
131+
return properties[i].propName;
132+
}
133+
}
134+
return null;
135+
}
122136
}
123137

124138
export class DirectiveBinding extends Binding {
@@ -256,8 +270,9 @@ export class ProtoElementInjector {
256270
}
257271
}
258272

259-
instantiate(parent:ElementInjector, host:ElementInjector, eventCallbacks):ElementInjector {
260-
return new ElementInjector(this, parent, host, eventCallbacks);
273+
instantiate(parent:ElementInjector, host:ElementInjector, eventCallbacks,
274+
reflector: Reflector):ElementInjector {
275+
return new ElementInjector(this, parent, host, eventCallbacks, reflector);
261276
}
262277

263278
directParent(): ProtoElementInjector {
@@ -311,7 +326,10 @@ export class ElementInjector extends TreeNode {
311326
_preBuiltObjects;
312327
_constructionCounter;
313328
_eventCallbacks;
314-
constructor(proto:ProtoElementInjector, parent:ElementInjector, host:ElementInjector, eventCallbacks: Map) {
329+
_refelector: Reflector;
330+
331+
constructor(proto:ProtoElementInjector, parent:ElementInjector, host:ElementInjector,
332+
eventCallbacks: Map, reflector: Reflector) {
315333
super(parent);
316334
if (isPresent(parent) && isPresent(host)) {
317335
throw new BaseException('Only either parent or host is allowed');
@@ -324,6 +342,7 @@ export class ElementInjector extends TreeNode {
324342
}
325343

326344
this._proto = proto;
345+
this._refelector = reflector;
327346

328347
//we cannot call clearDirectives because fields won't be detected
329348
this._preBuiltObjects = null;
@@ -488,6 +507,7 @@ export class ElementInjector extends TreeNode {
488507

489508
_getByDependency(dep:DirectiveDependency, requestor:Key) {
490509
if (isPresent(dep.eventEmitterName)) return this._buildEventEmitter(dep);
510+
if (isPresent(dep.propSetterName)) return this._buildPropSetter(dep);
491511
return this._getByKey(dep.key, dep.depth, dep.optional, requestor);
492512
}
493513

@@ -502,6 +522,13 @@ export class ElementInjector extends TreeNode {
502522
return (_) => {};
503523
}
504524

525+
_buildPropSetter(dep) {
526+
var ngElement = this._getPreBuiltObjectByKeyId(StaticKeys.instance().ngElementId);
527+
var domElement = ngElement.domElement;
528+
var setter = this._refelector.setter(dep.propSetterName);
529+
return function(v) { setter(domElement, v) };
530+
}
531+
505532
/*
506533
* It is fairly easy to annotate keys with metadata.
507534
* For example, key.metadata = 'directive'.

modules/angular2/src/core/compiler/view.js

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {ShadowDomStrategy} from './shadow_dom_strategy';
1818
import {ViewPool} from './view_pool';
1919
import {EventManager} from 'angular2/src/core/events/event_manager';
2020

21+
import {Reflector} from 'angular2/src/reflection/reflection';
22+
2123
const NG_BINDING_CLASS = 'ng-binding';
2224
const NG_BINDING_CLASS_SELECTOR = '.ng-binding';
2325

@@ -298,19 +300,23 @@ export class ProtoView {
298300
}
299301

300302
// TODO(rado): hostElementInjector should be moved to hydrate phase.
301-
instantiate(hostElementInjector: ElementInjector, eventManager: EventManager):View {
302-
if (this._viewPool.length() == 0) this._preFillPool(hostElementInjector, eventManager);
303+
instantiate(hostElementInjector: ElementInjector, eventManager: EventManager,
304+
reflector: Reflector):View {
305+
if (this._viewPool.length() == 0) this._preFillPool(hostElementInjector, eventManager,
306+
reflector);
303307
var view = this._viewPool.pop();
304-
return isPresent(view) ? view : this._instantiate(hostElementInjector, eventManager);
308+
return isPresent(view) ? view : this._instantiate(hostElementInjector, eventManager, reflector);
305309
}
306310

307-
_preFillPool(hostElementInjector: ElementInjector, eventManager: EventManager) {
311+
_preFillPool(hostElementInjector: ElementInjector, eventManager: EventManager,
312+
reflector: Reflector) {
308313
for (var i = 0; i < VIEW_POOL_PREFILL; i++) {
309-
this._viewPool.push(this._instantiate(hostElementInjector, eventManager));
314+
this._viewPool.push(this._instantiate(hostElementInjector, eventManager, reflector));
310315
}
311316
}
312317

313-
_instantiate(hostElementInjector: ElementInjector, eventManager: EventManager): View {
318+
_instantiate(hostElementInjector: ElementInjector, eventManager: EventManager,
319+
reflector: Reflector): View {
314320
var rootElementClone = this.instantiateInPlace ? this.element : DOM.importIntoDoc(this.element);
315321
var elementsWithBindingsDynamic;
316322
if (this.isTemplateElement) {
@@ -362,9 +368,11 @@ export class ProtoView {
362368
if (isPresent(protoElementInjector)) {
363369
if (isPresent(protoElementInjector.parent)) {
364370
var parentElementInjector = elementInjectors[protoElementInjector.parent.index];
365-
elementInjector = protoElementInjector.instantiate(parentElementInjector, null, binder.events);
371+
elementInjector = protoElementInjector.instantiate(parentElementInjector, null,
372+
binder.events, reflector);
366373
} else {
367-
elementInjector = protoElementInjector.instantiate(null, hostElementInjector, binder.events);
374+
elementInjector = protoElementInjector.instantiate(null, hostElementInjector,
375+
binder.events, reflector);
368376
ListWrapper.push(rootElementInjectors, elementInjector);
369377
}
370378
}
@@ -391,7 +399,7 @@ export class ProtoView {
391399
var bindingPropagationConfig = null;
392400
if (isPresent(binder.componentDirective)) {
393401
var strategy = this.shadowDomStrategy;
394-
var childView = binder.nestedProtoView.instantiate(elementInjector, eventManager);
402+
var childView = binder.nestedProtoView.instantiate(elementInjector, eventManager, reflector);
395403
view.changeDetector.addChild(childView.changeDetector);
396404

397405
lightDom = strategy.constructLightDom(view, childView, element);
@@ -407,7 +415,7 @@ export class ProtoView {
407415
if (isPresent(binder.viewportDirective)) {
408416
var destLightDom = this._directParentElementLightDom(protoElementInjector, preBuiltObjects);
409417
viewContainer = new ViewContainer(view, element, binder.nestedProtoView, elementInjector,
410-
eventManager, destLightDom);
418+
eventManager, reflector, destLightDom);
411419
ListWrapper.push(viewContainers, viewContainer);
412420
}
413421

modules/angular2/src/core/compiler/view_container.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {Injector} from 'angular2/di';
66
import * as eiModule from 'angular2/src/core/compiler/element_injector';
77
import {isPresent, isBlank} from 'angular2/src/facade/lang';
88
import {EventManager} from 'angular2/src/core/events/event_manager';
9+
import {Reflector} from 'angular2/src/reflection/reflection';
910

1011
export class ViewContainer {
1112
parentView: viewModule.View;
@@ -14,17 +15,24 @@ export class ViewContainer {
1415
_views: List<viewModule.View>;
1516
_lightDom: any;
1617
_eventManager: EventManager;
18+
_reflector: Reflector;
1719
elementInjector: eiModule.ElementInjector;
1820
appInjector: Injector;
1921
hostElementInjector: eiModule.ElementInjector;
2022

21-
constructor(parentView: viewModule.View, templateElement, defaultProtoView: viewModule.ProtoView,
22-
elementInjector: eiModule.ElementInjector, eventManager: EventManager, lightDom = null) {
23+
constructor(parentView: viewModule.View,
24+
templateElement,
25+
defaultProtoView: viewModule.ProtoView,
26+
elementInjector: eiModule.ElementInjector,
27+
eventManager: EventManager,
28+
reflector: Reflector,
29+
lightDom = null) {
2330
this.parentView = parentView;
2431
this.templateElement = templateElement;
2532
this.defaultProtoView = defaultProtoView;
2633
this.elementInjector = elementInjector;
2734
this._lightDom = lightDom;
35+
this._reflector = reflector;
2836

2937
// The order in this list matches the DOM order.
3038
this._views = [];
@@ -73,7 +81,8 @@ export class ViewContainer {
7381
if (!this.hydrated()) throw new BaseException(
7482
'Cannot create views on a dehydrated ViewContainer');
7583
// TODO(rado): replace with viewFactory.
76-
var newView = this.defaultProtoView.instantiate(this.hostElementInjector, this._eventManager);
84+
var newView = this.defaultProtoView.instantiate(this.hostElementInjector, this._eventManager,
85+
this._reflector);
7786
// insertion must come before hydration so that element injector trees are attached.
7887
this.insert(newView, atIndex);
7988
newView.hydrate(this.appInjector, this.hostElementInjector, this.parentView.context);

0 commit comments

Comments
 (0)