refactor(shadow dom): do not use injectors nor directives
This prepares us for the app/render split in the compiler.
This commit is contained in:
2
modules/angular2/src/core/application.js
vendored
2
modules/angular2/src/core/application.js
vendored
@ -67,7 +67,7 @@ function _injectorBindings(appComponentType): List<Binding> {
|
||||
// the angular application. Thus the context and lightDomInjector are
|
||||
// empty.
|
||||
var view = appProtoView.instantiate(null, eventManager);
|
||||
view.hydrate(injector, null, new Object());
|
||||
view.hydrate(injector, null, null, new Object());
|
||||
return view;
|
||||
});
|
||||
}, [ChangeDetection, Compiler, Injector, appElementToken, appComponentAnnotatedTypeToken,
|
||||
|
13
modules/angular2/src/core/compiler/compiler.js
vendored
13
modules/angular2/src/core/compiler/compiler.js
vendored
@ -11,7 +11,6 @@ import {CompileElement} from './pipeline/compile_element';
|
||||
import {createDefaultSteps} from './pipeline/default_steps';
|
||||
import {TemplateLoader} from './template_loader';
|
||||
import {TemplateResolver} from './template_resolver';
|
||||
import {DirectiveMetadata} from './directive_metadata';
|
||||
import {Template} from '../annotations/template';
|
||||
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
||||
import {CompileStep} from './pipeline/compile_step';
|
||||
@ -56,7 +55,6 @@ export class Compiler {
|
||||
_templateLoader:TemplateLoader;
|
||||
_compiling:Map<Type, Promise>;
|
||||
_shadowDomStrategy: ShadowDomStrategy;
|
||||
_shadowDomDirectives: List<DirectiveMetadata>;
|
||||
_templateResolver: TemplateResolver;
|
||||
_componentUrlMapper: ComponentUrlMapper;
|
||||
_urlResolver: UrlResolver;
|
||||
@ -80,11 +78,6 @@ export class Compiler {
|
||||
this._templateLoader = templateLoader;
|
||||
this._compiling = MapWrapper.create();
|
||||
this._shadowDomStrategy = shadowDomStrategy;
|
||||
this._shadowDomDirectives = [];
|
||||
var types = shadowDomStrategy.polyfillDirectives();
|
||||
for (var i = 0; i < types.length; i++) {
|
||||
ListWrapper.push(this._shadowDomDirectives, reader.read(types[i]));
|
||||
}
|
||||
this._templateResolver = templateResolver;
|
||||
this._componentUrlMapper = componentUrlMapper;
|
||||
this._urlResolver = urlResolver;
|
||||
@ -93,12 +86,8 @@ export class Compiler {
|
||||
}
|
||||
|
||||
createSteps(component:Type, template: Template):List<CompileStep> {
|
||||
// Merge directive metadata (from the template and from the shadow dom strategy)
|
||||
var dirMetadata = [];
|
||||
var tplMetadata = ListWrapper.map(this._flattenDirectives(template),
|
||||
var dirMetadata = ListWrapper.map(this._flattenDirectives(template),
|
||||
(d) => this._reader.read(d));
|
||||
dirMetadata = ListWrapper.concat(dirMetadata, tplMetadata);
|
||||
dirMetadata = ListWrapper.concat(dirMetadata, this._shadowDomDirectives);
|
||||
|
||||
var cmpMetadata = this._reader.read(component);
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
import {int, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||
import {ProtoElementInjector} from './element_injector';
|
||||
import {DirectiveMetadata} from './directive_metadata';
|
||||
import {List, StringMap} from 'angular2/src/facade/collection';
|
||||
@ -11,12 +12,24 @@ export class ElementBinder {
|
||||
hasElementPropertyBindings:boolean;
|
||||
nestedProtoView: ProtoView;
|
||||
events:StringMap;
|
||||
contentTagSelector:string;
|
||||
parent:ElementBinder;
|
||||
index:int;
|
||||
distanceToParent:int;
|
||||
constructor(
|
||||
index:int, parent:ElementBinder, distanceToParent: int,
|
||||
protoElementInjector: ProtoElementInjector, componentDirective:DirectiveMetadata,
|
||||
viewportDirective:DirectiveMetadata) {
|
||||
if (isBlank(index)) {
|
||||
throw new BaseException('null index not allowed.');
|
||||
}
|
||||
|
||||
this.protoElementInjector = protoElementInjector;
|
||||
this.componentDirective = componentDirective;
|
||||
this.viewportDirective = viewportDirective;
|
||||
this.parent = parent;
|
||||
this.index = index;
|
||||
this.distanceToParent = distanceToParent;
|
||||
// updated later when events are bound
|
||||
this.events = null;
|
||||
// updated later when text nodes are bound
|
||||
@ -25,5 +38,7 @@ export class ElementBinder {
|
||||
this.hasElementPropertyBindings = false;
|
||||
// updated later, so we are able to resolve cycles
|
||||
this.nestedProtoView = null;
|
||||
// updated later in the compilation pipeline
|
||||
this.contentTagSelector = null;
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import {Injector, Key, Dependency, bind, Binding, NoProviderError, ProviderError
|
||||
import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility';
|
||||
import {EventEmitter, PropertySetter} from 'angular2/src/core/annotations/di';
|
||||
import * as viewModule from 'angular2/src/core/compiler/view';
|
||||
import {LightDom, DestinationLightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom';
|
||||
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
||||
import {NgElement} from 'angular2/src/core/dom/element';
|
||||
import {Directive, onChange, onDestroy} from 'angular2/src/core/annotations/annotations'
|
||||
@ -24,8 +23,6 @@ class StaticKeys {
|
||||
viewId:number;
|
||||
ngElementId:number;
|
||||
viewContainerId:number;
|
||||
destinationLightDomId:number;
|
||||
lightDomId:number;
|
||||
bindingPropagationConfigId:number;
|
||||
|
||||
constructor() {
|
||||
@ -33,8 +30,6 @@ class StaticKeys {
|
||||
this.viewId = Key.get(viewModule.View).id;
|
||||
this.ngElementId = Key.get(NgElement).id;
|
||||
this.viewContainerId = Key.get(ViewContainer).id;
|
||||
this.destinationLightDomId = Key.get(DestinationLightDom).id;
|
||||
this.lightDomId = Key.get(LightDom).id;
|
||||
this.bindingPropagationConfigId = Key.get(BindingPropagationConfig).id;
|
||||
}
|
||||
|
||||
@ -166,14 +161,12 @@ export class PreBuiltObjects {
|
||||
view:viewModule.View;
|
||||
element:NgElement;
|
||||
viewContainer:ViewContainer;
|
||||
lightDom:LightDom;
|
||||
bindingPropagationConfig:BindingPropagationConfig;
|
||||
constructor(view, element:NgElement, viewContainer:ViewContainer, lightDom:LightDom,
|
||||
constructor(view, element:NgElement, viewContainer:ViewContainer,
|
||||
bindingPropagationConfig:BindingPropagationConfig) {
|
||||
this.view = view;
|
||||
this.element = element;
|
||||
this.viewContainer = viewContainer;
|
||||
this.lightDom = lightDom;
|
||||
this.bindingPropagationConfig = bindingPropagationConfig;
|
||||
}
|
||||
}
|
||||
@ -577,13 +570,6 @@ export class ElementInjector extends TreeNode {
|
||||
if (keyId === staticKeys.ngElementId) return this._preBuiltObjects.element;
|
||||
if (keyId === staticKeys.viewContainerId) return this._preBuiltObjects.viewContainer;
|
||||
if (keyId === staticKeys.bindingPropagationConfigId) return this._preBuiltObjects.bindingPropagationConfig;
|
||||
if (keyId === staticKeys.destinationLightDomId) {
|
||||
var p:ElementInjector = this.directParent();
|
||||
return isPresent(p) ? p._preBuiltObjects.lightDom : null;
|
||||
}
|
||||
if (keyId === staticKeys.lightDomId) {
|
||||
return this._preBuiltObjects.lightDom;
|
||||
}
|
||||
|
||||
//TODO add other objects as needed
|
||||
return _undefined;
|
||||
|
@ -36,10 +36,12 @@ export class CompileElement {
|
||||
inheritedProtoView:ProtoView;
|
||||
inheritedProtoElementInjector:ProtoElementInjector;
|
||||
inheritedElementBinder:ElementBinder;
|
||||
distanceToParentInjector:number;
|
||||
distanceToParentInjector:int;
|
||||
distanceToParentBinder:int;
|
||||
compileChildren: boolean;
|
||||
ignoreBindings: boolean;
|
||||
elementDescription: string; // e.g. '<div [class]="foo">' : used to provide context in case of error
|
||||
contentTagSelector: string;
|
||||
|
||||
constructor(element, compilationUnit = '') {
|
||||
this.element = element;
|
||||
@ -65,9 +67,11 @@ export class CompileElement {
|
||||
// an own elementBinder
|
||||
this.inheritedElementBinder = null;
|
||||
this.distanceToParentInjector = 0;
|
||||
this.distanceToParentBinder = 0;
|
||||
this.compileChildren = true;
|
||||
// set to true to ignore all the bindings on the element
|
||||
this.ignoreBindings = false;
|
||||
this.contentTagSelector = null;
|
||||
// description is calculated here as compilation steps may change the element
|
||||
var tplDesc = assertionsEnabled()? getElementDescription(element) : null;
|
||||
if (compilationUnit !== '') {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import {ChangeDetection, Parser} from 'angular2/change_detection';
|
||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
|
||||
import {PropertyBindingParser} from './property_binding_parser';
|
||||
import {TextInterpolationParser} from './text_interpolation_parser';
|
||||
@ -32,6 +31,7 @@ export function createDefaultSteps(
|
||||
var steps = [
|
||||
new ViewSplitter(parser),
|
||||
cssProcessor.getCompileStep(compiledComponent, shadowDomStrategy, templateUrl),
|
||||
shadowDomStrategy.getTemplateCompileStep(compiledComponent),
|
||||
new PropertyBindingParser(parser),
|
||||
new DirectiveParser(directives),
|
||||
new TextInterpolationParser(parser),
|
||||
@ -41,10 +41,5 @@ export function createDefaultSteps(
|
||||
new ElementBinderBuilder(parser),
|
||||
];
|
||||
|
||||
var shadowDomStep = shadowDomStrategy.getTemplateCompileStep(compiledComponent);
|
||||
if (isPresent(shadowDomStep)) {
|
||||
ListWrapper.push(steps, shadowDomStep);
|
||||
}
|
||||
|
||||
return steps;
|
||||
}
|
||||
|
@ -49,7 +49,8 @@ export class DirectiveParser extends CompileStep {
|
||||
var classList = current.classList();
|
||||
|
||||
var cssSelector = new CssSelector();
|
||||
cssSelector.setElement(DOM.nodeName(current.element));
|
||||
var nodeName = DOM.nodeName(current.element);
|
||||
cssSelector.setElement(nodeName);
|
||||
for (var i=0; i < classList.length; i++) {
|
||||
cssSelector.addClassName(classList[i]);
|
||||
}
|
||||
|
@ -135,6 +135,11 @@ export class ElementBinderBuilder extends CompileStep {
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
var elementBinder = null;
|
||||
var parentElementBinder = null;
|
||||
var distanceToParentBinder = this._getDistanceToParentBinder(parent, current);
|
||||
if (isPresent(parent)) {
|
||||
parentElementBinder = parent.inheritedElementBinder;
|
||||
}
|
||||
if (current.hasBindings) {
|
||||
var protoView = current.inheritedProtoView;
|
||||
var protoInjectorWasBuilt = isBlank(parent) ? true :
|
||||
@ -143,8 +148,9 @@ export class ElementBinderBuilder extends CompileStep {
|
||||
var currentProtoElementInjector = protoInjectorWasBuilt ?
|
||||
current.inheritedProtoElementInjector : null;
|
||||
|
||||
elementBinder = protoView.bindElement(currentProtoElementInjector,
|
||||
current.componentDirective, current.viewportDirective);
|
||||
elementBinder = protoView.bindElement(parentElementBinder, distanceToParentBinder,
|
||||
currentProtoElementInjector, current.componentDirective, current.viewportDirective);
|
||||
current.distanceToParentBinder = 0;
|
||||
|
||||
if (isPresent(current.textNodeBindings)) {
|
||||
this._bindTextNodes(protoView, current);
|
||||
@ -155,15 +161,23 @@ export class ElementBinderBuilder extends CompileStep {
|
||||
if (isPresent(current.eventBindings)) {
|
||||
this._bindEvents(protoView, current);
|
||||
}
|
||||
if (isPresent(current.contentTagSelector)) {
|
||||
elementBinder.contentTagSelector = current.contentTagSelector;
|
||||
}
|
||||
var directives = current.getAllDirectives();
|
||||
this._bindDirectiveProperties(directives, current);
|
||||
this._bindDirectiveEvents(directives, current);
|
||||
} else if (isPresent(parent)) {
|
||||
elementBinder = parent.inheritedElementBinder;
|
||||
elementBinder = parentElementBinder;
|
||||
current.distanceToParentBinder = distanceToParentBinder;
|
||||
}
|
||||
current.inheritedElementBinder = elementBinder;
|
||||
}
|
||||
|
||||
_getDistanceToParentBinder(parent, current) {
|
||||
return isPresent(parent) ? parent.distanceToParentBinder + 1 : 0;
|
||||
}
|
||||
|
||||
_bindTextNodes(protoView, compileElement) {
|
||||
MapWrapper.forEach(compileElement.textNodeBindings, (expression, indexInParent) => {
|
||||
protoView.bindTextNode(indexInParent, expression);
|
||||
|
@ -37,7 +37,8 @@ export class ElementBindingMarker extends CompileStep {
|
||||
(isPresent(current.eventBindings) && MapWrapper.size(current.eventBindings)>0) ||
|
||||
(isPresent(current.decoratorDirectives) && current.decoratorDirectives.length > 0) ||
|
||||
isPresent(current.viewportDirective) ||
|
||||
isPresent(current.componentDirective);
|
||||
isPresent(current.componentDirective) ||
|
||||
isPresent(current.contentTagSelector);
|
||||
|
||||
if (hasBindings) {
|
||||
var element = current.element;
|
||||
|
@ -1,10 +1,7 @@
|
||||
import {Decorator} from '../../annotations/annotations';
|
||||
import * as ldModule from './light_dom';
|
||||
import {Inject} from 'angular2/di';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {NgElement} from 'angular2/src/core/dom/element';
|
||||
|
||||
class ContentStrategy {
|
||||
nodes:List;
|
||||
@ -17,23 +14,16 @@ class ContentStrategy {
|
||||
* and thus does not affect redistribution.
|
||||
*/
|
||||
class RenderedContent extends ContentStrategy {
|
||||
static _lazyScriptTemplate;
|
||||
beginScript;
|
||||
endScript;
|
||||
|
||||
constructor(contentEl) {
|
||||
super();
|
||||
this._replaceContentElementWithScriptTags(contentEl);
|
||||
this.beginScript = contentEl;
|
||||
this.endScript = DOM.nextSibling(this.beginScript);
|
||||
this.nodes = [];
|
||||
}
|
||||
|
||||
_scriptTemplate() {
|
||||
if (!isPresent(RenderedContent._lazyScriptTemplate)) {
|
||||
RenderedContent._lazyScriptTemplate = DOM.createScriptTag('type', 'ng/content');
|
||||
}
|
||||
return RenderedContent._lazyScriptTemplate;
|
||||
}
|
||||
|
||||
// Inserts the nodes in between the start and end scripts.
|
||||
// Previous content is removed.
|
||||
insert(nodes:List) {
|
||||
@ -42,16 +32,6 @@ class RenderedContent extends ContentStrategy {
|
||||
this._removeNodesUntil(ListWrapper.isEmpty(nodes) ? this.endScript : nodes[0]);
|
||||
}
|
||||
|
||||
// Replaces the content tag with a pair of script tags
|
||||
_replaceContentElementWithScriptTags(contentEl) {
|
||||
this.beginScript = DOM.clone(this._scriptTemplate());
|
||||
this.endScript = DOM.clone(this._scriptTemplate());
|
||||
|
||||
DOM.insertBefore(contentEl, this.beginScript);
|
||||
DOM.insertBefore(contentEl, this.endScript);
|
||||
DOM.removeChild(DOM.parentElement(contentEl), contentEl);
|
||||
}
|
||||
|
||||
_removeNodesUntil(node) {
|
||||
var p = DOM.parentElement(this.beginScript);
|
||||
for (var next = DOM.nextSibling(this.beginScript);
|
||||
@ -83,18 +63,17 @@ class IntermediateContent extends ContentStrategy {
|
||||
}
|
||||
|
||||
|
||||
@Decorator({
|
||||
selector: 'content'
|
||||
})
|
||||
export class Content {
|
||||
select:string;
|
||||
_strategy:ContentStrategy;
|
||||
contentStartElement;
|
||||
|
||||
constructor(@Inject(ldModule.DestinationLightDom) destinationLightDom, contentEl:NgElement) {
|
||||
this.select = contentEl.getAttribute('select');
|
||||
constructor(destinationLightDom:ldModule.LightDom, contentStartEl, selector:string) {
|
||||
this.select = selector;
|
||||
this.contentStartElement = contentStartEl;
|
||||
this._strategy = isPresent(destinationLightDom) ?
|
||||
new IntermediateContent(destinationLightDom) :
|
||||
new RenderedContent(contentEl.domElement);
|
||||
new RenderedContent(contentStartEl);
|
||||
}
|
||||
|
||||
nodes():List {
|
||||
|
@ -3,8 +3,6 @@ import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
|
||||
import * as viewModule from '../view';
|
||||
import {ElementInjector} from '../element_injector';
|
||||
import {ViewContainer} from '../view_container';
|
||||
import {Content} from './content_tag';
|
||||
|
||||
export class DestinationLightDom {}
|
||||
@ -12,11 +10,13 @@ export class DestinationLightDom {}
|
||||
|
||||
class _Root {
|
||||
node;
|
||||
injector:ElementInjector;
|
||||
viewContainer;
|
||||
content;
|
||||
|
||||
constructor(node, injector) {
|
||||
constructor(node, viewContainer, content) {
|
||||
this.node = node;
|
||||
this.injector = injector;
|
||||
this.viewContainer = viewContainer;
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,19 +52,18 @@ export class LightDom {
|
||||
|
||||
// Collects the Content directives from the view and all its child views
|
||||
_collectAllContentTags(view: viewModule.View, acc:List<Content>):List<Content> {
|
||||
var eis = view.elementInjectors;
|
||||
for (var i = 0; i < eis.length; ++i) {
|
||||
var ei = eis[i];
|
||||
if (isBlank(ei)) continue;
|
||||
|
||||
if (ei.hasDirective(Content)) {
|
||||
ListWrapper.push(acc, ei.get(Content));
|
||||
|
||||
} else if (ei.hasPreBuiltObject(ViewContainer)) {
|
||||
var vc = ei.get(ViewContainer);
|
||||
var contentTags = view.contentTags;
|
||||
var vcs = view.viewContainers;
|
||||
for (var i=0; i<vcs.length; i++) {
|
||||
var vc = vcs[i];
|
||||
var contentTag = contentTags[i];
|
||||
if (isPresent(contentTag)) {
|
||||
ListWrapper.push(acc, contentTag);
|
||||
}
|
||||
if (isPresent(vc)) {
|
||||
ListWrapper.forEach(vc.contentTagContainers(), (view) => {
|
||||
this._collectAllContentTags(view, acc);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
@ -76,21 +75,16 @@ export class LightDom {
|
||||
// - plain DOM nodes
|
||||
expandedDomNodes():List {
|
||||
var res = [];
|
||||
|
||||
|
||||
var roots = this._roots();
|
||||
for (var i = 0; i < roots.length; ++i) {
|
||||
|
||||
var root = roots[i];
|
||||
var ei = root.injector;
|
||||
|
||||
if (isPresent(ei) && ei.hasPreBuiltObject(ViewContainer)) {
|
||||
var vc = root.injector.get(ViewContainer);
|
||||
res = ListWrapper.concat(res, vc.nodes());
|
||||
|
||||
} else if (isPresent(ei) && ei.hasDirective(Content)) {
|
||||
var content = root.injector.get(Content);
|
||||
res = ListWrapper.concat(res, content.nodes());
|
||||
|
||||
if (isPresent(root.viewContainer)) {
|
||||
res = ListWrapper.concat(res, root.viewContainer.nodes());
|
||||
} else if (isPresent(root.content)) {
|
||||
res = ListWrapper.concat(res, root.content.nodes());
|
||||
} else {
|
||||
ListWrapper.push(res, root.node);
|
||||
}
|
||||
@ -103,10 +97,24 @@ export class LightDom {
|
||||
_roots() {
|
||||
if (isPresent(this.roots)) return this.roots;
|
||||
|
||||
var viewInj = this.lightDomView.elementInjectors;
|
||||
this.roots = ListWrapper.map(this.nodes, (n) =>
|
||||
new _Root(n, ListWrapper.find(viewInj,
|
||||
(inj) => isPresent(inj) ? inj.forElement(n) : false)));
|
||||
var viewContainers = this.lightDomView.viewContainers;
|
||||
var contentTags = this.lightDomView.contentTags;
|
||||
|
||||
this.roots = ListWrapper.map(this.nodes, (n) => {
|
||||
var foundVc = null;
|
||||
var foundContentTag = null;
|
||||
for (var i=0; i<viewContainers.length; i++) {
|
||||
var vc = viewContainers[i];
|
||||
var contentTag = contentTags[i];
|
||||
if (isPresent(vc) && vc.templateElement === n) {
|
||||
foundVc = vc;
|
||||
}
|
||||
if (isPresent(contentTag) && contentTag.contentStartElement === n) {
|
||||
foundContentTag = contentTag;
|
||||
}
|
||||
}
|
||||
return new _Root(n, foundVc, foundContentTag);
|
||||
});
|
||||
|
||||
return this.roots;
|
||||
}
|
||||
@ -119,10 +127,10 @@ function redistributeNodes(contents:List<Content>, nodes:List) {
|
||||
var select = content.select;
|
||||
var matchSelector = (n) => DOM.elementMatches(n, select);
|
||||
|
||||
if (isBlank(select)) {
|
||||
// Empty selector is identical to <content/>
|
||||
if (select.length === 0) {
|
||||
content.insert(nodes);
|
||||
ListWrapper.clear(nodes);
|
||||
|
||||
} else {
|
||||
var matchingNodes = ListWrapper.filter(nodes, matchSelector);
|
||||
content.insert(matchingNodes);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {Type, isBlank, isPresent, int} from 'angular2/src/facade/lang';
|
||||
import {Type, isBlank, isPresent, int, StringWrapper, assertionsEnabled} from 'angular2/src/facade/lang';
|
||||
import {List, ListWrapper, MapWrapper, Map} from 'angular2/src/facade/collection';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
@ -6,7 +6,6 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import * as viewModule from './view';
|
||||
|
||||
import {Content} from './shadow_dom_emulation/content_tag';
|
||||
import {LightDom} from './shadow_dom_emulation/light_dom';
|
||||
import {ShadowCss} from './shadow_dom_emulation/shadow_css';
|
||||
|
||||
@ -19,20 +18,30 @@ import * as NS from './pipeline/compile_step';
|
||||
import {CompileElement} from './pipeline/compile_element';
|
||||
import {CompileControl} from './pipeline/compile_control';
|
||||
|
||||
var _EMPTY_STEP;
|
||||
|
||||
// Note: fill _EMPTY_STEP to prevent
|
||||
// problems from cyclic dependencies
|
||||
function _emptyStep() {
|
||||
if (isBlank(_EMPTY_STEP)) {
|
||||
_EMPTY_STEP = new _EmptyCompileStep();
|
||||
}
|
||||
return _EMPTY_STEP;
|
||||
}
|
||||
|
||||
export class ShadowDomStrategy {
|
||||
attachTemplate(el, view:viewModule.View) {}
|
||||
constructLightDom(lightDomView:viewModule.View, shadowDomView:viewModule.View, el): LightDom { return null; }
|
||||
polyfillDirectives():List<Type> { return []; }
|
||||
|
||||
/**
|
||||
* An optional step that can modify the template style elements.
|
||||
*
|
||||
* @param {DirectiveMetadata} cmpMetadata
|
||||
* @param {string} templateUrl the template base URL
|
||||
* @returns {CompileStep} a compile step to append to the compiler pipeline, null if not required.
|
||||
* @returns {CompileStep} a compile step to append to the compiler pipeline
|
||||
*/
|
||||
getStyleCompileStep(cmpMetadata: DirectiveMetadata, templateUrl: string): NS.CompileStep {
|
||||
return null;
|
||||
return _emptyStep();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -41,9 +50,9 @@ export class ShadowDomStrategy {
|
||||
* This step could be used to modify the template in order to scope the styles.
|
||||
*
|
||||
* @param {DirectiveMetadata} cmpMetadata
|
||||
* @returns {CompileStep} a compile step to append to the compiler pipeline, null if not required.
|
||||
* @returns {CompileStep} a compile step to append to the compiler pipeline
|
||||
*/
|
||||
getTemplateCompileStep(cmpMetadata: DirectiveMetadata): NS.CompileStep { return null; }
|
||||
getTemplateCompileStep(cmpMetadata: DirectiveMetadata): NS.CompileStep { return _emptyStep(); }
|
||||
|
||||
/**
|
||||
* The application element does not go through the compiler pipeline.
|
||||
@ -87,14 +96,14 @@ export class EmulatedUnscopedShadowDomStrategy extends ShadowDomStrategy {
|
||||
return new LightDom(lightDomView, shadowDomView, el);
|
||||
}
|
||||
|
||||
polyfillDirectives():List<Type> {
|
||||
return [Content];
|
||||
}
|
||||
|
||||
getStyleCompileStep(cmpMetadata: DirectiveMetadata, templateUrl: string): NS.CompileStep {
|
||||
return new _EmulatedUnscopedCssStep(cmpMetadata, templateUrl, this._styleUrlResolver,
|
||||
this._styleHost);
|
||||
}
|
||||
|
||||
getTemplateCompileStep(cmpMetadata: DirectiveMetadata): NS.CompileStep {
|
||||
return new _BaseEmulatedShadowDomStep();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -156,7 +165,39 @@ export class NativeShadowDomStrategy extends ShadowDomStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
class _ShimShadowDomStep extends NS.CompileStep {
|
||||
class _BaseEmulatedShadowDomStep extends NS.CompileStep {
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
if (current.ignoreBindings) {
|
||||
return;
|
||||
}
|
||||
var nodeName = DOM.nodeName(current.element);
|
||||
if (StringWrapper.equals(nodeName.toUpperCase(), 'CONTENT')) {
|
||||
var attrs = current.attrs();
|
||||
var selector = MapWrapper.get(attrs, 'select');
|
||||
current.contentTagSelector = isPresent(selector) ? selector : '';
|
||||
|
||||
var contentStart = DOM.createScriptTag('type', 'ng/contentStart');
|
||||
if (assertionsEnabled()) {
|
||||
DOM.setAttribute(contentStart, 'select', current.contentTagSelector);
|
||||
}
|
||||
var contentEnd = DOM.createScriptTag('type', 'ng/contentEnd');
|
||||
DOM.insertBefore(current.element, contentStart);
|
||||
DOM.insertBefore(current.element, contentEnd);
|
||||
DOM.remove(current.element);
|
||||
|
||||
current.element = contentStart;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class _EmptyCompileStep extends NS.CompileStep {
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class _ShimShadowDomStep extends _BaseEmulatedShadowDomStep {
|
||||
_contentAttribute: string;
|
||||
|
||||
constructor(cmpMetadata: DirectiveMetadata) {
|
||||
@ -167,6 +208,7 @@ class _ShimShadowDomStep extends NS.CompileStep {
|
||||
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
super.process(parent, current, control);
|
||||
if (current.ignoreBindings) {
|
||||
return;
|
||||
}
|
||||
|
73
modules/angular2/src/core/compiler/view.js
vendored
73
modules/angular2/src/core/compiler/view.js
vendored
@ -13,7 +13,8 @@ import {IMPLEMENTS, int, isPresent, isBlank, BaseException} from 'angular2/src/f
|
||||
import {Injector} from 'angular2/di';
|
||||
import {NgElement} from 'angular2/src/core/dom/element';
|
||||
import {ViewContainer} from './view_container';
|
||||
import {LightDom, DestinationLightDom} from './shadow_dom_emulation/light_dom';
|
||||
import {LightDom} from './shadow_dom_emulation/light_dom';
|
||||
import {Content} from './shadow_dom_emulation/content_tag';
|
||||
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
||||
import {ViewPool} from './view_pool';
|
||||
import {EventManager} from 'angular2/src/core/events/event_manager';
|
||||
@ -41,7 +42,9 @@ export class View {
|
||||
nodes:List;
|
||||
componentChildViews: List<View>;
|
||||
viewContainers: List<ViewContainer>;
|
||||
contentTags: List<Content>;
|
||||
preBuiltObjects: List<PreBuiltObjects>;
|
||||
lightDoms: List<LightDom>;
|
||||
proto: ProtoView;
|
||||
context: any;
|
||||
contextWithLocals:ContextWithVariableBindings;
|
||||
@ -56,7 +59,9 @@ export class View {
|
||||
this.bindElements = null;
|
||||
this.componentChildViews = null;
|
||||
this.viewContainers = null;
|
||||
this.contentTags = null;
|
||||
this.preBuiltObjects = null;
|
||||
this.lightDoms = null;
|
||||
this.context = null;
|
||||
this.contextWithLocals = (MapWrapper.size(protoContextLocals) > 0)
|
||||
? new ContextWithVariableBindings(null, MapWrapper.clone(protoContextLocals))
|
||||
@ -64,15 +69,17 @@ export class View {
|
||||
}
|
||||
|
||||
init(changeDetector:ChangeDetector, elementInjectors:List, rootElementInjectors:List, textNodes: List, bindElements:List,
|
||||
viewContainers:List, preBuiltObjects:List, componentChildViews:List) {
|
||||
viewContainers:List, contentTags:List, preBuiltObjects:List, componentChildViews:List, lightDoms:List<LightDom>) {
|
||||
this.changeDetector = changeDetector;
|
||||
this.elementInjectors = elementInjectors;
|
||||
this.rootElementInjectors = rootElementInjectors;
|
||||
this.textNodes = textNodes;
|
||||
this.bindElements = bindElements;
|
||||
this.viewContainers = viewContainers;
|
||||
this.contentTags = contentTags;
|
||||
this.preBuiltObjects = preBuiltObjects;
|
||||
this.componentChildViews = componentChildViews;
|
||||
this.lightDoms = lightDoms;
|
||||
}
|
||||
|
||||
setLocal(contextName: string, value) {
|
||||
@ -125,14 +132,17 @@ export class View {
|
||||
* A call to hydrate/dehydrate does not attach/detach the view from the view
|
||||
* tree.
|
||||
*/
|
||||
hydrate(appInjector: Injector, hostElementInjector: ElementInjector,
|
||||
hydrate(appInjector: Injector, hostElementInjector: ElementInjector, hostLightDom: LightDom,
|
||||
context: Object) {
|
||||
if (this.hydrated()) throw new BaseException('The view is already hydrated.');
|
||||
this._hydrateContext(context);
|
||||
|
||||
// viewContainers
|
||||
for (var i = 0; i < this.viewContainers.length; i++) {
|
||||
this.viewContainers[i].hydrate(appInjector, hostElementInjector);
|
||||
var vc = this.viewContainers[i];
|
||||
if (isPresent(vc)) {
|
||||
vc.hydrate(appInjector, hostElementInjector, hostLightDom);
|
||||
}
|
||||
}
|
||||
|
||||
var binders = this.proto.elementBinders;
|
||||
@ -171,18 +181,14 @@ export class View {
|
||||
|
||||
if (isPresent(componentDirective)) {
|
||||
this.componentChildViews[componentChildViewIndex++].hydrate(shadowDomAppInjector,
|
||||
elementInjector, elementInjector.getComponent());
|
||||
elementInjector, this.lightDoms[i], elementInjector.getComponent());
|
||||
}
|
||||
}
|
||||
|
||||
// this should be moved into DOM write queue
|
||||
for (var i = 0; i < binders.length; ++i) {
|
||||
var componentDirective = binders[i].componentDirective;
|
||||
if (isPresent(componentDirective)) {
|
||||
var lightDom = this.preBuiltObjects[i].lightDom;
|
||||
if (isPresent(lightDom)) {
|
||||
lightDom.redistribute();
|
||||
}
|
||||
for (var i = 0; i < this.lightDoms.length; ++i) {
|
||||
var lightDom = this.lightDoms[i];
|
||||
if (isPresent(lightDom)) {
|
||||
lightDom.redistribute();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -205,7 +211,10 @@ export class View {
|
||||
// viewContainers
|
||||
if (isPresent(this.viewContainers)) {
|
||||
for (var i = 0; i < this.viewContainers.length; i++) {
|
||||
this.viewContainers[i].dehydrate();
|
||||
var vc = this.viewContainers[i];
|
||||
if (isPresent(vc)) {
|
||||
vc.dehydrate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,8 +379,10 @@ export class ProtoView {
|
||||
var textNodes = [];
|
||||
var elementsWithPropertyBindings = [];
|
||||
var preBuiltObjects = ListWrapper.createFixedSize(binders.length);
|
||||
var viewContainers = [];
|
||||
var viewContainers = ListWrapper.createFixedSize(binders.length);
|
||||
var contentTags = ListWrapper.createFixedSize(binders.length);
|
||||
var componentChildViews = [];
|
||||
var lightDoms = ListWrapper.createFixedSize(binders.length);
|
||||
|
||||
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
|
||||
var binder = binders[binderIdx];
|
||||
@ -427,20 +438,32 @@ export class ProtoView {
|
||||
|
||||
ListWrapper.push(componentChildViews, childView);
|
||||
}
|
||||
lightDoms[binderIdx] = lightDom;
|
||||
|
||||
var destLightDom = null;
|
||||
if (isPresent(binder.parent) && binder.distanceToParent === 1) {
|
||||
destLightDom = lightDoms[binder.parent.index];
|
||||
}
|
||||
|
||||
// viewContainers
|
||||
var viewContainer = null;
|
||||
if (isPresent(binder.viewportDirective)) {
|
||||
var destLightDom = this._directParentElementLightDom(protoElementInjector, preBuiltObjects);
|
||||
viewContainer = new ViewContainer(view, element, binder.nestedProtoView, elementInjector,
|
||||
eventManager, destLightDom);
|
||||
ListWrapper.push(viewContainers, viewContainer);
|
||||
}
|
||||
viewContainers[binderIdx] = viewContainer;
|
||||
|
||||
// contentTags
|
||||
var contentTag = null;
|
||||
if (isPresent(binder.contentTagSelector)) {
|
||||
contentTag = new Content(destLightDom, element, binder.contentTagSelector);
|
||||
}
|
||||
contentTags[binderIdx] = contentTag;
|
||||
|
||||
// preBuiltObjects
|
||||
if (isPresent(elementInjector)) {
|
||||
preBuiltObjects[binderIdx] = new PreBuiltObjects(view, new NgElement(element), viewContainer,
|
||||
lightDom, bindingPropagationConfig);
|
||||
bindingPropagationConfig);
|
||||
}
|
||||
|
||||
// events
|
||||
@ -460,7 +483,7 @@ export class ProtoView {
|
||||
this.eventHandlers = eventHandlers;
|
||||
|
||||
view.init(changeDetector, elementInjectors, rootElementInjectors, textNodes, elementsWithPropertyBindings,
|
||||
viewContainers, preBuiltObjects, componentChildViews);
|
||||
viewContainers, contentTags, preBuiltObjects, componentChildViews, lightDoms);
|
||||
|
||||
return view;
|
||||
}
|
||||
@ -497,19 +520,15 @@ export class ProtoView {
|
||||
}
|
||||
}
|
||||
|
||||
_directParentElementLightDom(protoElementInjector:ProtoElementInjector, preBuiltObjects:List):LightDom {
|
||||
var p = protoElementInjector.directParent();
|
||||
return isPresent(p) ? preBuiltObjects[p.index].lightDom : null;
|
||||
}
|
||||
|
||||
bindVariable(contextName:string, templateName:string) {
|
||||
MapWrapper.set(this.variableBindings, contextName, templateName);
|
||||
MapWrapper.set(this.protoContextLocals, templateName, null);
|
||||
}
|
||||
|
||||
bindElement(protoElementInjector:ProtoElementInjector,
|
||||
bindElement(parent:ElementBinder, distanceToParent:int, protoElementInjector:ProtoElementInjector,
|
||||
componentDirective:DirectiveMetadata = null, viewportDirective:DirectiveMetadata = null):ElementBinder {
|
||||
var elBinder = new ElementBinder(protoElementInjector, componentDirective, viewportDirective);
|
||||
var elBinder = new ElementBinder(this.elementBinders.length, parent, distanceToParent,
|
||||
protoElementInjector, componentDirective, viewportDirective);
|
||||
ListWrapper.push(this.elementBinders, elBinder);
|
||||
return elBinder;
|
||||
}
|
||||
@ -601,7 +620,7 @@ export class ProtoView {
|
||||
var cmpType = rootComponentAnnotatedType.type;
|
||||
var rootProtoView = new ProtoView(insertionElement, protoChangeDetector, shadowDomStrategy);
|
||||
rootProtoView.instantiateInPlace = true;
|
||||
var binder = rootProtoView.bindElement(
|
||||
var binder = rootProtoView.bindElement(null, 0,
|
||||
new ProtoElementInjector(null, 0, [cmpType], true));
|
||||
binder.componentDirective = rootComponentAnnotatedType;
|
||||
binder.nestedProtoView = protoView;
|
||||
|
@ -40,10 +40,10 @@ export class ViewContainer {
|
||||
this._eventManager = eventManager;
|
||||
}
|
||||
|
||||
hydrate(appInjector: Injector, hostElementInjector: eiModule.ElementInjector) {
|
||||
hydrate(appInjector: Injector, hostElementInjector: eiModule.ElementInjector, hostLightDom: ldModule.LightDom) {
|
||||
this.appInjector = appInjector;
|
||||
this.hostElementInjector = hostElementInjector;
|
||||
this.hostLightDom = isPresent(hostElementInjector) ? hostElementInjector.get(ldModule.LightDom) : null;
|
||||
this.hostLightDom = hostLightDom;
|
||||
}
|
||||
|
||||
dehydrate() {
|
||||
@ -85,7 +85,7 @@ export class ViewContainer {
|
||||
var newView = this.defaultProtoView.instantiate(this.hostElementInjector, this._eventManager);
|
||||
// insertion must come before hydration so that element injector trees are attached.
|
||||
this.insert(newView, atIndex);
|
||||
newView.hydrate(this.appInjector, this.hostElementInjector, this.parentView.context);
|
||||
newView.hydrate(this.appInjector, this.hostElementInjector, this.hostLightDom, this.parentView.context);
|
||||
|
||||
// new content tags might have appeared, we need to redistrubute.
|
||||
if (isPresent(this.hostLightDom)) {
|
||||
|
Reference in New Issue
Block a user