BlockFactory class
BlockFactory is used to create new Blocks. BlockFactory is created by the Compiler as a result of compiling a template.
class BlockFactory { final List directivePositions; final List<dom.Node> templateElements; final Profiler _perf; BlockFactory(this.templateElements, this.directivePositions, this._perf); BoundBlockFactory bind(Injector injector) { return new BoundBlockFactory(this, injector); } Block call(Injector injector, [List<dom.Node> elements]) { if (elements == null) { elements = cloneElements(templateElements); } try { assert(_perf.startTimer('ng.block') != false); var block = new Block(elements); _link(block, elements, directivePositions, injector); return block; } finally { assert(_perf.stopTimer('ng.block') != false); } } _link(Block block, List<dom.Node> nodeList, List directivePositions, Injector parentInjector) { var preRenderedIndexOffset = 0; var directiveDefsByName = { }; for (num i = 0, ii = directivePositions.length; i < ii;) { num index = directivePositions[i++]; List<DirectiveRef> directiveRefs = directivePositions[i++]; List childDirectivePositions = directivePositions[i++]; var nodeListIndex = index + preRenderedIndexOffset; dom.Node node = nodeList[nodeListIndex]; try { assert(_perf.startTimer('ng.block.link(${_html(node)})') != false); // if node isn't attached to the DOM, create a parent for it. var parentNode = node.parentNode; var fakeParent = false; if (parentNode == null) { fakeParent = true; parentNode = new dom.DivElement(); parentNode.append(node); } var childInjector = _instantiateDirectives(block, parentInjector, node, directiveRefs, parentInjector.get(Parser)); if (childDirectivePositions != null) { _link(block, node.nodes, childDirectivePositions, childInjector); } if (fakeParent) { // extract the node from the parentNode. nodeList[nodeListIndex] = parentNode.nodes[0]; } } finally { assert(_perf.stopTimer('ng.block.link(${_html(node)})') != false); } } } Injector _instantiateDirectives(Block block, Injector parentInjector, dom.Node node, List<DirectiveRef> directiveRefs, Parser parser) { assert(_perf.startTimer('ng.block.link.setUp(${_html(node)}})') != false); Injector nodeInjector; Scope scope; Map<Type, _ComponentFactory> fctrs; var nodeAttrs = node is dom.Element ? new NodeAttrs(node) : null; try { if (directiveRefs == null || directiveRefs.length == 0) return parentInjector; var nodeModule = new Module(); var blockHoleFactory = (_) => null; var blockFactory = (_) => null; var boundBlockFactory = (_) => null; var nodesAttrsDirectives = null; nodeModule.value(Block, block); 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.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(TextChangeListener)); }); } 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); } }); } 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); BlockCache blockCache = injector.get(BlockCache); Http http = injector.get(Http); TemplateCache templateCache = injector.get(TemplateCache); // 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)); if (fctrs == null) fctrs = new Map<Type, _ComponentFactory>(); fctrs[ref.type] = componentFactory; return componentFactory.call(injector, compiler, scope, blockCache, http, templateCache); }, 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); blockHoleFactory = (_) => new BlockHole([node]); blockFactory = (_) => ref.blockFactory; boundBlockFactory = (Injector injector) => ref.blockFactory.bind(injector); } }); nodeModule.factory(BlockHole, blockHoleFactory); nodeModule.factory(BlockFactory, blockFactory); nodeModule.factory(BoundBlockFactory, boundBlockFactory); nodeInjector = parentInjector.createChild([nodeModule]); scope = nodeInjector.get(Scope); } finally { assert(_perf.stopTimer('ng.block.link.setUp(${_html(node)}})') != false); } directiveRefs.forEach((DirectiveRef ref) { try { assert(_perf.startTimer('ng.block.link.${ref.type}') != false); var controller = nodeInjector.get(ref.type); assert(_perf.startTimer('ng.block.link.${ref.type}.map') != false); var shadowScope = (fctrs != null && fctrs.containsKey(ref.type)) ? fctrs[ref.type].shadowScope : null; if (ref.annotation.publishAs != null) { (shadowScope == null ? scope : shadowScope)[ref.annotation.publishAs] = controller; } if (nodeAttrs == null) nodeAttrs = new _AnchorAttrs(ref); for(var map in ref.mappings) { map(nodeAttrs, scope, controller); } if (controller is NgAttachAware) { var removeWatcher; removeWatcher = scope.$watch(() { removeWatcher(); controller.attach(); }); } if (controller is NgDetachAware) { scope.$on(r'$destroy', controller.detach); } assert(_perf.stopTimer('ng.block.link.${ref.type}.map') != false); } finally { assert(_perf.stopTimer('ng.block.link.${ref.type}') != false); } }); return nodeInjector; } // 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); }; }
Constructors
Properties
final List directivePositions #
final List directivePositions
final List<Node> templateElements #
final List<dom.Node> templateElements
Methods
BoundBlockFactory bind(Injector injector) #
BoundBlockFactory bind(Injector injector) { return new BoundBlockFactory(this, injector); }
Block call(Injector injector, [List<Node> elements]) #
Block call(Injector injector, [List<dom.Node> elements]) { if (elements == null) { elements = cloneElements(templateElements); } try { assert(_perf.startTimer('ng.block') != false); var block = new Block(elements); _link(block, elements, directivePositions, injector); return block; } finally { assert(_perf.stopTimer('ng.block') != false); } }