refactor(proto_view_factory): expose data for generating change detectors
Also consolidates metadata handling in `ElementInjector` BREAKING CHANGE: - renames `DirectiveMetadataReader` into `DirectiveResolver` and removes `src/core/compiler/directive_metadata`. Fixes #1712 Fixes #1713
This commit is contained in:
+35
-56
@@ -4,9 +4,10 @@ import {Type, isBlank, isPresent, BaseException, normalizeBlank, stringify} from
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {DirectiveMetadataReader} from './directive_metadata_reader';
|
||||
import {Component, Directive} from '../annotations_impl/annotations';
|
||||
import {DirectiveResolver} from './directive_resolver';
|
||||
|
||||
import {AppProtoView} from './view';
|
||||
import {ElementBinder} from './element_binder';
|
||||
import {ProtoViewRef} from './view_ref';
|
||||
import {DirectiveBinding} from './element_injector';
|
||||
import {TemplateResolver} from './template_resolver';
|
||||
@@ -47,7 +48,7 @@ export class CompilerCache {
|
||||
*/
|
||||
@Injectable()
|
||||
export class Compiler {
|
||||
_reader: DirectiveMetadataReader;
|
||||
_reader: DirectiveResolver;
|
||||
_compilerCache:CompilerCache;
|
||||
_compiling:Map<Type, Promise>;
|
||||
_templateResolver: TemplateResolver;
|
||||
@@ -57,7 +58,7 @@ export class Compiler {
|
||||
_render: renderApi.RenderCompiler;
|
||||
_protoViewFactory:ProtoViewFactory;
|
||||
|
||||
constructor(reader: DirectiveMetadataReader,
|
||||
constructor(reader: DirectiveResolver,
|
||||
cache:CompilerCache,
|
||||
templateResolver: TemplateResolver,
|
||||
componentUrlMapper: ComponentUrlMapper,
|
||||
@@ -79,11 +80,11 @@ export class Compiler {
|
||||
if (directiveTypeOrBinding instanceof DirectiveBinding) {
|
||||
return directiveTypeOrBinding;
|
||||
} else if (directiveTypeOrBinding instanceof Binding) {
|
||||
let meta = this._reader.read(directiveTypeOrBinding.token);
|
||||
return DirectiveBinding.createFromBinding(directiveTypeOrBinding, meta.annotation);
|
||||
let annotation = this._reader.resolve(directiveTypeOrBinding.token);
|
||||
return DirectiveBinding.createFromBinding(directiveTypeOrBinding, annotation);
|
||||
} else {
|
||||
let meta = this._reader.read(directiveTypeOrBinding);
|
||||
return DirectiveBinding.createFromType(meta.type, meta.annotation);
|
||||
let annotation = this._reader.resolve(directiveTypeOrBinding);
|
||||
return DirectiveBinding.createFromType(directiveTypeOrBinding, annotation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,9 +94,9 @@ export class Compiler {
|
||||
var componentBinding = this._bindDirective(componentTypeOrBinding);
|
||||
this._assertTypeIsComponent(componentBinding);
|
||||
|
||||
var directiveMetadata = Compiler.buildRenderDirective(componentBinding);
|
||||
var directiveMetadata = componentBinding.metadata;
|
||||
return this._render.compileHost(directiveMetadata).then( (hostRenderPv) => {
|
||||
return this._compileNestedProtoViews(null, null, hostRenderPv, [componentBinding], true);
|
||||
return this._compileNestedProtoViews(componentBinding, hostRenderPv, [componentBinding]);
|
||||
}).then( (appProtoView) => {
|
||||
return new ProtoViewRef(appProtoView);
|
||||
});
|
||||
@@ -139,7 +140,7 @@ export class Compiler {
|
||||
);
|
||||
var renderTemplate = this._buildRenderTemplate(component, template, directives);
|
||||
pvPromise = this._render.compile(renderTemplate).then( (renderPv) => {
|
||||
return this._compileNestedProtoViews(null, componentBinding, renderPv, directives, true);
|
||||
return this._compileNestedProtoViews(componentBinding, renderPv, directives);
|
||||
});
|
||||
|
||||
MapWrapper.set(this._compiling, component, pvPromise);
|
||||
@@ -147,10 +148,12 @@ export class Compiler {
|
||||
}
|
||||
|
||||
// TODO(tbosch): union type return AppProtoView or Promise<AppProtoView>
|
||||
_compileNestedProtoViews(parentProtoView, componentBinding, renderPv, directives, isComponentRootView) {
|
||||
var nestedPVPromises = [];
|
||||
var protoView = this._protoViewFactory.createProtoView(parentProtoView, componentBinding, renderPv, directives);
|
||||
if (isComponentRootView && isPresent(componentBinding)) {
|
||||
_compileNestedProtoViews(componentBinding, renderPv, directives) {
|
||||
var protoViews = this._protoViewFactory.createAppProtoViews(componentBinding, renderPv, directives);
|
||||
var protoView = protoViews[0];
|
||||
// TODO(tbosch): we should be caching host protoViews as well!
|
||||
// -> need a separate cache for this...
|
||||
if (renderPv.type === renderApi.ProtoViewDto.COMPONENT_VIEW_TYPE && isPresent(componentBinding)) {
|
||||
// Populate the cache before compiling the nested components,
|
||||
// so that components can reference themselves in their template.
|
||||
var component = componentBinding.key.token;
|
||||
@@ -158,25 +161,18 @@ export class Compiler {
|
||||
MapWrapper.delete(this._compiling, component);
|
||||
}
|
||||
|
||||
var binderIndex = 0;
|
||||
ListWrapper.forEach(protoView.elementBinders, (elementBinder) => {
|
||||
var nestedPVPromises = [];
|
||||
ListWrapper.forEach(this._collectComponentElementBinders(protoViews), (elementBinder) => {
|
||||
var nestedComponent = elementBinder.componentDirective;
|
||||
var nestedRenderProtoView = renderPv.elementBinders[binderIndex].nestedProtoView;
|
||||
var elementBinderDone = (nestedPv) => {
|
||||
elementBinder.nestedProtoView = nestedPv;
|
||||
};
|
||||
var nestedCall = null;
|
||||
if (isPresent(nestedComponent)) {
|
||||
nestedCall = this._compile(nestedComponent);
|
||||
} else if (isPresent(nestedRenderProtoView)) {
|
||||
nestedCall = this._compileNestedProtoViews(protoView, componentBinding, nestedRenderProtoView, directives, false);
|
||||
}
|
||||
var nestedCall = this._compile(nestedComponent);
|
||||
if (PromiseWrapper.isPromise(nestedCall)) {
|
||||
ListWrapper.push(nestedPVPromises, nestedCall.then(elementBinderDone));
|
||||
} else if (isPresent(nestedCall)) {
|
||||
elementBinderDone(nestedCall);
|
||||
}
|
||||
binderIndex++;
|
||||
});
|
||||
|
||||
var protoViewDone = (_) => {
|
||||
@@ -189,6 +185,18 @@ export class Compiler {
|
||||
}
|
||||
}
|
||||
|
||||
_collectComponentElementBinders(protoViews:List<AppProtoView>):List<ElementBinder> {
|
||||
var componentElementBinders = [];
|
||||
ListWrapper.forEach(protoViews, (protoView) => {
|
||||
ListWrapper.forEach(protoView.elementBinders, (elementBinder) => {
|
||||
if (isPresent(elementBinder.componentDirective)) {
|
||||
ListWrapper.push(componentElementBinders, elementBinder);
|
||||
}
|
||||
});
|
||||
});
|
||||
return componentElementBinders;
|
||||
}
|
||||
|
||||
_buildRenderTemplate(component, view, directives): renderApi.ViewDefinition {
|
||||
var componentUrl = this._urlResolver.resolve(
|
||||
this._appUrl, this._componentUrlMapper.getUrl(component)
|
||||
@@ -206,36 +214,7 @@ export class Compiler {
|
||||
componentId: stringify(component),
|
||||
absUrl: templateAbsUrl,
|
||||
template: view.template,
|
||||
directives: ListWrapper.map(directives, Compiler.buildRenderDirective)
|
||||
});
|
||||
}
|
||||
|
||||
static buildRenderDirective(directiveBinding):renderApi.DirectiveMetadata {
|
||||
var ann = directiveBinding.annotation;
|
||||
var renderType;
|
||||
var compileChildren = ann.compileChildren;
|
||||
if (ann instanceof Component) {
|
||||
renderType = renderApi.DirectiveMetadata.COMPONENT_TYPE;
|
||||
} else {
|
||||
renderType = renderApi.DirectiveMetadata.DIRECTIVE_TYPE;
|
||||
}
|
||||
var readAttributes = [];
|
||||
ListWrapper.forEach(directiveBinding.dependencies, (dep) => {
|
||||
if (isPresent(dep.attributeName)) {
|
||||
ListWrapper.push(readAttributes, dep.attributeName);
|
||||
}
|
||||
});
|
||||
return new renderApi.DirectiveMetadata({
|
||||
id: stringify(directiveBinding.key.token),
|
||||
type: renderType,
|
||||
selector: ann.selector,
|
||||
compileChildren: compileChildren,
|
||||
hostListeners: isPresent(ann.hostListeners) ? MapWrapper.createFromStringMap(ann.hostListeners) : null,
|
||||
hostProperties: isPresent(ann.hostProperties) ? MapWrapper.createFromStringMap(ann.hostProperties) : null,
|
||||
hostAttributes: isPresent(ann.hostAttributes) ? MapWrapper.createFromStringMap(ann.hostAttributes) : null,
|
||||
hostActions: isPresent(ann.hostActions) ? MapWrapper.createFromStringMap(ann.hostActions) : null,
|
||||
properties: isPresent(ann.properties) ? MapWrapper.createFromStringMap(ann.properties) : null,
|
||||
readAttributes: readAttributes
|
||||
directives: ListWrapper.map(directives, directiveBinding => directiveBinding.metadata )
|
||||
});
|
||||
}
|
||||
|
||||
@@ -260,7 +239,7 @@ export class Compiler {
|
||||
}
|
||||
|
||||
_assertTypeIsComponent(directiveBinding:DirectiveBinding):void {
|
||||
if (!(directiveBinding.annotation instanceof Component)) {
|
||||
if (directiveBinding.metadata.type !== renderApi.DirectiveMetadata.COMPONENT_TYPE) {
|
||||
throw new BaseException(`Could not load '${stringify(directiveBinding.key.token)}' because it is not a component.`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import {Type} from 'angular2/src/facade/lang';
|
||||
import {List} from 'angular2/src/facade/collection';
|
||||
import {Directive} from 'angular2/src/core/annotations_impl/annotations'
|
||||
import {ResolvedBinding} from 'angular2/di';
|
||||
|
||||
/**
|
||||
* Combination of a type with the Directive annotation
|
||||
*/
|
||||
export class DirectiveMetadata {
|
||||
type:Type;
|
||||
annotation:Directive;
|
||||
resolvedInjectables:List<ResolvedBinding>;
|
||||
|
||||
constructor(type:Type, annotation:Directive, resolvedInjectables:List<ResolvedBinding>) {
|
||||
this.annotation = annotation;
|
||||
this.type = type;
|
||||
this.resolvedInjectables = resolvedInjectables;
|
||||
}
|
||||
}
|
||||
+4
-10
@@ -1,24 +1,18 @@
|
||||
import {Injector} from 'angular2/di';
|
||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
||||
import {Type, isPresent, BaseException, stringify} from 'angular2/src/facade/lang';
|
||||
import {Directive, Component} from '../annotations_impl/annotations';
|
||||
import {DirectiveMetadata} from './directive_metadata';
|
||||
import {Directive} from '../annotations_impl/annotations';
|
||||
import {reflector} from 'angular2/src/reflection/reflection';
|
||||
|
||||
@Injectable()
|
||||
export class DirectiveMetadataReader {
|
||||
read(type:Type):DirectiveMetadata {
|
||||
export class DirectiveResolver {
|
||||
resolve(type:Type):Directive {
|
||||
var annotations = reflector.annotations(type);
|
||||
if (isPresent(annotations)) {
|
||||
for (var i=0; i<annotations.length; i++) {
|
||||
var annotation = annotations[i];
|
||||
|
||||
if (annotation instanceof Directive) {
|
||||
var resolvedInjectables = null;
|
||||
if (annotation instanceof Component && isPresent(annotation.injectables)) {
|
||||
resolvedInjectables = Injector.resolve(annotation.injectables);
|
||||
}
|
||||
return new DirectiveMetadata(type, annotation, resolvedInjectables);
|
||||
return annotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
+73
-22
@@ -15,6 +15,8 @@ import {Directive, Component, onChange, onDestroy, onAllChangesDone} from 'angul
|
||||
import {ChangeDetector, ChangeDetectorRef} from 'angular2/change_detection';
|
||||
import {QueryList} from './query_list';
|
||||
import {reflector} from 'angular2/src/reflection/reflection';
|
||||
import {DirectiveMetadata} from 'angular2/src/render/api';
|
||||
|
||||
|
||||
var _MAX_DIRECTIVE_CONSTRUCTION_COUNTER = 10;
|
||||
|
||||
@@ -223,54 +225,104 @@ export class DirectiveDependency extends Dependency {
|
||||
}
|
||||
|
||||
export class DirectiveBinding extends ResolvedBinding {
|
||||
callOnDestroy:boolean;
|
||||
callOnChange:boolean;
|
||||
callOnAllChangesDone:boolean;
|
||||
annotation:Directive;
|
||||
resolvedInjectables:List<ResolvedBinding>;
|
||||
metadata: DirectiveMetadata;
|
||||
publishAs: List<Type>;
|
||||
|
||||
constructor(key:Key, factory:Function, dependencies:List, providedAsPromise:boolean, annotation:Directive) {
|
||||
constructor(key:Key, factory:Function, dependencies:List, providedAsPromise:boolean,
|
||||
resolvedInjectables:List<ResolvedBinding>, metadata:DirectiveMetadata, annotation: Directive) {
|
||||
super(key, factory, dependencies, providedAsPromise);
|
||||
this.callOnDestroy = isPresent(annotation) && annotation.hasLifecycleHook(onDestroy);
|
||||
this.callOnChange = isPresent(annotation) && annotation.hasLifecycleHook(onChange);
|
||||
this.callOnAllChangesDone = isPresent(annotation) && annotation.hasLifecycleHook(onAllChangesDone);
|
||||
this.annotation = annotation;
|
||||
if (annotation instanceof Component && isPresent(annotation.injectables)) {
|
||||
this.resolvedInjectables = Injector.resolve(annotation.injectables);
|
||||
this.resolvedInjectables = resolvedInjectables;
|
||||
this.metadata = metadata;
|
||||
if (annotation instanceof Component) {
|
||||
this.publishAs = annotation.publishAs;
|
||||
} else {
|
||||
this.publishAs = null;
|
||||
}
|
||||
}
|
||||
|
||||
get callOnDestroy() {
|
||||
return this.metadata.callOnDestroy;
|
||||
}
|
||||
|
||||
get callOnChange() {
|
||||
return this.metadata.callOnChange;
|
||||
}
|
||||
|
||||
get callOnAllChangesDone() {
|
||||
return this.metadata.callOnAllChangesDone;
|
||||
}
|
||||
|
||||
get displayName() {
|
||||
return this.key.displayName;
|
||||
}
|
||||
|
||||
get eventEmitters():List<string> {
|
||||
return isPresent(this.annotation) && isPresent(this.annotation.events) ? this.annotation.events : [];
|
||||
return isPresent(this.metadata) && isPresent(this.metadata.events) ? this.metadata.events : [];
|
||||
}
|
||||
|
||||
get hostActions() { //StringMap
|
||||
return isPresent(this.annotation) && isPresent(this.annotation.hostActions) ? this.annotation.hostActions : {};
|
||||
get hostActions():Map<string,string> {
|
||||
return isPresent(this.metadata) && isPresent(this.metadata.hostActions) ? this.metadata.hostActions : MapWrapper.create();
|
||||
}
|
||||
|
||||
get changeDetection() {
|
||||
if (this.annotation instanceof Component) {
|
||||
var c:Component = this.annotation;
|
||||
return c.changeDetection;
|
||||
if (isPresent(metadata)) {
|
||||
return metadata.changeDetection;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static createFromBinding(b:Binding, annotation:Directive):DirectiveBinding {
|
||||
static createFromBinding(b:Binding, ann:Directive):DirectiveBinding {
|
||||
if (isBlank(ann)) {
|
||||
ann = new Directive();
|
||||
}
|
||||
var rb = b.resolve();
|
||||
var deps = ListWrapper.map(rb.dependencies, DirectiveDependency.createFrom);
|
||||
return new DirectiveBinding(rb.key, rb.factory, deps, rb.providedAsPromise, annotation);
|
||||
var renderType;
|
||||
var compileChildren = ann.compileChildren;
|
||||
var resolvedInjectables = null;
|
||||
var changeDetection = null;
|
||||
if (ann instanceof Component) {
|
||||
renderType = DirectiveMetadata.COMPONENT_TYPE;
|
||||
if (isPresent(ann.injectables)) {
|
||||
resolvedInjectables = Injector.resolve(ann.injectables);
|
||||
}
|
||||
changeDetection = ann.changeDetection;
|
||||
} else {
|
||||
renderType = DirectiveMetadata.DIRECTIVE_TYPE;
|
||||
}
|
||||
var readAttributes = [];
|
||||
ListWrapper.forEach(deps, (dep) => {
|
||||
if (isPresent(dep.attributeName)) {
|
||||
ListWrapper.push(readAttributes, dep.attributeName);
|
||||
}
|
||||
});
|
||||
var metadata = new DirectiveMetadata({
|
||||
id: stringify(rb.key.token),
|
||||
type: renderType,
|
||||
selector: ann.selector,
|
||||
compileChildren: compileChildren,
|
||||
events: ann.events,
|
||||
hostListeners: isPresent(ann.hostListeners) ? MapWrapper.createFromStringMap(ann.hostListeners) : null,
|
||||
hostProperties: isPresent(ann.hostProperties) ? MapWrapper.createFromStringMap(ann.hostProperties) : null,
|
||||
hostAttributes: isPresent(ann.hostAttributes) ? MapWrapper.createFromStringMap(ann.hostAttributes) : null,
|
||||
hostActions: isPresent(ann.hostActions) ? MapWrapper.createFromStringMap(ann.hostActions) : null,
|
||||
properties: isPresent(ann.properties) ? MapWrapper.createFromStringMap(ann.properties) : null,
|
||||
readAttributes: readAttributes,
|
||||
callOnDestroy: ann.hasLifecycleHook(onDestroy),
|
||||
callOnChange: ann.hasLifecycleHook(onChange),
|
||||
callOnAllChangesDone: ann.hasLifecycleHook(onAllChangesDone),
|
||||
changeDetection: changeDetection
|
||||
});
|
||||
return new DirectiveBinding(rb.key, rb.factory, deps, rb.providedAsPromise, resolvedInjectables, metadata, ann);
|
||||
}
|
||||
|
||||
static createFromType(type:Type, annotation:Directive):DirectiveBinding {
|
||||
var binding = new Binding(type, {toClass: type});
|
||||
return DirectiveBinding.createFromBinding(binding, annotation);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO(rado): benchmark and consider rolling in as ElementInjector fields.
|
||||
@@ -476,7 +528,7 @@ export class ProtoElementInjector {
|
||||
|
||||
_createHostActionAccessors(b:DirectiveBinding) {
|
||||
var res = [];
|
||||
StringMapWrapper.forEach(b.hostActions, (actionExpression, actionName) => {
|
||||
MapWrapper.forEach(b.hostActions, (actionExpression, actionName) => {
|
||||
ListWrapper.push(res, new HostActionAccessor(actionExpression, reflector.getter(actionName)))
|
||||
});
|
||||
return res;
|
||||
@@ -627,8 +679,7 @@ export class ElementInjector extends TreeNode {
|
||||
var p = this._proto;
|
||||
if (isPresent(p._keyId0)) this._getDirectiveByKeyId(p._keyId0);
|
||||
if (isPresent(shadowDomAppInjector)) {
|
||||
var componentAnnotation:Component = this._proto._binding0.annotation;
|
||||
var publishAs = componentAnnotation.publishAs;
|
||||
var publishAs = this._proto._binding0.publishAs;
|
||||
if (isPresent(publishAs) && publishAs.length > 0) {
|
||||
// If there's a component directive on this element injector, then
|
||||
// 0-th key must contain the directive itself.
|
||||
|
||||
+175
-91
File diff suppressed because it is too large
Load Diff
+7
-6
@@ -168,20 +168,21 @@ export class AppProtoView {
|
||||
variableBindings: Map;
|
||||
protoLocals:Map;
|
||||
bindings:List;
|
||||
variableNames:List;
|
||||
render:renderApi.RenderProtoViewRef;
|
||||
|
||||
constructor(
|
||||
render:renderApi.RenderProtoViewRef,
|
||||
protoChangeDetector:ProtoChangeDetector,
|
||||
variableBindings:Map,
|
||||
protoLocals:Map,
|
||||
variableNames:List) {
|
||||
variableBindings:Map) {
|
||||
this.render = render;
|
||||
this.elementBinders = [];
|
||||
this.variableBindings = variableBindings;
|
||||
this.protoLocals = protoLocals;
|
||||
this.variableNames = variableNames;
|
||||
this.protoLocals = MapWrapper.create();
|
||||
if (isPresent(variableBindings)) {
|
||||
MapWrapper.forEach(variableBindings, (templateName, _) => {
|
||||
MapWrapper.set(this.protoLocals, templateName, null);
|
||||
});
|
||||
}
|
||||
this.protoChangeDetector = protoChangeDetector;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,15 +7,15 @@ import * as viewModule from './view';
|
||||
import * as avmModule from './view_manager';
|
||||
import {Renderer} from 'angular2/src/render/api';
|
||||
import {BindingPropagationConfig, Locals} from 'angular2/change_detection';
|
||||
import {DirectiveMetadataReader} from './directive_metadata_reader';
|
||||
import {DirectiveResolver} from './directive_resolver';
|
||||
import {RenderViewRef} from 'angular2/src/render/api';
|
||||
|
||||
@Injectable()
|
||||
export class AppViewManagerUtils {
|
||||
_metadataReader:DirectiveMetadataReader;
|
||||
_directiveResolver:DirectiveResolver;
|
||||
|
||||
constructor(metadataReader:DirectiveMetadataReader) {
|
||||
this._metadataReader = metadataReader;
|
||||
constructor(metadataReader:DirectiveResolver) {
|
||||
this._directiveResolver = metadataReader;
|
||||
}
|
||||
|
||||
getComponentInstance(parentView:viewModule.AppView, boundElementIndex:number):any {
|
||||
@@ -171,7 +171,7 @@ export class AppViewManagerUtils {
|
||||
if (isBlank(injector)) {
|
||||
injector = elementInjector.getLightDomAppInjector();
|
||||
}
|
||||
var annotation = this._metadataReader.read(componentBinding.token).annotation;
|
||||
var annotation = this._directiveResolver.resolve(componentBinding.token);
|
||||
var componentDirective = eli.DirectiveBinding.createFromBinding(componentBinding, annotation);
|
||||
elementInjector.dynamicallyCreateComponent(componentDirective, injector);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user