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);
}
}