refactor(render): create and store render ProtoViewRef in every app ProtoView
Needed to change Renderer.mergeChildComponentProtoViews to not create new ProtoViews to be able to deal with cyclic references. This commit is part of using the new render layer in Angular.
This commit is contained in:
12
modules/angular2/src/core/application.js
vendored
12
modules/angular2/src/core/application.js
vendored
@ -3,7 +3,6 @@ import {Type, isBlank, isPresent, BaseException, assertionsEnabled, print, strin
|
||||
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {Compiler, CompilerCache} from './compiler/compiler';
|
||||
import {ProtoView} from './compiler/view';
|
||||
import {Reflector, reflector} from 'angular2/src/reflection/reflection';
|
||||
import {Parser, Lexer, ChangeDetection, dynamicChangeDetection, jitChangeDetection} from 'angular2/change_detection';
|
||||
import {ExceptionHandler} from './exception_handler';
|
||||
@ -72,12 +71,11 @@ function _injectorBindings(appComponentType): List<Binding> {
|
||||
throw new BaseException(`Only Components can be bootstrapped; ` +
|
||||
`Directive of ${stringify(type)} is not a Component`);
|
||||
}
|
||||
return compiler.compile(appComponentAnnotatedType.type).then(
|
||||
(protoView) => {
|
||||
var appProtoView = ProtoView.createRootProtoView(protoView, appElement,
|
||||
DirectiveBinding.createFromType(appComponentAnnotatedType.type, appComponentAnnotatedType.annotation),
|
||||
changeDetection.createProtoChangeDetector('root'),
|
||||
strategy);
|
||||
return compiler.compileRoot(
|
||||
appElement,
|
||||
DirectiveBinding.createFromType(appComponentAnnotatedType.type, appComponentAnnotatedType.annotation)
|
||||
).then(
|
||||
(appProtoView) => {
|
||||
// The light Dom of the app element is not considered part of
|
||||
// the angular application. Thus the context and lightDomInjector are
|
||||
// empty.
|
||||
|
124
modules/angular2/src/core/compiler/compiler.js
vendored
124
modules/angular2/src/core/compiler/compiler.js
vendored
@ -85,6 +85,17 @@ export class NewCompiler {
|
||||
return DirectiveBinding.createFromType(meta.type, meta.annotation);
|
||||
}
|
||||
|
||||
// Create a rootView as if the compiler encountered <rootcmp></rootcmp>.
|
||||
// Used for bootstrapping.
|
||||
compileRoot(elementOrSelector, componentBinding:DirectiveBinding):Promise<ProtoView> {
|
||||
return this._renderer.createRootProtoView(elementOrSelector, 'root').then( (rootRenderPv) => {
|
||||
return this._compileNestedProtoViews(null, rootRenderPv, [componentBinding], true)
|
||||
}).then( (rootProtoView) => {
|
||||
rootProtoView.instantiateInPlace = true;
|
||||
return rootProtoView;
|
||||
});
|
||||
}
|
||||
|
||||
compile(component: Type):Promise<ProtoView> {
|
||||
var protoView = this._compile(this._bindDirective(component));
|
||||
return PromiseWrapper.isPromise(protoView) ? protoView : PromiseWrapper.resolve(protoView);
|
||||
@ -96,7 +107,8 @@ export class NewCompiler {
|
||||
var protoView = this._compilerCache.get(component);
|
||||
if (isPresent(protoView)) {
|
||||
// The component has already been compiled into a ProtoView,
|
||||
// returns a resolved Promise.
|
||||
// returns a plain ProtoView, not wrapped inside of a Promise.
|
||||
// Needed for recursive components.
|
||||
return protoView;
|
||||
}
|
||||
|
||||
@ -113,31 +125,72 @@ export class NewCompiler {
|
||||
this._flattenDirectives(template),
|
||||
(directive) => this._bindDirective(directive)
|
||||
);
|
||||
|
||||
pvPromise = this._compileNoRecurse(componentBinding, template, directives).then( (protoView) => {
|
||||
// Populate the cache before compiling the nested components,
|
||||
// so that components can reference themselves in their template.
|
||||
this._compilerCache.set(component, protoView);
|
||||
MapWrapper.delete(this._compiling, component);
|
||||
|
||||
// Compile all the components from the template
|
||||
var nestedPVPromises = this._compileNestedComponents(protoView);
|
||||
if (nestedPVPromises.length > 0) {
|
||||
// Returns ProtoView Promise when there are any asynchronous nested ProtoViews.
|
||||
// The promise will resolved after nested ProtoViews are compiled.
|
||||
return PromiseWrapper.then(PromiseWrapper.all(nestedPVPromises),
|
||||
(_) => protoView,
|
||||
(e) => { throw new BaseException(`${e} -> Failed to compile ${stringify(component)}`); }
|
||||
);
|
||||
}
|
||||
return protoView;
|
||||
var renderTemplate = this._buildRenderTemplate(component, template, directives);
|
||||
pvPromise = this._renderer.compile(renderTemplate).then( (renderPv) => {
|
||||
return this._compileNestedProtoViews(componentBinding, renderPv, directives, true);
|
||||
});
|
||||
|
||||
MapWrapper.set(this._compiling, component, pvPromise);
|
||||
return pvPromise;
|
||||
}
|
||||
|
||||
_compileNoRecurse(componentBinding, template, directives):Promise<ProtoView> {
|
||||
var component = componentBinding.key.token;
|
||||
// TODO(tbosch): union type return ProtoView or Promise<ProtoView>
|
||||
_compileNestedProtoViews(componentBinding, renderPv, directives, isComponentRootView) {
|
||||
var nestedPVPromises = [];
|
||||
var protoView = this._protoViewFactory.createProtoView(componentBinding, renderPv, directives);
|
||||
if (isComponentRootView && isPresent(componentBinding)) {
|
||||
// Populate the cache before compiling the nested components,
|
||||
// so that components can reference themselves in their template.
|
||||
var component = componentBinding.key.token;
|
||||
this._compilerCache.set(component, protoView);
|
||||
MapWrapper.delete(this._compiling, component);
|
||||
}
|
||||
|
||||
var binderIndex = 0;
|
||||
ListWrapper.forEach(protoView.elementBinders, (elementBinder) => {
|
||||
var nestedComponent = elementBinder.componentDirective;
|
||||
var nestedRenderProtoView = renderPv.elementBinders[binderIndex].nestedProtoView;
|
||||
var elementBinderDone = (nestedPv) => {
|
||||
elementBinder.nestedProtoView = nestedPv;
|
||||
// Can't set the parentProtoView for components,
|
||||
// as their ProtoView might be used in multiple other components.
|
||||
nestedPv.parentProtoView = isPresent(nestedComponent) ? null : protoView;
|
||||
};
|
||||
var nestedCall = null;
|
||||
if (isPresent(nestedComponent)) {
|
||||
if (!(nestedComponent.annotation instanceof DynamicComponent)) {
|
||||
nestedCall = this._compile(nestedComponent);
|
||||
}
|
||||
} else if (isPresent(nestedRenderProtoView)) {
|
||||
nestedCall = this._compileNestedProtoViews(componentBinding, nestedRenderProtoView, directives, false);
|
||||
}
|
||||
if (PromiseWrapper.isPromise(nestedCall)) {
|
||||
ListWrapper.push(nestedPVPromises, nestedCall.then(elementBinderDone));
|
||||
} else if (isPresent(nestedCall)) {
|
||||
elementBinderDone(nestedCall);
|
||||
}
|
||||
binderIndex++;
|
||||
});
|
||||
|
||||
var protoViewDone = (_) => {
|
||||
var childComponentRenderPvRefs = [];
|
||||
ListWrapper.forEach(protoView.elementBinders, (eb) => {
|
||||
if (isPresent(eb.componentDirective)) {
|
||||
var componentPv = eb.nestedProtoView;
|
||||
ListWrapper.push(childComponentRenderPvRefs, isPresent(componentPv) ? componentPv.render : null);
|
||||
}
|
||||
});
|
||||
this._renderer.mergeChildComponentProtoViews(protoView.render, childComponentRenderPvRefs);
|
||||
return protoView;
|
||||
};
|
||||
if (nestedPVPromises.length > 0) {
|
||||
return PromiseWrapper.all(nestedPVPromises).then(protoViewDone);
|
||||
} else {
|
||||
return protoViewDone(null);
|
||||
}
|
||||
}
|
||||
|
||||
_buildRenderTemplate(component, template, directives) {
|
||||
var componentUrl = this._urlResolver.resolve(
|
||||
this._appUrl, this._componentUrlMapper.getUrl(component)
|
||||
);
|
||||
@ -150,37 +203,12 @@ export class NewCompiler {
|
||||
// is able to resolve urls in stylesheets.
|
||||
templateAbsUrl = componentUrl;
|
||||
}
|
||||
var renderTemplate = new renderApi.Template({
|
||||
return new renderApi.Template({
|
||||
componentId: stringify(component),
|
||||
absUrl: templateAbsUrl,
|
||||
inline: template.inline,
|
||||
directives: ListWrapper.map(directives, this._buildRenderDirective)
|
||||
});
|
||||
return this._renderer.compile(renderTemplate).then( (renderPv) => {
|
||||
return this._protoViewFactory.createProtoView(componentBinding.annotation, renderPv, directives);
|
||||
});
|
||||
}
|
||||
|
||||
_compileNestedComponents(protoView, nestedPVPromises = null):List<Promise> {
|
||||
if (isBlank(nestedPVPromises)) {
|
||||
nestedPVPromises = [];
|
||||
}
|
||||
ListWrapper.map(protoView.elementBinders, (elementBinder) => {
|
||||
var nestedComponent = elementBinder.componentDirective;
|
||||
if (isPresent(nestedComponent) && !(nestedComponent.annotation instanceof DynamicComponent)) {
|
||||
var nestedCall = this._compile(nestedComponent);
|
||||
if (PromiseWrapper.isPromise(nestedCall)) {
|
||||
ListWrapper.push(nestedPVPromises, nestedCall.then( (nestedPv) => {
|
||||
elementBinder.nestedProtoView = nestedPv;
|
||||
}));
|
||||
} else {
|
||||
elementBinder.nestedProtoView = nestedCall;
|
||||
}
|
||||
} else if (isPresent(elementBinder.nestedProtoView)) {
|
||||
this._compileNestedComponents(elementBinder.nestedProtoView, nestedPVPromises);
|
||||
}
|
||||
});
|
||||
return nestedPVPromises;
|
||||
}
|
||||
|
||||
_buildRenderDirective(directiveBinding) {
|
||||
@ -269,7 +297,7 @@ export class Compiler extends NewCompiler {
|
||||
new DefaultStepFactory(parser, shadowDomStrategy.render),
|
||||
templateLoader
|
||||
),
|
||||
null, null
|
||||
null, shadowDomStrategy.render
|
||||
),
|
||||
new ProtoViewFactory(changeDetection, shadowDomStrategy)
|
||||
);
|
||||
|
@ -20,16 +20,19 @@ export class ProtoViewFactory {
|
||||
this._shadowDomStrategy = shadowDomStrategy;
|
||||
}
|
||||
|
||||
createProtoView(componentAnnotation:Component, renderProtoView: renderApi.ProtoView, directives:List<DirectiveBinding>):ProtoView {
|
||||
return this._createProtoView(null, componentAnnotation, renderProtoView, directives);
|
||||
}
|
||||
|
||||
_createProtoView(parent:ProtoView, componentAnnotation:Component, renderProtoView: renderApi.ProtoView, directives:List<DirectiveBinding>):ProtoView {
|
||||
var protoChangeDetector = this._changeDetection.createProtoChangeDetector('dummy',
|
||||
componentAnnotation.changeDetection);
|
||||
createProtoView(componentBinding:DirectiveBinding, renderProtoView: renderApi.ProtoView, directives:List<DirectiveBinding>):ProtoView {
|
||||
var protoChangeDetector;
|
||||
if (isBlank(componentBinding)) {
|
||||
protoChangeDetector = this._changeDetection.createProtoChangeDetector('root', null);
|
||||
} else {
|
||||
var componentAnnotation:Component = componentBinding.annotation;
|
||||
protoChangeDetector = this._changeDetection.createProtoChangeDetector(
|
||||
'dummy', componentAnnotation.changeDetection
|
||||
);
|
||||
}
|
||||
var domProtoView = this._getDomProtoView(renderProtoView.render);
|
||||
var protoView = new ProtoView(domProtoView.element, protoChangeDetector,
|
||||
this._shadowDomStrategy, parent);
|
||||
var protoView = new ProtoView(renderProtoView.render, domProtoView.element, protoChangeDetector,
|
||||
this._shadowDomStrategy, null);
|
||||
|
||||
for (var i=0; i<renderProtoView.elementBinders.length; i++) {
|
||||
var renderElementBinder = renderProtoView.elementBinders[i];
|
||||
@ -42,13 +45,10 @@ export class ProtoViewFactory {
|
||||
i, parentPeiWithDistance,
|
||||
sortedDirectives, renderElementBinder
|
||||
);
|
||||
var elementBinder = this._createElementBinder(
|
||||
this._createElementBinder(
|
||||
protoView, renderElementBinder, domElementBinder, protoElementInjector, sortedDirectives
|
||||
);
|
||||
this._createDirectiveBinders(protoView, sortedDirectives);
|
||||
if (isPresent(renderElementBinder.nestedProtoView)) {
|
||||
elementBinder.nestedProtoView = this._createProtoView(protoView, componentAnnotation, renderElementBinder.nestedProtoView, directives);
|
||||
}
|
||||
}
|
||||
MapWrapper.forEach(renderProtoView.variableBindings, (mappedName, varName) => {
|
||||
protoView.bindVariable(varName, mappedName);
|
||||
|
26
modules/angular2/src/core/compiler/view.js
vendored
26
modules/angular2/src/core/compiler/view.js
vendored
@ -16,6 +16,7 @@ import {Content} from './shadow_dom_emulation/content_tag';
|
||||
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
||||
import {ViewPool} from './view_pool';
|
||||
import {EventManager} from 'angular2/src/render/dom/events/event_manager';
|
||||
import * as renderApi from 'angular2/src/render/api';
|
||||
|
||||
const NG_BINDING_CLASS = 'ng-binding';
|
||||
const NG_BINDING_CLASS_SELECTOR = '.ng-binding';
|
||||
@ -283,11 +284,14 @@ export class ProtoView {
|
||||
|
||||
_directiveMementosMap:Map;
|
||||
_directiveMementos:List;
|
||||
render:renderApi.ProtoViewRef;
|
||||
|
||||
constructor(
|
||||
render:renderApi.ProtoViewRef,
|
||||
template,
|
||||
protoChangeDetector:ProtoChangeDetector,
|
||||
shadowDomStrategy:ShadowDomStrategy, parentProtoView:ProtoView = null) {
|
||||
this.render = render;
|
||||
this.element = template;
|
||||
this.elementBinders = [];
|
||||
this.variableBindings = MapWrapper.create();
|
||||
@ -642,28 +646,6 @@ export class ProtoView {
|
||||
|
||||
return MapWrapper.get(this._directiveMementosMap, id);
|
||||
}
|
||||
|
||||
// Create a rootView as if the compiler encountered <rootcmp></rootcmp>,
|
||||
// and the component template is already compiled into protoView.
|
||||
// Used for bootstrapping.
|
||||
static createRootProtoView(protoView: ProtoView,
|
||||
insertionElement,
|
||||
rootComponentBinding: DirectiveBinding,
|
||||
protoChangeDetector:ProtoChangeDetector,
|
||||
shadowDomStrategy: ShadowDomStrategy
|
||||
): ProtoView {
|
||||
|
||||
DOM.addClass(insertionElement, NG_BINDING_CLASS);
|
||||
var cmpType = rootComponentBinding.key.token;
|
||||
var rootProtoView = new ProtoView(insertionElement, protoChangeDetector, shadowDomStrategy);
|
||||
rootProtoView.instantiateInPlace = true;
|
||||
var binder = rootProtoView.bindElement(null, 0,
|
||||
new ProtoElementInjector(null, 0, [cmpType], true));
|
||||
binder.componentDirective = rootComponentBinding;
|
||||
binder.nestedProtoView = protoView;
|
||||
shadowDomStrategy.shimAppElement(cmpType, insertionElement);
|
||||
return rootProtoView;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user