Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

Commit b1a518b

Browse files
committed
feat(element binder): ElementBinder.bind
1 parent eb559ad commit b1a518b

File tree

5 files changed

+235
-236
lines changed

5 files changed

+235
-236
lines changed

lib/core_dom/common.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ class DirectiveRef {
1414
final String value;
1515
final List<ApplyMapping> mappings = new List<ApplyMapping>();
1616

17-
ViewFactory viewFactory;
18-
1917
DirectiveRef(this.element, this.type, this.annotation, [ this.value ]);
2018

2119
String toString() {

lib/core_dom/compiler.dart

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,37 +15,33 @@ class Compiler implements Function {
1515
List<ElementBinder> elementBinders = null; // don't pre-create to create sparse tree and prevent GC pressure.
1616

1717
do {
18-
ElementBinder declaredElementSelector = existingElementBinder == null
18+
ElementBinder elementBinder = existingElementBinder == null
1919
? directives.selector(domCursor.current)
2020
: existingElementBinder;
2121

22-
declaredElementSelector.offsetIndex = templateCursor.index;
22+
elementBinder.offsetIndex = templateCursor.index;
2323

24-
var compileTransclusionCallback = (ElementBinder transclusionBinder) {
25-
return compileTransclusion(
24+
if (elementBinder.hasTemplate) {
25+
elementBinder.templateViewFactory = compileTransclusion(
2626
domCursor, templateCursor,
27-
declaredElementSelector.template, transclusionBinder, directives);
28-
};
27+
elementBinder.template, elementBinder.templateBinder, directives);
28+
}
2929

30-
var compileChildrenCallback = () {
31-
var childDirectivePositions = null;
30+
if (elementBinder.shouldCompileChildren) {
3231
if (domCursor.descend()) {
3332
templateCursor.descend();
3433

35-
childDirectivePositions =
36-
_compileView(domCursor, templateCursor, null, directives);
34+
elementBinder.childElementBinders =
35+
_compileView(domCursor, templateCursor, null, directives);
3736

3837
domCursor.ascend();
3938
templateCursor.ascend();
4039
}
41-
return childDirectivePositions;
42-
};
43-
44-
declaredElementSelector.walkDOM(compileTransclusionCallback, compileChildrenCallback);
40+
}
4541

46-
if (declaredElementSelector.isUseful()) {
42+
if (elementBinder.isUseful) {
4743
if (elementBinders == null) elementBinders = [];
48-
elementBinders.add(declaredElementSelector);
44+
elementBinders.add(elementBinder);
4945
}
5046
} while (templateCursor.moveNext() && domCursor.moveNext());
5147

@@ -64,12 +60,11 @@ class Compiler implements Function {
6460

6561
var transcludeCursor = templateCursor.replaceWithAnchor(anchorName);
6662
var domCursorIndex = domCursor.index;
67-
var directivePositions =
63+
var elementBinders =
6864
_compileView(domCursor, transcludeCursor, transcludedElementBinder, directives);
69-
if (directivePositions == null) directivePositions = [];
65+
if (elementBinders == null) elementBinders = [];
7066

71-
viewFactory = new ViewFactory(transcludeCursor.elements,
72-
directivePositions, _perf, _expando);
67+
viewFactory = new ViewFactory(transcludeCursor.elements, elementBinders, _perf, _expando);
7368
domCursor.index = domCursorIndex;
7469

7570
if (domCursor.isInstance) {

lib/core_dom/element_binder.dart

Lines changed: 215 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ part of angular.core.dom;
22

33
@NgInjectableService()
44
class ElementBinderFactory {
5-
Parser _parser;
5+
final Parser _parser;
6+
final Profiler _perf;
7+
final Expando _expando;
68

7-
ElementBinderFactory(Parser this._parser);
9+
ElementBinderFactory(this._parser, this._perf, this._expando);
810

9-
binder([int templateIndex]) {
10-
return new ElementBinder(_parser);
11+
binder() {
12+
return new ElementBinder(_parser, _perf, _expando);
1113
}
1214
}
1315

@@ -17,31 +19,38 @@ class ElementBinderFactory {
1719
*/
1820

1921
class ElementBinder {
22+
// DI Services
2023
Parser _parser;
24+
Profiler _perf;
25+
Expando _expando;
2126

22-
ElementBinder(this._parser);
23-
24-
ElementBinder.forTransclusion(ElementBinder other) {
25-
_parser = other._parser;
26-
decorators = other.decorators;
27-
component = other.component;
28-
childMode = other.childMode;
29-
childElementBinders = other.childElementBinders;
30-
}
31-
27+
// Member fields
3228
List<DirectiveRef> decorators = [];
33-
3429
DirectiveRef template;
30+
ViewFactory templateViewFactory;
3531

3632
DirectiveRef component;
37-
3833
var childElementBinders;
39-
4034
var offsetIndex;
4135

4236
// Can be either COMPILE_CHILDREN or IGNORE_CHILDREN
4337
String childMode = NgAnnotation.COMPILE_CHILDREN;
4438

39+
40+
ElementBinder(this._parser, this._perf, this._expando);
41+
42+
ElementBinder.forTransclusion(ElementBinder other) {
43+
_parser = other._parser;
44+
_perf = other._perf;
45+
_expando = other._expando;
46+
47+
decorators = other.decorators;
48+
component = other.component;
49+
childElementBinders = other.childElementBinders;
50+
offsetIndex = other.offsetIndex;
51+
childMode = other.childMode;
52+
}
53+
4554
addDirective(DirectiveRef ref) {
4655
var annotation = ref.annotation;
4756
var children = annotation.children;
@@ -61,15 +70,19 @@ class ElementBinder {
6170
createMappings(ref);
6271
}
6372

64-
List<DirectiveRef> walkDOM(compileTransclusionCallback, compileChildrenCallback) {
65-
if (template != null) {
66-
template.viewFactory = compileTransclusionCallback(new ElementBinder.forTransclusion(this));
67-
} else if (childMode == NgAnnotation.COMPILE_CHILDREN) {
68-
childElementBinders = compileChildrenCallback();
69-
}
73+
bool get hasTemplate {
74+
return template != null;
7075
}
7176

72-
List<DirectiveRef> get usableDirectiveRefs {
77+
bool get shouldCompileChildren {
78+
return childMode == NgAnnotation.COMPILE_CHILDREN;
79+
}
80+
81+
ElementBinder get templateBinder {
82+
return new ElementBinder.forTransclusion(this);
83+
}
84+
85+
List<DirectiveRef> get _usableDirectiveRefs {
7386
if (template != null) {
7487
return [template];
7588
}
@@ -79,9 +92,185 @@ class ElementBinder {
7992
return decorators;
8093
}
8194

95+
bool get isUseful {
96+
return (_usableDirectiveRefs != null && _usableDirectiveRefs.length != 0) || childElementBinders != null;
97+
}
98+
99+
// DI visibility callback allowing node-local visibility.
100+
101+
static final Function _elementOnly = (Injector requesting, Injector defining) {
102+
if (requesting.name == _SHADOW) {
103+
requesting = requesting.parent;
104+
}
105+
return identical(requesting, defining);
106+
};
107+
108+
// DI visibility callback allowing visibility from direct child into parent.
109+
110+
static final Function _elementDirectChildren = (Injector requesting, Injector defining) {
111+
if (requesting.name == _SHADOW) {
112+
requesting = requesting.parent;
113+
}
114+
return _elementOnly(requesting, defining) || identical(requesting.parent, defining);
115+
};
116+
117+
Injector bind(View view, Injector parentInjector, dom.Node node) {
118+
var timerId;
119+
assert((timerId = _perf.startTimer('ng.view.link.setUp', _html(node))) != false);
120+
Injector nodeInjector;
121+
Scope scope = parentInjector.get(Scope);
122+
FilterMap filters = parentInjector.get(FilterMap);
123+
Map<Type, _ComponentFactory> fctrs;
124+
var nodeAttrs = node is dom.Element ? new NodeAttrs(node) : null;
125+
ElementProbe probe;
126+
127+
var directiveRefs = _usableDirectiveRefs;
128+
try {
129+
if (directiveRefs == null || directiveRefs.length == 0) return parentInjector;
130+
var nodeModule = new Module();
131+
var viewPortFactory = (_) => null;
132+
var viewFactory = (_) => null;
133+
var boundViewFactory = (_) => null;
134+
var nodesAttrsDirectives = null;
82135

83-
bool isUseful() {
84-
return (usableDirectiveRefs != null && usableDirectiveRefs.length != 0) || childElementBinders != null;
136+
nodeModule.value(View, view);
137+
nodeModule.value(dom.Element, node);
138+
nodeModule.value(dom.Node, node);
139+
nodeModule.value(NodeAttrs, nodeAttrs);
140+
directiveRefs.forEach((DirectiveRef ref) {
141+
NgAnnotation annotation = ref.annotation;
142+
var visibility = _elementOnly;
143+
if (ref.annotation is NgController) {
144+
scope = scope.createChild(new PrototypeMap(scope.context));
145+
nodeModule.value(Scope, scope);
146+
}
147+
if (ref.annotation.visibility == NgDirective.CHILDREN_VISIBILITY) {
148+
visibility = null;
149+
} else if (ref.annotation.visibility == NgDirective.DIRECT_CHILDREN_VISIBILITY) {
150+
visibility = _elementDirectChildren;
151+
}
152+
if (ref.type == NgTextMustacheDirective) {
153+
nodeModule.factory(NgTextMustacheDirective, (Injector injector) {
154+
return new NgTextMustacheDirective(
155+
node, ref.value, injector.get(Interpolate), injector.get(Scope),
156+
injector.get(AstParser), injector.get(FilterMap));
157+
});
158+
} else if (ref.type == NgAttrMustacheDirective) {
159+
if (nodesAttrsDirectives == null) {
160+
nodesAttrsDirectives = [];
161+
nodeModule.factory(NgAttrMustacheDirective, (Injector injector) {
162+
var scope = injector.get(Scope);
163+
var interpolate = injector.get(Interpolate);
164+
for (var ref in nodesAttrsDirectives) {
165+
new NgAttrMustacheDirective(nodeAttrs, ref.value, interpolate,
166+
scope, injector.get(AstParser), injector.get(FilterMap));
167+
}
168+
});
169+
}
170+
nodesAttrsDirectives.add(ref);
171+
} else if (ref.annotation is NgComponent) {
172+
//nodeModule.factory(type, new ComponentFactory(node, ref.directive), visibility: visibility);
173+
// TODO(misko): there should be no need to wrap function like this.
174+
nodeModule.factory(ref.type, (Injector injector) {
175+
Compiler compiler = injector.get(Compiler);
176+
Scope scope = injector.get(Scope);
177+
ViewCache viewCache = injector.get(ViewCache);
178+
Http http = injector.get(Http);
179+
TemplateCache templateCache = injector.get(TemplateCache);
180+
DirectiveMap directives = injector.get(DirectiveMap);
181+
// This is a bit of a hack since we are returning different type then we are.
182+
var componentFactory = new _ComponentFactory(node, ref.type,
183+
ref.annotation as NgComponent,
184+
injector.get(dom.NodeTreeSanitizer), _expando);
185+
if (fctrs == null) fctrs = new Map<Type, _ComponentFactory>();
186+
fctrs[ref.type] = componentFactory;
187+
return componentFactory.call(injector, compiler, scope, viewCache, http, templateCache, directives);
188+
}, visibility: visibility);
189+
} else {
190+
nodeModule.type(ref.type, visibility: visibility);
191+
}
192+
for (var publishType in ref.annotation.publishTypes) {
193+
nodeModule.factory(publishType, (Injector injector) => injector.get(ref.type), visibility: visibility);
194+
}
195+
if (annotation.children == NgAnnotation.TRANSCLUDE_CHILDREN) {
196+
// Currently, transclude is only supported for NgDirective.
197+
assert(annotation is NgDirective);
198+
viewPortFactory = (_) => new ViewPort(node,
199+
parentInjector.get(NgAnimate));
200+
viewFactory = (_) => templateViewFactory;
201+
boundViewFactory = (Injector injector) => templateViewFactory.bind(injector);
202+
}
203+
});
204+
nodeModule
205+
..factory(ViewPort, viewPortFactory)
206+
..factory(ViewFactory, viewFactory)
207+
..factory(BoundViewFactory, boundViewFactory)
208+
..factory(ElementProbe, (_) => probe);
209+
nodeInjector = parentInjector.createChild([nodeModule]);
210+
probe = _expando[node] = new ElementProbe(
211+
parentInjector.get(ElementProbe), node, nodeInjector, scope);
212+
} finally {
213+
assert(_perf.stopTimer(timerId) != false);
214+
}
215+
directiveRefs.forEach((DirectiveRef ref) {
216+
var linkTimer;
217+
try {
218+
var linkMapTimer;
219+
assert((linkTimer = _perf.startTimer('ng.view.link', ref.type)) != false);
220+
var controller = nodeInjector.get(ref.type);
221+
probe.directives.add(controller);
222+
assert((linkMapTimer = _perf.startTimer('ng.view.link.map', ref.type)) != false);
223+
var shadowScope = (fctrs != null && fctrs.containsKey(ref.type)) ? fctrs[ref.type].shadowScope : null;
224+
if (ref.annotation is NgController) {
225+
scope.context[(ref.annotation as NgController).publishAs] = controller;
226+
} else if (ref.annotation is NgComponent) {
227+
shadowScope.context[(ref.annotation as NgComponent).publishAs] = controller;
228+
}
229+
if (nodeAttrs == null) nodeAttrs = new _AnchorAttrs(ref);
230+
var attachDelayStatus = controller is NgAttachAware ? [false] : null;
231+
checkAttachReady() {
232+
if (attachDelayStatus.reduce((a, b) => a && b)) {
233+
attachDelayStatus = null;
234+
if (scope.isAttached) {
235+
controller.attach();
236+
}
237+
}
238+
}
239+
for (var map in ref.mappings) {
240+
var notify;
241+
if (attachDelayStatus != null) {
242+
var index = attachDelayStatus.length;
243+
attachDelayStatus.add(false);
244+
notify = () {
245+
if (attachDelayStatus != null) {
246+
attachDelayStatus[index] = true;
247+
checkAttachReady();
248+
}
249+
};
250+
} else {
251+
notify = () => null;
252+
}
253+
map(nodeAttrs, scope, controller, filters, notify);
254+
}
255+
if (attachDelayStatus != null) {
256+
Watch watch;
257+
watch = scope.watch(
258+
'1', // Cheat a bit.
259+
(_, __) {
260+
watch.remove();
261+
attachDelayStatus[0] = true;
262+
checkAttachReady();
263+
});
264+
}
265+
if (controller is NgDetachAware) {
266+
scope.on(ScopeEvent.DESTROY).listen((_) => controller.detach());
267+
}
268+
assert(_perf.stopTimer(linkMapTimer) != false);
269+
} finally {
270+
assert(_perf.stopTimer(linkTimer) != false);
271+
}
272+
});
273+
return nodeInjector;
85274
}
86275

87276
static RegExp _MAPPING = new RegExp(r'^(\@|=\>\!|\=\>|\<\=\>|\&)\s*(.*)$');
@@ -188,5 +377,4 @@ class ElementBinder {
188377
ref.mappings.add(mappingFn);
189378
});
190379
}
191-
192380
}

0 commit comments

Comments
 (0)