@@ -28,9 +28,9 @@ class BlockCache {
28
28
29
29
flush ([Function callback]) {
30
30
groupCache.forEach ((blocks) {
31
- while (! blocks.isEmpty ) {
31
+ while (blocks.isNotEmpty ) {
32
32
Block block = blocks.removeLast ();
33
- if (? callback) callback (block);
33
+ if (callback != null ) callback (block);
34
34
}
35
35
});
36
36
}
@@ -84,10 +84,10 @@ class Block implements ElementWrapper {
84
84
ASSERT (elements != null );
85
85
ASSERT (directivePositions != null );
86
86
ASSERT (blockCaches != null );
87
- _link (elements, directivePositions, blockCaches);
87
+ _link (elements, directivePositions, blockCaches, $injector );
88
88
}
89
89
90
- _link (List <dom.Node > nodeList, List directivePositions, List <BlockCache > blockCaches) {
90
+ _link (List <dom.Node > nodeList, List directivePositions, List <BlockCache > blockCaches, Injector parentInjector ) {
91
91
var stack;
92
92
try {throw '' ;} catch (e,s) {stack = s;}
93
93
var preRenderedIndexOffset = 0 ;
@@ -112,7 +112,7 @@ class Block implements ElementWrapper {
112
112
113
113
Map <String , BlockListFactory > anchorsByName = {};
114
114
List <String > directiveNames = [];
115
-
115
+ var injector = parentInjector;
116
116
if (directiveRefs != null ) {
117
117
for (var j = 0 , jj = directiveRefs.length; j < jj; j++ ) {
118
118
var blockCache;
@@ -135,10 +135,10 @@ class Block implements ElementWrapper {
135
135
anchorsByName[name] = $blockListFactory ([node], directiveRef.blockTypes, blockCache);
136
136
}
137
137
}
138
- _instantiateDirectives (directiveDefsByName, directiveNames, node, anchorsByName);
138
+ injector = _instantiateDirectives (directiveDefsByName, directiveNames, node, anchorsByName, parentInjector );
139
139
}
140
140
if (childDirectivePositions != null ) {
141
- _link (node.nodes, childDirectivePositions, blockCaches);
141
+ _link (node.nodes, childDirectivePositions, blockCaches, injector );
142
142
}
143
143
144
144
if (fakeParent) {
@@ -148,10 +148,11 @@ class Block implements ElementWrapper {
148
148
}
149
149
}
150
150
151
- _instantiateDirectives (Map <String , DirectiveRef > directiveDefsByName,
151
+ Injector _instantiateDirectives (Map <String , DirectiveRef > directiveDefsByName,
152
152
List <String > directiveNames,
153
153
dom.Node node,
154
- Map <String , BlockList > anchorsByName) {
154
+ Map <String , BlockList > anchorsByName,
155
+ Injector parentInjector) {
155
156
var elementModule = new Module ();
156
157
elementModule.value (Block , this );
157
158
elementModule.value (dom.Element , node);
@@ -160,45 +161,88 @@ class Block implements ElementWrapper {
160
161
def.directive.type, def.directive.type));
161
162
162
163
for (var i = 0 , ii = directiveNames.length; i < ii; i++ ) {
163
- var directiveName = directiveNames[i];
164
- DirectiveRef directiveRef = directiveDefsByName[directiveName];
165
-
166
- var directiveModule = new Module ();
167
-
168
- directiveModule.value (DirectiveValue ,
169
- new DirectiveValue .fromString (directiveRef.value));
170
-
171
- directiveModule.value (BlockList , anchorsByName[directiveName]);
172
-
164
+ DirectiveRef directiveRef = directiveDefsByName[directiveNames[i]];
173
165
Type directiveType = directiveRef.directive.type;
166
+ var visibility = local;
167
+ if (directiveRef.directive.$visibility == DirectiveVisibility .CHILDREN ) {
168
+ visibility = null ;
169
+ } else if (directiveRef.directive.$visibility == DirectiveVisibility .DIRECT_CHILDREN ) {
170
+ visibility = directChildren;
171
+ }
172
+ elementModule.type (directiveType, directiveType, creation: directOnly, visibility: visibility);
173
+ }
174
174
175
- var injector = $injector.createChild (
176
- [elementModule, directiveModule],
177
- [directiveType]);
178
-
179
- try {
180
- var directiveInstance = injector.get (directiveType);
181
- if (directiveRef.directive.isComponent) {
182
- directiveInstance = new ComponentWrapper (directiveRef, directiveInstance, node,
183
- $injector.get (Parser ), $injector.get (Compiler ), $injector.get (Http ));
175
+ var injector = parentInjector.createChild ([elementModule]);
176
+
177
+ int prevInstantiatedCount;
178
+ List <String > alreadyInstantiated = < String > [];
179
+ // TODO(pavelgj): this is a workaround for the lack of directive
180
+ // instantiation ordering. A better way is to sort directives in the
181
+ // order they must be instantiated in.
182
+ do {
183
+ prevInstantiatedCount = alreadyInstantiated.length;
184
+ for (var i = 0 , ii = directiveNames.length; i < ii; i++ ) {
185
+ var directiveName = directiveNames[i];
186
+ if (alreadyInstantiated.contains (directiveName)) continue ;
187
+ DirectiveRef directiveRef = directiveDefsByName[directiveName];
188
+
189
+ Map <Type , dynamic > locals = new HashMap <Type , dynamic >();
190
+ locals[DirectiveValue ] =
191
+ new DirectiveValue .fromString (directiveRef.value);
192
+ locals[BlockList ] = anchorsByName[directiveName];
193
+
194
+ Type directiveType = directiveRef.directive.type;
195
+
196
+ try {
197
+ var directiveInstance = injector.instantiate (directiveType, locals);
198
+ alreadyInstantiated.add (directiveName);
199
+ if (directiveRef.directive.isComponent) {
200
+ directiveInstance = new ComponentWrapper (directiveRef, directiveInstance, node,
201
+ $injector.get (Parser ), $injector.get (Compiler ), $injector.get (Http ));
184
202
185
- }
186
- directives.add (directiveInstance);
187
- } catch (e,s) {
188
- var msg;
189
- if (e is MirroredUncaughtExceptionError ) {
190
- //TODO(misko): why is this here? Injector should never throw this exception
191
- msg = e.exception_string + "\n ORIGINAL Stack trace:\n " + e.stacktrace.toString ();
192
- } else {
193
- msg = "Creating $directiveName : " + e.toString () +
203
+ }
204
+ directives.add (directiveInstance);
205
+ } catch (e, s) {
206
+ if (e is MirroredUncaughtExceptionError ) {
207
+ //TODO(misko): why is this here? Injector should never throw this exception
208
+ throw e.exception_string + "\n ORIGINAL Stack trace:\n " + e.stacktrace.toString ();
209
+ } else if (e is IndirectInstantiationError ) {
210
+ // ignore.
211
+ } else {
212
+ throw "Creating $directiveName : " + e.toString () +
194
213
"\n ORIGINAL Stack trace:\n " + s.toString ();
214
+ }
195
215
}
196
-
197
- throw msg;
198
216
}
217
+ } while (alreadyInstantiated.length != prevInstantiatedCount);
218
+
219
+ if (alreadyInstantiated.length != directiveNames.length) {
220
+ throw 'Cyclic dependency in directives on $node .' ;
199
221
}
222
+ return injector;
200
223
}
201
224
225
+
226
+ /// DI creation strategy that only allows 'explicit' injection.
227
+ dynamic directOnly (Symbol type,
228
+ Injector requesting,
229
+ Injector defining,
230
+ bool directInstantation,
231
+ Factory factory ) {
232
+ if (! directInstantation) {
233
+ throw new IndirectInstantiationError (type);
234
+ }
235
+ return factory ();
236
+ }
237
+
238
+ /// DI visibility callback allowin node-local visibility.
239
+ bool local (Injector requesting, Injector defining) =>
240
+ identical (requesting, defining);
241
+
242
+ /// DI visibility callback allowin visibility from direct child into parent.
243
+ bool directChildren (Injector requesting, Injector defining) =>
244
+ local (requesting, defining) || identical (requesting.parent, defining);
245
+
202
246
attach (Scope scope) {
203
247
// Attach directives
204
248
for (var i = 0 , ii = directives.length; i < ii; i++ ) {
@@ -336,6 +380,19 @@ class Block implements ElementWrapper {
336
380
}
337
381
}
338
382
383
+ class IndirectInstantiationError {
384
+ IndirectInstantiationError (type)
385
+ : exception_string = '$type must be directly instantated before being '
386
+ 'injected from child injection' ;
387
+
388
+ /** The result of toString() for the exception object. */
389
+ final String exception_string;
390
+
391
+ String toString () {
392
+ return exception_string;
393
+ }
394
+ }
395
+
339
396
class ComponentWrapper {
340
397
DirectiveRef directiveRef;
341
398
dynamic controller;
0 commit comments