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

Commit

Permalink
feat(element binder): ElementBinder.bind
Browse files Browse the repository at this point in the history
  • Loading branch information
jbdeboer committed Mar 11, 2014
1 parent eb559ad commit b1a518b
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 236 deletions.
2 changes: 0 additions & 2 deletions lib/core_dom/common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ class DirectiveRef {
final String value;
final List<ApplyMapping> mappings = new List<ApplyMapping>();

ViewFactory viewFactory;

DirectiveRef(this.element, this.type, this.annotation, [ this.value ]);

String toString() {
Expand Down
35 changes: 15 additions & 20 deletions lib/core_dom/compiler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,37 +15,33 @@ class Compiler implements Function {
List<ElementBinder> elementBinders = null; // don't pre-create to create sparse tree and prevent GC pressure.

do {
ElementBinder declaredElementSelector = existingElementBinder == null
ElementBinder elementBinder = existingElementBinder == null
? directives.selector(domCursor.current)
: existingElementBinder;

declaredElementSelector.offsetIndex = templateCursor.index;
elementBinder.offsetIndex = templateCursor.index;

var compileTransclusionCallback = (ElementBinder transclusionBinder) {
return compileTransclusion(
if (elementBinder.hasTemplate) {
elementBinder.templateViewFactory = compileTransclusion(
domCursor, templateCursor,
declaredElementSelector.template, transclusionBinder, directives);
};
elementBinder.template, elementBinder.templateBinder, directives);
}

var compileChildrenCallback = () {
var childDirectivePositions = null;
if (elementBinder.shouldCompileChildren) {
if (domCursor.descend()) {
templateCursor.descend();

childDirectivePositions =
_compileView(domCursor, templateCursor, null, directives);
elementBinder.childElementBinders =
_compileView(domCursor, templateCursor, null, directives);

domCursor.ascend();
templateCursor.ascend();
}
return childDirectivePositions;
};

declaredElementSelector.walkDOM(compileTransclusionCallback, compileChildrenCallback);
}

if (declaredElementSelector.isUseful()) {
if (elementBinder.isUseful) {
if (elementBinders == null) elementBinders = [];
elementBinders.add(declaredElementSelector);
elementBinders.add(elementBinder);
}
} while (templateCursor.moveNext() && domCursor.moveNext());

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

var transcludeCursor = templateCursor.replaceWithAnchor(anchorName);
var domCursorIndex = domCursor.index;
var directivePositions =
var elementBinders =
_compileView(domCursor, transcludeCursor, transcludedElementBinder, directives);
if (directivePositions == null) directivePositions = [];
if (elementBinders == null) elementBinders = [];

viewFactory = new ViewFactory(transcludeCursor.elements,
directivePositions, _perf, _expando);
viewFactory = new ViewFactory(transcludeCursor.elements, elementBinders, _perf, _expando);
domCursor.index = domCursorIndex;

if (domCursor.isInstance) {
Expand Down
242 changes: 215 additions & 27 deletions lib/core_dom/element_binder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ part of angular.core.dom;

@NgInjectableService()
class ElementBinderFactory {
Parser _parser;
final Parser _parser;
final Profiler _perf;
final Expando _expando;

ElementBinderFactory(Parser this._parser);
ElementBinderFactory(this._parser, this._perf, this._expando);

binder([int templateIndex]) {
return new ElementBinder(_parser);
binder() {
return new ElementBinder(_parser, _perf, _expando);
}
}

Expand All @@ -17,31 +19,38 @@ class ElementBinderFactory {
*/

class ElementBinder {
// DI Services
Parser _parser;
Profiler _perf;
Expando _expando;

ElementBinder(this._parser);

ElementBinder.forTransclusion(ElementBinder other) {
_parser = other._parser;
decorators = other.decorators;
component = other.component;
childMode = other.childMode;
childElementBinders = other.childElementBinders;
}

// Member fields
List<DirectiveRef> decorators = [];

DirectiveRef template;
ViewFactory templateViewFactory;

DirectiveRef component;

var childElementBinders;

var offsetIndex;

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


ElementBinder(this._parser, this._perf, this._expando);

ElementBinder.forTransclusion(ElementBinder other) {
_parser = other._parser;
_perf = other._perf;
_expando = other._expando;

decorators = other.decorators;
component = other.component;
childElementBinders = other.childElementBinders;
offsetIndex = other.offsetIndex;
childMode = other.childMode;
}

addDirective(DirectiveRef ref) {
var annotation = ref.annotation;
var children = annotation.children;
Expand All @@ -61,15 +70,19 @@ class ElementBinder {
createMappings(ref);
}

List<DirectiveRef> walkDOM(compileTransclusionCallback, compileChildrenCallback) {
if (template != null) {
template.viewFactory = compileTransclusionCallback(new ElementBinder.forTransclusion(this));
} else if (childMode == NgAnnotation.COMPILE_CHILDREN) {
childElementBinders = compileChildrenCallback();
}
bool get hasTemplate {
return template != null;
}

List<DirectiveRef> get usableDirectiveRefs {
bool get shouldCompileChildren {
return childMode == NgAnnotation.COMPILE_CHILDREN;
}

ElementBinder get templateBinder {
return new ElementBinder.forTransclusion(this);
}

List<DirectiveRef> get _usableDirectiveRefs {
if (template != null) {
return [template];
}
Expand All @@ -79,9 +92,185 @@ class ElementBinder {
return decorators;
}

bool get isUseful {
return (_usableDirectiveRefs != null && _usableDirectiveRefs.length != 0) || childElementBinders != null;
}

// DI visibility callback allowing node-local visibility.

static final Function _elementOnly = (Injector requesting, Injector defining) {
if (requesting.name == _SHADOW) {
requesting = requesting.parent;
}
return identical(requesting, defining);
};

// DI visibility callback allowing visibility from direct child into parent.

static final Function _elementDirectChildren = (Injector requesting, Injector defining) {
if (requesting.name == _SHADOW) {
requesting = requesting.parent;
}
return _elementOnly(requesting, defining) || identical(requesting.parent, defining);
};

Injector bind(View view, Injector parentInjector, dom.Node node) {
var timerId;
assert((timerId = _perf.startTimer('ng.view.link.setUp', _html(node))) != false);
Injector nodeInjector;
Scope scope = parentInjector.get(Scope);
FilterMap filters = parentInjector.get(FilterMap);
Map<Type, _ComponentFactory> fctrs;
var nodeAttrs = node is dom.Element ? new NodeAttrs(node) : null;
ElementProbe probe;

var directiveRefs = _usableDirectiveRefs;
try {
if (directiveRefs == null || directiveRefs.length == 0) return parentInjector;
var nodeModule = new Module();
var viewPortFactory = (_) => null;
var viewFactory = (_) => null;
var boundViewFactory = (_) => null;
var nodesAttrsDirectives = null;

bool isUseful() {
return (usableDirectiveRefs != null && usableDirectiveRefs.length != 0) || childElementBinders != null;
nodeModule.value(View, view);
nodeModule.value(dom.Element, node);
nodeModule.value(dom.Node, node);
nodeModule.value(NodeAttrs, nodeAttrs);
directiveRefs.forEach((DirectiveRef ref) {
NgAnnotation annotation = ref.annotation;
var visibility = _elementOnly;
if (ref.annotation is NgController) {
scope = scope.createChild(new PrototypeMap(scope.context));
nodeModule.value(Scope, scope);
}
if (ref.annotation.visibility == NgDirective.CHILDREN_VISIBILITY) {
visibility = null;
} else if (ref.annotation.visibility == NgDirective.DIRECT_CHILDREN_VISIBILITY) {
visibility = _elementDirectChildren;
}
if (ref.type == NgTextMustacheDirective) {
nodeModule.factory(NgTextMustacheDirective, (Injector injector) {
return new NgTextMustacheDirective(
node, ref.value, injector.get(Interpolate), injector.get(Scope),
injector.get(AstParser), injector.get(FilterMap));
});
} else if (ref.type == NgAttrMustacheDirective) {
if (nodesAttrsDirectives == null) {
nodesAttrsDirectives = [];
nodeModule.factory(NgAttrMustacheDirective, (Injector injector) {
var scope = injector.get(Scope);
var interpolate = injector.get(Interpolate);
for (var ref in nodesAttrsDirectives) {
new NgAttrMustacheDirective(nodeAttrs, ref.value, interpolate,
scope, injector.get(AstParser), injector.get(FilterMap));
}
});
}
nodesAttrsDirectives.add(ref);
} else if (ref.annotation is NgComponent) {
//nodeModule.factory(type, new ComponentFactory(node, ref.directive), visibility: visibility);
// TODO(misko): there should be no need to wrap function like this.
nodeModule.factory(ref.type, (Injector injector) {
Compiler compiler = injector.get(Compiler);
Scope scope = injector.get(Scope);
ViewCache viewCache = injector.get(ViewCache);
Http http = injector.get(Http);
TemplateCache templateCache = injector.get(TemplateCache);
DirectiveMap directives = injector.get(DirectiveMap);
// This is a bit of a hack since we are returning different type then we are.
var componentFactory = new _ComponentFactory(node, ref.type,
ref.annotation as NgComponent,
injector.get(dom.NodeTreeSanitizer), _expando);
if (fctrs == null) fctrs = new Map<Type, _ComponentFactory>();
fctrs[ref.type] = componentFactory;
return componentFactory.call(injector, compiler, scope, viewCache, http, templateCache, directives);
}, visibility: visibility);
} else {
nodeModule.type(ref.type, visibility: visibility);
}
for (var publishType in ref.annotation.publishTypes) {
nodeModule.factory(publishType, (Injector injector) => injector.get(ref.type), visibility: visibility);
}
if (annotation.children == NgAnnotation.TRANSCLUDE_CHILDREN) {
// Currently, transclude is only supported for NgDirective.
assert(annotation is NgDirective);
viewPortFactory = (_) => new ViewPort(node,
parentInjector.get(NgAnimate));
viewFactory = (_) => templateViewFactory;
boundViewFactory = (Injector injector) => templateViewFactory.bind(injector);
}
});
nodeModule
..factory(ViewPort, viewPortFactory)
..factory(ViewFactory, viewFactory)
..factory(BoundViewFactory, boundViewFactory)
..factory(ElementProbe, (_) => probe);
nodeInjector = parentInjector.createChild([nodeModule]);
probe = _expando[node] = new ElementProbe(
parentInjector.get(ElementProbe), node, nodeInjector, scope);
} finally {
assert(_perf.stopTimer(timerId) != false);
}
directiveRefs.forEach((DirectiveRef ref) {
var linkTimer;
try {
var linkMapTimer;
assert((linkTimer = _perf.startTimer('ng.view.link', ref.type)) != false);
var controller = nodeInjector.get(ref.type);
probe.directives.add(controller);
assert((linkMapTimer = _perf.startTimer('ng.view.link.map', ref.type)) != false);
var shadowScope = (fctrs != null && fctrs.containsKey(ref.type)) ? fctrs[ref.type].shadowScope : null;
if (ref.annotation is NgController) {
scope.context[(ref.annotation as NgController).publishAs] = controller;
} else if (ref.annotation is NgComponent) {
shadowScope.context[(ref.annotation as NgComponent).publishAs] = controller;
}
if (nodeAttrs == null) nodeAttrs = new _AnchorAttrs(ref);
var attachDelayStatus = controller is NgAttachAware ? [false] : null;
checkAttachReady() {
if (attachDelayStatus.reduce((a, b) => a && b)) {
attachDelayStatus = null;
if (scope.isAttached) {
controller.attach();
}
}
}
for (var map in ref.mappings) {
var notify;
if (attachDelayStatus != null) {
var index = attachDelayStatus.length;
attachDelayStatus.add(false);
notify = () {
if (attachDelayStatus != null) {
attachDelayStatus[index] = true;
checkAttachReady();
}
};
} else {
notify = () => null;
}
map(nodeAttrs, scope, controller, filters, notify);
}
if (attachDelayStatus != null) {
Watch watch;
watch = scope.watch(
'1', // Cheat a bit.
(_, __) {
watch.remove();
attachDelayStatus[0] = true;
checkAttachReady();
});
}
if (controller is NgDetachAware) {
scope.on(ScopeEvent.DESTROY).listen((_) => controller.detach());
}
assert(_perf.stopTimer(linkMapTimer) != false);
} finally {
assert(_perf.stopTimer(linkTimer) != false);
}
});
return nodeInjector;
}

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

}
Loading

0 comments on commit b1a518b

Please sign in to comment.