Skip to content

Commit b4338b6

Browse files
committed
fix(compiler): fix directive registration order
fix angular#328
1 parent fd34a56 commit b4338b6

File tree

8 files changed

+71
-52
lines changed

8 files changed

+71
-52
lines changed

modules/core/src/compiler/directive_metadata.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Type, FIELD} from 'facade/lang';
1+
import {Type} from 'facade/lang';
22
import {Directive} from '../annotations/annotations'
33
import {List} from 'facade/collection'
44
import {ShadowDomStrategy} from './shadow_dom';

modules/core/src/compiler/pipeline/compile_element.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {List, Map, ListWrapper, MapWrapper} from 'facade/collection';
22
import {Element, DOM} from 'facade/dom';
3-
import {int, isBlank, isPresent} from 'facade/lang';
3+
import {int, isBlank, isPresent, Type} from 'facade/lang';
44
import {DirectiveMetadata} from '../directive_metadata';
55
import {Decorator} from '../../annotations/annotations';
66
import {Component} from '../../annotations/annotations';
@@ -27,6 +27,7 @@ export class CompileElement {
2727
decoratorDirectives:List<DirectiveMetadata>;
2828
templateDirective:DirectiveMetadata;
2929
componentDirective:DirectiveMetadata;
30+
_allDirectives:List<DirectiveMetadata>;
3031
isViewRoot:boolean;
3132
hasBindings:boolean;
3233
inheritedProtoView:ProtoView;
@@ -45,6 +46,7 @@ export class CompileElement {
4546
this.decoratorDirectives = null;
4647
this.templateDirective = null;
4748
this.componentDirective = null;
49+
this._allDirectives = null;
4850
this.isViewRoot = false;
4951
this.hasBindings = false;
5052
// inherited down to children if they don't have
@@ -116,6 +118,7 @@ export class CompileElement {
116118

117119
addDirective(directive:DirectiveMetadata) {
118120
var annotation = directive.annotation;
121+
this._allDirectives = null;
119122
if (annotation instanceof Decorator) {
120123
if (isBlank(this.decoratorDirectives)) {
121124
this.decoratorDirectives = ListWrapper.create();
@@ -130,4 +133,23 @@ export class CompileElement {
130133
this.componentDirective = directive;
131134
}
132135
}
136+
137+
getAllDirectives(): List<DirectiveMetadata> {
138+
if (this._allDirectives === null) {
139+
// Collect all the directives
140+
// When present the component directive must be first
141+
var directives = ListWrapper.create();
142+
if (isPresent(this.componentDirective)) {
143+
ListWrapper.push(directives, this.componentDirective);
144+
}
145+
if (isPresent(this.templateDirective)) {
146+
ListWrapper.push(directives, this.templateDirective);
147+
}
148+
if (isPresent(this.decoratorDirectives)) {
149+
directives = ListWrapper.concat(directives, this.decoratorDirectives);
150+
}
151+
this._allDirectives = directives;
152+
}
153+
return this._allDirectives;
154+
}
133155
}

modules/core/src/compiler/pipeline/element_binder_builder.js

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export class ElementBinderBuilder extends CompileStep {
5858
if (isPresent(current.eventBindings)) {
5959
this._bindEvents(protoView, current);
6060
}
61-
this._bindDirectiveProperties(this._collectDirectives(current), current);
61+
this._bindDirectiveProperties(current.getAllDirectives(), current);
6262
} else if (isPresent(parent)) {
6363
elementBinder = parent.inheritedElementBinder;
6464
}
@@ -85,37 +85,21 @@ export class ElementBinderBuilder extends CompileStep {
8585
});
8686
}
8787

88-
_collectDirectives(compileElement) {
89-
var directives;
90-
if (isPresent(compileElement.decoratorDirectives)) {
91-
directives = ListWrapper.clone(compileElement.decoratorDirectives);
92-
} else {
93-
directives = [];
94-
}
95-
if (isPresent(compileElement.templateDirective)) {
96-
ListWrapper.push(directives, compileElement.templateDirective);
97-
}
98-
if (isPresent(compileElement.componentDirective)) {
99-
ListWrapper.push(directives, compileElement.componentDirective);
100-
}
101-
return directives;
102-
}
103-
104-
_bindDirectiveProperties(typesWithAnnotations, compileElement) {
88+
_bindDirectiveProperties(directives: List<DirectiveMetadata>,
89+
compileElement: CompileElement) {
10590
var protoView = compileElement.inheritedProtoView;
106-
var directiveIndex = 0;
107-
ListWrapper.forEach(typesWithAnnotations, (typeWithAnnotation) => {
108-
var annotation = typeWithAnnotation.annotation;
109-
if (isBlank(annotation.bind)) {
110-
return;
111-
}
112-
StringMapWrapper.forEach(annotation.bind, (dirProp, elProp) => {
91+
92+
for (var directiveIndex = 0; directiveIndex < directives.length; directiveIndex++) {
93+
var directive = ListWrapper.get(directives, directiveIndex);
94+
var annotation = directive.annotation;
95+
if (isBlank(annotation.bind)) continue;
96+
StringMapWrapper.forEach(annotation.bind, function (dirProp, elProp) {
11397
var expression = isPresent(compileElement.propertyBindings) ?
11498
MapWrapper.get(compileElement.propertyBindings, elProp) :
11599
null;
116100
if (isBlank(expression)) {
117101
throw new BaseException('No element binding found for property '+elProp
118-
+' which is required by directive '+stringify(typeWithAnnotation.type));
102+
+' which is required by directive '+stringify(directive.type));
119103
}
120104
var len = dirProp.length;
121105
var dirBindingName = dirProp;
@@ -129,7 +113,6 @@ export class ElementBinderBuilder extends CompileStep {
129113
isContentWatch
130114
);
131115
});
132-
directiveIndex++;
133-
});
116+
}
134117
}
135118
}

modules/core/src/compiler/pipeline/proto_element_injector_builder.js

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ export class ProtoElementInjectorBuilder extends CompileStep {
3232
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
3333
var distanceToParentInjector = this._getDistanceToParentInjector(parent, current);
3434
var parentProtoElementInjector = this._getParentProtoElementInjector(parent, current);
35-
var injectorBindings = this._collectDirectiveBindings(current);
36-
35+
var injectorBindings = ListWrapper.map(current.getAllDirectives(), this._createBinding);
3736
// TODO: add lightDomServices as well,
3837
// but after the directives as we rely on that order
3938
// in the element_binder_builder.
@@ -65,22 +64,6 @@ export class ProtoElementInjectorBuilder extends CompileStep {
6564
return null;
6665
}
6766

68-
_collectDirectiveBindings(pipelineElement) {
69-
var directiveTypes = [];
70-
if (isPresent(pipelineElement.componentDirective)) {
71-
ListWrapper.push(directiveTypes, this._createBinding(pipelineElement.componentDirective));
72-
}
73-
if (isPresent(pipelineElement.templateDirective)) {
74-
ListWrapper.push(directiveTypes, this._createBinding(pipelineElement.templateDirective));
75-
}
76-
if (isPresent(pipelineElement.decoratorDirectives)) {
77-
for (var i=0; i<pipelineElement.decoratorDirectives.length; i++) {
78-
ListWrapper.push(directiveTypes, this._createBinding(pipelineElement.decoratorDirectives[i]));
79-
}
80-
}
81-
return directiveTypes;
82-
}
83-
8467
_createBinding(d:DirectiveMetadata): DirectiveBinding {
8568
return DirectiveBinding.createFromType(d.type, d.annotation);
8669
}

modules/core/test/compiler/integration_spec.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,22 @@ export function main() {
7979
});
8080
});
8181

82+
// GH issue 328 - https://github.com/angular/angular/issues/328
83+
it('should support different directive types on a single node', (done) => {
84+
compiler.compile(MyComp, el('<child-cmp my-dir [elprop]="ctxProp"></child-cmp>')).then((pv) => {
85+
createView(pv);
86+
87+
ctx.ctxProp = 'Hello World!';
88+
cd.detectChanges();
89+
90+
var elInj = view.elementInjectors[0];
91+
expect(elInj.get(MyDir).dirProp).toEqual('Hello World!');
92+
expect(elInj.get(ChildComp).dirProp).toEqual(null);
93+
94+
done();
95+
});
96+
});
97+
8298
it('should support template directives via `<template>` elements.', (done) => {
8399
compiler.compile(MyComp, el('<div><template let-some-tmpl="greeting"><copy-me>{{greeting}}</copy-me></template></div>')).then((pv) => {
84100
createView(pv);
@@ -145,8 +161,10 @@ class MyComp {
145161
})
146162
class ChildComp {
147163
ctxProp:string;
164+
dirProp:string;
148165
constructor(service: MyService) {
149166
this.ctxProp = service.greeting;
167+
this.dirProp = null;
150168
}
151169
}
152170

modules/core/test/compiler/pipeline/directive_parser_spec.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@ export function main() {
6262
expect(results[0].componentDirective).toEqual(reader.read(SomeComponent));
6363
});
6464

65+
it('component directives must be first in collected directives', () => {
66+
var results = createPipeline().process(el('<div some-comp some-decor></div>'));
67+
var dirs = results[0].getAllDirectives();
68+
expect(dirs.length).toEqual(2);
69+
expect(dirs[0]).toEqual(reader.read(SomeComponent));
70+
expect(dirs[1]).toEqual(reader.read(SomeDecorator));
71+
});
72+
6573
it('should detect them in property bindings', () => {
6674
var pipeline = createPipeline({propertyBindings: {
6775
'some-comp': 'someExpr'

modules/core/test/compiler/pipeline/element_binder_builder_spec.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ export function main() {
5656
if (isPresent(current.element.getAttribute('directives'))) {
5757
hasBinding = true;
5858
for (var i=0; i<directives.length; i++) {
59-
current.addDirective(reflector.read(directives[i]));
59+
var dirMetadata = reflector.read(directives[i]);
60+
current.addDirective(dirMetadata);
6061
}
6162
}
6263
if (hasBinding) {
@@ -192,9 +193,9 @@ export function main() {
192193
'boundprop2': 'prop2',
193194
'boundprop3': 'prop3'
194195
});
195-
var directives = [SomeDecoratorDirectiveWith2Bindings,
196+
var directives = [SomeComponentDirectiveWithBinding,
196197
SomeTemplateDirectiveWithBinding,
197-
SomeComponentDirectiveWithBinding];
198+
SomeDecoratorDirectiveWith2Bindings];
198199
var protoElementInjector = new ProtoElementInjector(null, 0, directives, true);
199200
var pipeline = createPipeline({
200201
propertyBindings: propertyBindings,

modules/core/test/compiler/pipeline/proto_element_injector_builder_spec.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ export function main() {
3232
}
3333
if (isPresent(current.element.getAttribute('directives'))) {
3434
for (var i=0; i<directives.length; i++) {
35-
current.addDirective(reader.read(directives[i]));
35+
var dirMetadata = reader.read(directives[i]);
36+
current.addDirective(dirMetadata);
3637
}
3738
}
3839
current.inheritedProtoView = protoView;
@@ -133,9 +134,11 @@ export function main() {
133134

134135
class TestableProtoElementInjectorBuilder extends ProtoElementInjectorBuilder {
135136
debugObjects:List;
137+
136138
constructor() {
137139
this.debugObjects = [];
138140
}
141+
139142
findArgsFor(protoElementInjector:ProtoElementInjector) {
140143
for (var i=0; i<this.debugObjects.length; i+=2) {
141144
if (this.debugObjects[i] === protoElementInjector) {
@@ -144,6 +147,7 @@ class TestableProtoElementInjectorBuilder extends ProtoElementInjectorBuilder {
144147
}
145148
return null;
146149
}
150+
147151
internalCreateProtoElementInjector(parent, index, bindings, firstBindingIsComponent, distance) {
148152
var result = new ProtoElementInjector(parent, index, bindings, firstBindingIsComponent, distance);
149153
ListWrapper.push(this.debugObjects, result);

0 commit comments

Comments
 (0)