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:
parent
5114411749
commit
ecb068019b
|
@ -18,7 +18,7 @@ import {Router, RootRouter} from './src/router/router';
|
||||||
import {RouteRegistry} from './src/router/route_registry';
|
import {RouteRegistry} from './src/router/route_registry';
|
||||||
import {Pipeline} from './src/router/pipeline';
|
import {Pipeline} from './src/router/pipeline';
|
||||||
import {Location} from './src/router/location';
|
import {Location} from './src/router/location';
|
||||||
import {appComponentAnnotatedTypeToken} from './src/core/application_tokens';
|
import {appComponentRefToken} from './src/core/application_tokens';
|
||||||
import {bind} from './di';
|
import {bind} from './di';
|
||||||
|
|
||||||
export var routerInjectables:List = [
|
export var routerInjectables:List = [
|
||||||
|
@ -26,7 +26,7 @@ export var routerInjectables:List = [
|
||||||
Pipeline,
|
Pipeline,
|
||||||
BrowserLocation,
|
BrowserLocation,
|
||||||
Location,
|
Location,
|
||||||
bind(Router).toFactory((registry, pipeline, location, meta) => {
|
bind(Router).toFactory((registry, pipeline, location, app) => {
|
||||||
return new RootRouter(registry, pipeline, location, meta.type);
|
return new RootRouter(registry, pipeline, location, app.hostComponentType);
|
||||||
}, [RouteRegistry, Pipeline, Location, appComponentAnnotatedTypeToken])
|
}, [RouteRegistry, Pipeline, Location, appComponentRefToken])
|
||||||
];
|
];
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {Parser, Lexer, ChangeDetection, DynamicChangeDetection, PipeRegistry, de
|
||||||
import {ExceptionHandler} from './exception_handler';
|
import {ExceptionHandler} from './exception_handler';
|
||||||
import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
|
import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
|
||||||
import {TemplateResolver} from './compiler/template_resolver';
|
import {TemplateResolver} from './compiler/template_resolver';
|
||||||
import {DirectiveMetadataReader} from './compiler/directive_metadata_reader';
|
import {DirectiveResolver} from './compiler/directive_resolver';
|
||||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||||
|
@ -39,8 +39,7 @@ import {DefaultDomCompiler} from 'angular2/src/render/dom/compiler/compiler';
|
||||||
import {internalView} from 'angular2/src/core/compiler/view_ref';
|
import {internalView} from 'angular2/src/core/compiler/view_ref';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
appComponentRefToken,
|
appComponentRefToken
|
||||||
appComponentAnnotatedTypeToken
|
|
||||||
} from './application_tokens';
|
} from './application_tokens';
|
||||||
|
|
||||||
var _rootInjector: Injector;
|
var _rootInjector: Injector;
|
||||||
|
@ -54,16 +53,14 @@ var _rootBindings = [
|
||||||
function _injectorBindings(appComponentType): List<Binding> {
|
function _injectorBindings(appComponentType): List<Binding> {
|
||||||
return [
|
return [
|
||||||
bind(DOCUMENT_TOKEN).toValue(DOM.defaultDoc()),
|
bind(DOCUMENT_TOKEN).toValue(DOM.defaultDoc()),
|
||||||
bind(appComponentAnnotatedTypeToken).toFactory((reader) => {
|
|
||||||
// TODO(rado): investigate whether to support bindings on root component.
|
|
||||||
return reader.read(appComponentType);
|
|
||||||
}, [DirectiveMetadataReader]),
|
|
||||||
|
|
||||||
bind(appComponentRefToken).toAsyncFactory((dynamicComponentLoader, injector,
|
bind(appComponentRefToken).toAsyncFactory((dynamicComponentLoader, injector,
|
||||||
appComponentAnnotatedType, testability, registry) => {
|
metadataReader, testability, registry) => {
|
||||||
|
|
||||||
var selector = appComponentAnnotatedType.annotation.selector;
|
var annotation = metadataReader.resolve(appComponentType);
|
||||||
return dynamicComponentLoader.loadIntoNewLocation(appComponentAnnotatedType.type, null, selector, injector).then( (componentRef) => {
|
|
||||||
|
var selector = annotation.selector;
|
||||||
|
// TODO(rado): investigate whether to support bindings on root component.
|
||||||
|
return dynamicComponentLoader.loadIntoNewLocation(appComponentType, null, selector, injector).then( (componentRef) => {
|
||||||
var domView = resolveInternalDomView(componentRef.hostView.render);
|
var domView = resolveInternalDomView(componentRef.hostView.render);
|
||||||
// We need to do this here to ensure that we create Testability and
|
// We need to do this here to ensure that we create Testability and
|
||||||
// it's ready on the window for users.
|
// it's ready on the window for users.
|
||||||
|
@ -71,7 +68,7 @@ function _injectorBindings(appComponentType): List<Binding> {
|
||||||
|
|
||||||
return componentRef;
|
return componentRef;
|
||||||
});
|
});
|
||||||
}, [DynamicComponentLoader, Injector, appComponentAnnotatedTypeToken,
|
}, [DynamicComponentLoader, Injector, DirectiveResolver,
|
||||||
Testability, TestabilityRegistry]),
|
Testability, TestabilityRegistry]),
|
||||||
|
|
||||||
bind(appComponentType).toFactory((ref) => ref.instance,
|
bind(appComponentType).toFactory((ref) => ref.instance,
|
||||||
|
@ -109,7 +106,7 @@ function _injectorBindings(appComponentType): List<Binding> {
|
||||||
bind(PipeRegistry).toValue(defaultPipeRegistry),
|
bind(PipeRegistry).toValue(defaultPipeRegistry),
|
||||||
bind(ChangeDetection).toClass(DynamicChangeDetection),
|
bind(ChangeDetection).toClass(DynamicChangeDetection),
|
||||||
TemplateLoader,
|
TemplateLoader,
|
||||||
DirectiveMetadataReader,
|
DirectiveResolver,
|
||||||
Parser,
|
Parser,
|
||||||
Lexer,
|
Lexer,
|
||||||
ExceptionHandler,
|
ExceptionHandler,
|
||||||
|
@ -262,7 +259,7 @@ export function bootstrap(appComponentType: Type,
|
||||||
lc.registerWith(zone, appChangeDetector);
|
lc.registerWith(zone, appChangeDetector);
|
||||||
lc.tick(); //the first tick that will bootstrap the app
|
lc.tick(); //the first tick that will bootstrap the app
|
||||||
|
|
||||||
bootstrapProcess.resolve(new ApplicationRef(componentRef, appInjector));
|
bootstrapProcess.resolve(new ApplicationRef(componentRef, appComponentType, appInjector));
|
||||||
},
|
},
|
||||||
|
|
||||||
(err) => {
|
(err) => {
|
||||||
|
@ -276,9 +273,15 @@ export function bootstrap(appComponentType: Type,
|
||||||
export class ApplicationRef {
|
export class ApplicationRef {
|
||||||
_hostComponent:ComponentRef;
|
_hostComponent:ComponentRef;
|
||||||
_injector:Injector;
|
_injector:Injector;
|
||||||
constructor(hostComponent:ComponentRef, injector:Injector) {
|
_hostComponentType:Type;
|
||||||
|
constructor(hostComponent:ComponentRef, hostComponentType:Type, injector:Injector) {
|
||||||
this._hostComponent = hostComponent;
|
this._hostComponent = hostComponent;
|
||||||
this._injector = injector;
|
this._injector = injector;
|
||||||
|
this._hostComponentType = hostComponentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hostComponentType() {
|
||||||
|
return this._hostComponentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
get hostComponent() {
|
get hostComponent() {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import {OpaqueToken} from 'angular2/di';
|
import {OpaqueToken} from 'angular2/di';
|
||||||
|
|
||||||
export var appComponentRefToken:OpaqueToken = new OpaqueToken('ComponentRef');
|
export var appComponentRefToken:OpaqueToken = new OpaqueToken('ComponentRef');
|
||||||
export var appComponentAnnotatedTypeToken:OpaqueToken = new OpaqueToken('AppComponentAnnotatedType');
|
|
||||||
|
|
|
@ -4,9 +4,10 @@ import {Type, isBlank, isPresent, BaseException, normalizeBlank, stringify} from
|
||||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||||
import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
|
import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
import {DirectiveMetadataReader} from './directive_metadata_reader';
|
import {DirectiveResolver} from './directive_resolver';
|
||||||
import {Component, Directive} from '../annotations_impl/annotations';
|
|
||||||
import {AppProtoView} from './view';
|
import {AppProtoView} from './view';
|
||||||
|
import {ElementBinder} from './element_binder';
|
||||||
import {ProtoViewRef} from './view_ref';
|
import {ProtoViewRef} from './view_ref';
|
||||||
import {DirectiveBinding} from './element_injector';
|
import {DirectiveBinding} from './element_injector';
|
||||||
import {TemplateResolver} from './template_resolver';
|
import {TemplateResolver} from './template_resolver';
|
||||||
|
@ -47,7 +48,7 @@ export class CompilerCache {
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class Compiler {
|
export class Compiler {
|
||||||
_reader: DirectiveMetadataReader;
|
_reader: DirectiveResolver;
|
||||||
_compilerCache:CompilerCache;
|
_compilerCache:CompilerCache;
|
||||||
_compiling:Map<Type, Promise>;
|
_compiling:Map<Type, Promise>;
|
||||||
_templateResolver: TemplateResolver;
|
_templateResolver: TemplateResolver;
|
||||||
|
@ -57,7 +58,7 @@ export class Compiler {
|
||||||
_render: renderApi.RenderCompiler;
|
_render: renderApi.RenderCompiler;
|
||||||
_protoViewFactory:ProtoViewFactory;
|
_protoViewFactory:ProtoViewFactory;
|
||||||
|
|
||||||
constructor(reader: DirectiveMetadataReader,
|
constructor(reader: DirectiveResolver,
|
||||||
cache:CompilerCache,
|
cache:CompilerCache,
|
||||||
templateResolver: TemplateResolver,
|
templateResolver: TemplateResolver,
|
||||||
componentUrlMapper: ComponentUrlMapper,
|
componentUrlMapper: ComponentUrlMapper,
|
||||||
|
@ -79,11 +80,11 @@ export class Compiler {
|
||||||
if (directiveTypeOrBinding instanceof DirectiveBinding) {
|
if (directiveTypeOrBinding instanceof DirectiveBinding) {
|
||||||
return directiveTypeOrBinding;
|
return directiveTypeOrBinding;
|
||||||
} else if (directiveTypeOrBinding instanceof Binding) {
|
} else if (directiveTypeOrBinding instanceof Binding) {
|
||||||
let meta = this._reader.read(directiveTypeOrBinding.token);
|
let annotation = this._reader.resolve(directiveTypeOrBinding.token);
|
||||||
return DirectiveBinding.createFromBinding(directiveTypeOrBinding, meta.annotation);
|
return DirectiveBinding.createFromBinding(directiveTypeOrBinding, annotation);
|
||||||
} else {
|
} else {
|
||||||
let meta = this._reader.read(directiveTypeOrBinding);
|
let annotation = this._reader.resolve(directiveTypeOrBinding);
|
||||||
return DirectiveBinding.createFromType(meta.type, meta.annotation);
|
return DirectiveBinding.createFromType(directiveTypeOrBinding, annotation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,9 +94,9 @@ export class Compiler {
|
||||||
var componentBinding = this._bindDirective(componentTypeOrBinding);
|
var componentBinding = this._bindDirective(componentTypeOrBinding);
|
||||||
this._assertTypeIsComponent(componentBinding);
|
this._assertTypeIsComponent(componentBinding);
|
||||||
|
|
||||||
var directiveMetadata = Compiler.buildRenderDirective(componentBinding);
|
var directiveMetadata = componentBinding.metadata;
|
||||||
return this._render.compileHost(directiveMetadata).then( (hostRenderPv) => {
|
return this._render.compileHost(directiveMetadata).then( (hostRenderPv) => {
|
||||||
return this._compileNestedProtoViews(null, null, hostRenderPv, [componentBinding], true);
|
return this._compileNestedProtoViews(componentBinding, hostRenderPv, [componentBinding]);
|
||||||
}).then( (appProtoView) => {
|
}).then( (appProtoView) => {
|
||||||
return new ProtoViewRef(appProtoView);
|
return new ProtoViewRef(appProtoView);
|
||||||
});
|
});
|
||||||
|
@ -139,7 +140,7 @@ export class Compiler {
|
||||||
);
|
);
|
||||||
var renderTemplate = this._buildRenderTemplate(component, template, directives);
|
var renderTemplate = this._buildRenderTemplate(component, template, directives);
|
||||||
pvPromise = this._render.compile(renderTemplate).then( (renderPv) => {
|
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);
|
MapWrapper.set(this._compiling, component, pvPromise);
|
||||||
|
@ -147,10 +148,12 @@ export class Compiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(tbosch): union type return AppProtoView or Promise<AppProtoView>
|
// TODO(tbosch): union type return AppProtoView or Promise<AppProtoView>
|
||||||
_compileNestedProtoViews(parentProtoView, componentBinding, renderPv, directives, isComponentRootView) {
|
_compileNestedProtoViews(componentBinding, renderPv, directives) {
|
||||||
var nestedPVPromises = [];
|
var protoViews = this._protoViewFactory.createAppProtoViews(componentBinding, renderPv, directives);
|
||||||
var protoView = this._protoViewFactory.createProtoView(parentProtoView, componentBinding, renderPv, directives);
|
var protoView = protoViews[0];
|
||||||
if (isComponentRootView && isPresent(componentBinding)) {
|
// 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,
|
// Populate the cache before compiling the nested components,
|
||||||
// so that components can reference themselves in their template.
|
// so that components can reference themselves in their template.
|
||||||
var component = componentBinding.key.token;
|
var component = componentBinding.key.token;
|
||||||
|
@ -158,25 +161,18 @@ export class Compiler {
|
||||||
MapWrapper.delete(this._compiling, component);
|
MapWrapper.delete(this._compiling, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
var binderIndex = 0;
|
var nestedPVPromises = [];
|
||||||
ListWrapper.forEach(protoView.elementBinders, (elementBinder) => {
|
ListWrapper.forEach(this._collectComponentElementBinders(protoViews), (elementBinder) => {
|
||||||
var nestedComponent = elementBinder.componentDirective;
|
var nestedComponent = elementBinder.componentDirective;
|
||||||
var nestedRenderProtoView = renderPv.elementBinders[binderIndex].nestedProtoView;
|
|
||||||
var elementBinderDone = (nestedPv) => {
|
var elementBinderDone = (nestedPv) => {
|
||||||
elementBinder.nestedProtoView = nestedPv;
|
elementBinder.nestedProtoView = nestedPv;
|
||||||
};
|
};
|
||||||
var nestedCall = null;
|
var nestedCall = this._compile(nestedComponent);
|
||||||
if (isPresent(nestedComponent)) {
|
|
||||||
nestedCall = this._compile(nestedComponent);
|
|
||||||
} else if (isPresent(nestedRenderProtoView)) {
|
|
||||||
nestedCall = this._compileNestedProtoViews(protoView, componentBinding, nestedRenderProtoView, directives, false);
|
|
||||||
}
|
|
||||||
if (PromiseWrapper.isPromise(nestedCall)) {
|
if (PromiseWrapper.isPromise(nestedCall)) {
|
||||||
ListWrapper.push(nestedPVPromises, nestedCall.then(elementBinderDone));
|
ListWrapper.push(nestedPVPromises, nestedCall.then(elementBinderDone));
|
||||||
} else if (isPresent(nestedCall)) {
|
} else if (isPresent(nestedCall)) {
|
||||||
elementBinderDone(nestedCall);
|
elementBinderDone(nestedCall);
|
||||||
}
|
}
|
||||||
binderIndex++;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var protoViewDone = (_) => {
|
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 {
|
_buildRenderTemplate(component, view, directives): renderApi.ViewDefinition {
|
||||||
var componentUrl = this._urlResolver.resolve(
|
var componentUrl = this._urlResolver.resolve(
|
||||||
this._appUrl, this._componentUrlMapper.getUrl(component)
|
this._appUrl, this._componentUrlMapper.getUrl(component)
|
||||||
|
@ -206,36 +214,7 @@ export class Compiler {
|
||||||
componentId: stringify(component),
|
componentId: stringify(component),
|
||||||
absUrl: templateAbsUrl,
|
absUrl: templateAbsUrl,
|
||||||
template: view.template,
|
template: view.template,
|
||||||
directives: ListWrapper.map(directives, Compiler.buildRenderDirective)
|
directives: ListWrapper.map(directives, directiveBinding => directiveBinding.metadata )
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,7 +239,7 @@ export class Compiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
_assertTypeIsComponent(directiveBinding:DirectiveBinding):void {
|
_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.`);
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +1,18 @@
|
||||||
import {Injector} from 'angular2/di';
|
|
||||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
import {Injectable} from 'angular2/src/di/annotations_impl';
|
||||||
import {Type, isPresent, BaseException, stringify} from 'angular2/src/facade/lang';
|
import {Type, isPresent, BaseException, stringify} from 'angular2/src/facade/lang';
|
||||||
import {Directive, Component} from '../annotations_impl/annotations';
|
import {Directive} from '../annotations_impl/annotations';
|
||||||
import {DirectiveMetadata} from './directive_metadata';
|
|
||||||
import {reflector} from 'angular2/src/reflection/reflection';
|
import {reflector} from 'angular2/src/reflection/reflection';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DirectiveMetadataReader {
|
export class DirectiveResolver {
|
||||||
read(type:Type):DirectiveMetadata {
|
resolve(type:Type):Directive {
|
||||||
var annotations = reflector.annotations(type);
|
var annotations = reflector.annotations(type);
|
||||||
if (isPresent(annotations)) {
|
if (isPresent(annotations)) {
|
||||||
for (var i=0; i<annotations.length; i++) {
|
for (var i=0; i<annotations.length; i++) {
|
||||||
var annotation = annotations[i];
|
var annotation = annotations[i];
|
||||||
|
|
||||||
if (annotation instanceof Directive) {
|
if (annotation instanceof Directive) {
|
||||||
var resolvedInjectables = null;
|
return annotation;
|
||||||
if (annotation instanceof Component && isPresent(annotation.injectables)) {
|
|
||||||
resolvedInjectables = Injector.resolve(annotation.injectables);
|
|
||||||
}
|
|
||||||
return new DirectiveMetadata(type, annotation, resolvedInjectables);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,6 +15,8 @@ import {Directive, Component, onChange, onDestroy, onAllChangesDone} from 'angul
|
||||||
import {ChangeDetector, ChangeDetectorRef} from 'angular2/change_detection';
|
import {ChangeDetector, ChangeDetectorRef} from 'angular2/change_detection';
|
||||||
import {QueryList} from './query_list';
|
import {QueryList} from './query_list';
|
||||||
import {reflector} from 'angular2/src/reflection/reflection';
|
import {reflector} from 'angular2/src/reflection/reflection';
|
||||||
|
import {DirectiveMetadata} from 'angular2/src/render/api';
|
||||||
|
|
||||||
|
|
||||||
var _MAX_DIRECTIVE_CONSTRUCTION_COUNTER = 10;
|
var _MAX_DIRECTIVE_CONSTRUCTION_COUNTER = 10;
|
||||||
|
|
||||||
|
@ -223,54 +225,104 @@ export class DirectiveDependency extends Dependency {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DirectiveBinding extends ResolvedBinding {
|
export class DirectiveBinding extends ResolvedBinding {
|
||||||
callOnDestroy:boolean;
|
|
||||||
callOnChange:boolean;
|
|
||||||
callOnAllChangesDone:boolean;
|
|
||||||
annotation:Directive;
|
|
||||||
resolvedInjectables:List<ResolvedBinding>;
|
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);
|
super(key, factory, dependencies, providedAsPromise);
|
||||||
this.callOnDestroy = isPresent(annotation) && annotation.hasLifecycleHook(onDestroy);
|
this.resolvedInjectables = resolvedInjectables;
|
||||||
this.callOnChange = isPresent(annotation) && annotation.hasLifecycleHook(onChange);
|
this.metadata = metadata;
|
||||||
this.callOnAllChangesDone = isPresent(annotation) && annotation.hasLifecycleHook(onAllChangesDone);
|
if (annotation instanceof Component) {
|
||||||
this.annotation = annotation;
|
this.publishAs = annotation.publishAs;
|
||||||
if (annotation instanceof Component && isPresent(annotation.injectables)) {
|
} else {
|
||||||
this.resolvedInjectables = Injector.resolve(annotation.injectables);
|
this.publishAs = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get callOnDestroy() {
|
||||||
|
return this.metadata.callOnDestroy;
|
||||||
|
}
|
||||||
|
|
||||||
|
get callOnChange() {
|
||||||
|
return this.metadata.callOnChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
get callOnAllChangesDone() {
|
||||||
|
return this.metadata.callOnAllChangesDone;
|
||||||
|
}
|
||||||
|
|
||||||
get displayName() {
|
get displayName() {
|
||||||
return this.key.displayName;
|
return this.key.displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
get eventEmitters():List<string> {
|
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
|
get hostActions():Map<string,string> {
|
||||||
return isPresent(this.annotation) && isPresent(this.annotation.hostActions) ? this.annotation.hostActions : {};
|
return isPresent(this.metadata) && isPresent(this.metadata.hostActions) ? this.metadata.hostActions : MapWrapper.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
get changeDetection() {
|
get changeDetection() {
|
||||||
if (this.annotation instanceof Component) {
|
if (isPresent(metadata)) {
|
||||||
var c:Component = this.annotation;
|
return metadata.changeDetection;
|
||||||
return c.changeDetection;
|
|
||||||
} else {
|
} else {
|
||||||
return null;
|
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 rb = b.resolve();
|
||||||
var deps = ListWrapper.map(rb.dependencies, DirectiveDependency.createFrom);
|
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 {
|
static createFromType(type:Type, annotation:Directive):DirectiveBinding {
|
||||||
var binding = new Binding(type, {toClass: type});
|
var binding = new Binding(type, {toClass: type});
|
||||||
return DirectiveBinding.createFromBinding(binding, annotation);
|
return DirectiveBinding.createFromBinding(binding, annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(rado): benchmark and consider rolling in as ElementInjector fields.
|
// TODO(rado): benchmark and consider rolling in as ElementInjector fields.
|
||||||
|
@ -476,7 +528,7 @@ export class ProtoElementInjector {
|
||||||
|
|
||||||
_createHostActionAccessors(b:DirectiveBinding) {
|
_createHostActionAccessors(b:DirectiveBinding) {
|
||||||
var res = [];
|
var res = [];
|
||||||
StringMapWrapper.forEach(b.hostActions, (actionExpression, actionName) => {
|
MapWrapper.forEach(b.hostActions, (actionExpression, actionName) => {
|
||||||
ListWrapper.push(res, new HostActionAccessor(actionExpression, reflector.getter(actionName)))
|
ListWrapper.push(res, new HostActionAccessor(actionExpression, reflector.getter(actionName)))
|
||||||
});
|
});
|
||||||
return res;
|
return res;
|
||||||
|
@ -627,8 +679,7 @@ export class ElementInjector extends TreeNode {
|
||||||
var p = this._proto;
|
var p = this._proto;
|
||||||
if (isPresent(p._keyId0)) this._getDirectiveByKeyId(p._keyId0);
|
if (isPresent(p._keyId0)) this._getDirectiveByKeyId(p._keyId0);
|
||||||
if (isPresent(shadowDomAppInjector)) {
|
if (isPresent(shadowDomAppInjector)) {
|
||||||
var componentAnnotation:Component = this._proto._binding0.annotation;
|
var publishAs = this._proto._binding0.publishAs;
|
||||||
var publishAs = componentAnnotation.publishAs;
|
|
||||||
if (isPresent(publishAs) && publishAs.length > 0) {
|
if (isPresent(publishAs) && publishAs.length > 0) {
|
||||||
// If there's a component directive on this element injector, then
|
// If there's a component directive on this element injector, then
|
||||||
// 0-th key must contain the directive itself.
|
// 0-th key must contain the directive itself.
|
||||||
|
|
|
@ -4,14 +4,15 @@ import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
import {reflector} from 'angular2/src/reflection/reflection';
|
import {reflector} from 'angular2/src/reflection/reflection';
|
||||||
|
|
||||||
import {ChangeDetection, DirectiveIndex, BindingRecord, DirectiveRecord, ProtoChangeDetector, ChangeDetectorDefinition} from 'angular2/change_detection';
|
import {
|
||||||
import {Component} from '../annotations_impl/annotations';
|
ChangeDetection, DirectiveIndex, BindingRecord, DirectiveRecord,
|
||||||
|
ProtoChangeDetector, DEFAULT, ChangeDetectorDefinition
|
||||||
|
} from 'angular2/change_detection';
|
||||||
|
|
||||||
import * as renderApi from 'angular2/src/render/api';
|
import * as renderApi from 'angular2/src/render/api';
|
||||||
import {AppProtoView} from './view';
|
import {AppProtoView} from './view';
|
||||||
import {ProtoElementInjector, DirectiveBinding} from './element_injector';
|
import {ProtoElementInjector, DirectiveBinding} from './element_injector';
|
||||||
|
|
||||||
|
|
||||||
class BindingRecordsCreator {
|
class BindingRecordsCreator {
|
||||||
_directiveRecordsMap;
|
_directiveRecordsMap;
|
||||||
_textNodeIndex:number;
|
_textNodeIndex:number;
|
||||||
|
@ -21,26 +22,31 @@ class BindingRecordsCreator {
|
||||||
this._textNodeIndex = 0;
|
this._textNodeIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
getBindingRecords(elementBinders:List<renderApi.ElementBinder>, sortedDirectives:List<SortedDirectives>):List<BindingRecord> {
|
getBindingRecords(elementBinders:List<renderApi.ElementBinder>,
|
||||||
|
allDirectiveMetadatas:List<renderApi.DirectiveMetadata>
|
||||||
|
):List<BindingRecord> {
|
||||||
var bindings = [];
|
var bindings = [];
|
||||||
|
|
||||||
for (var boundElementIndex = 0; boundElementIndex < elementBinders.length; boundElementIndex++) {
|
for (var boundElementIndex = 0; boundElementIndex < elementBinders.length; boundElementIndex++) {
|
||||||
var renderElementBinder = elementBinders[boundElementIndex];
|
var renderElementBinder = elementBinders[boundElementIndex];
|
||||||
bindings = ListWrapper.concat(bindings, this._createTextNodeRecords(renderElementBinder));
|
bindings = ListWrapper.concat(bindings, this._createTextNodeRecords(renderElementBinder));
|
||||||
bindings = ListWrapper.concat(bindings, this._createElementPropertyRecords(boundElementIndex, renderElementBinder));
|
bindings = ListWrapper.concat(bindings, this._createElementPropertyRecords(boundElementIndex, renderElementBinder));
|
||||||
bindings = ListWrapper.concat(bindings, this._createDirectiveRecords(boundElementIndex, sortedDirectives[boundElementIndex]));
|
bindings = ListWrapper.concat(bindings, this._createDirectiveRecords(boundElementIndex,
|
||||||
|
renderElementBinder.directives, allDirectiveMetadatas));
|
||||||
}
|
}
|
||||||
|
|
||||||
return bindings;
|
return bindings;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDirectiveRecords(sortedDirectives:List<SortedDirectives>): List {
|
getDirectiveRecords(
|
||||||
|
elementBinders:List<renderApi.ElementBinder>,
|
||||||
|
allDirectiveMetadatas:List<renderApi.DirectiveMetadata>): List<DirectiveRecord> {
|
||||||
var directiveRecords = [];
|
var directiveRecords = [];
|
||||||
|
|
||||||
for (var elementIndex = 0; elementIndex < sortedDirectives.length; ++elementIndex) {
|
for (var elementIndex = 0; elementIndex < elementBinders.length; ++elementIndex) {
|
||||||
var dirs = sortedDirectives[elementIndex].directives;
|
var dirs = elementBinders[elementIndex].directives;
|
||||||
for (var dirIndex = 0; dirIndex < dirs.length; ++dirIndex) {
|
for (var dirIndex = 0; dirIndex < dirs.length; ++dirIndex) {
|
||||||
ListWrapper.push(directiveRecords, this._getDirectiveRecord(elementIndex, dirIndex, dirs[dirIndex]));
|
ListWrapper.push(directiveRecords, this._getDirectiveRecord(elementIndex, dirIndex, allDirectiveMetadatas[dirs[dirIndex].directiveIndex]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,17 +66,19 @@ class BindingRecordsCreator {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
_createDirectiveRecords(boundElementIndex:number, sortedDirectives:SortedDirectives) {
|
_createDirectiveRecords(boundElementIndex:number, directiveBinders:List<renderApi.DirectiveBinder>,
|
||||||
|
allDirectiveMetadatas:List<renderApi.DirectiveMetadata>) {
|
||||||
var res = [];
|
var res = [];
|
||||||
for (var i = 0; i < sortedDirectives.renderDirectives.length; i++) {
|
for (var i = 0; i < directiveBinders.length; i++) {
|
||||||
var directiveBinder = sortedDirectives.renderDirectives[i];
|
var directiveBinder = directiveBinders[i];
|
||||||
|
var directiveMetadata = allDirectiveMetadatas[directiveBinder.directiveIndex];
|
||||||
|
|
||||||
// directive properties
|
// directive properties
|
||||||
MapWrapper.forEach(directiveBinder.propertyBindings, (astWithSource, propertyName) => {
|
MapWrapper.forEach(directiveBinder.propertyBindings, (astWithSource, propertyName) => {
|
||||||
// TODO: these setters should eventually be created by change detection, to make
|
// TODO: these setters should eventually be created by change detection, to make
|
||||||
// it monomorphic!
|
// it monomorphic!
|
||||||
var setter = reflector.setter(propertyName);
|
var setter = reflector.setter(propertyName);
|
||||||
var directiveRecord = this._getDirectiveRecord(boundElementIndex, i, sortedDirectives.directives[i]);
|
var directiveRecord = this._getDirectiveRecord(boundElementIndex, i, directiveMetadata);
|
||||||
var b = BindingRecord.createForDirective(astWithSource, propertyName, setter, directiveRecord);
|
var b = BindingRecord.createForDirective(astWithSource, propertyName, setter, directiveRecord);
|
||||||
ListWrapper.push(res, b);
|
ListWrapper.push(res, b);
|
||||||
});
|
});
|
||||||
|
@ -85,22 +93,21 @@ class BindingRecordsCreator {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getDirectiveRecord(boundElementIndex:number, directiveIndex:number, binding:DirectiveBinding): DirectiveRecord {
|
_getDirectiveRecord(boundElementIndex:number, directiveIndex:number, directiveMetadata:renderApi.DirectiveMetadata): DirectiveRecord {
|
||||||
var id = boundElementIndex * 100 + directiveIndex;
|
var id = boundElementIndex * 100 + directiveIndex;
|
||||||
|
|
||||||
if (!MapWrapper.contains(this._directiveRecordsMap, id)) {
|
if (!MapWrapper.contains(this._directiveRecordsMap, id)) {
|
||||||
var changeDetection = binding.changeDetection;
|
var changeDetection = directiveMetadata.changeDetection;
|
||||||
|
|
||||||
MapWrapper.set(this._directiveRecordsMap, id,
|
MapWrapper.set(this._directiveRecordsMap, id,
|
||||||
new DirectiveRecord(new DirectiveIndex(boundElementIndex, directiveIndex),
|
new DirectiveRecord(new DirectiveIndex(boundElementIndex, directiveIndex),
|
||||||
binding.callOnAllChangesDone, binding.callOnChange, changeDetection));
|
directiveMetadata.callOnAllChangesDone, directiveMetadata.callOnChange, changeDetection));
|
||||||
}
|
}
|
||||||
|
|
||||||
return MapWrapper.get(this._directiveRecordsMap, id);
|
return MapWrapper.get(this._directiveRecordsMap, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ProtoViewFactory {
|
export class ProtoViewFactory {
|
||||||
_changeDetection:ChangeDetection;
|
_changeDetection:ChangeDetection;
|
||||||
|
@ -109,32 +116,117 @@ export class ProtoViewFactory {
|
||||||
this._changeDetection = changeDetection;
|
this._changeDetection = changeDetection;
|
||||||
}
|
}
|
||||||
|
|
||||||
createProtoView(parentProtoView:AppProtoView, componentBinding:DirectiveBinding,
|
/**
|
||||||
renderProtoView: renderApi.ProtoViewDto, directives:List<DirectiveBinding>):AppProtoView {
|
* Returns the data needed to create ChangeDetectors
|
||||||
|
* for the given ProtoView and all nested ProtoViews.
|
||||||
|
*/
|
||||||
|
getChangeDetectorDefinitions(hostComponentMetadata:renderApi.DirectiveMetadata,
|
||||||
|
rootRenderProtoView: renderApi.ProtoViewDto, allRenderDirectiveMetadata:List<renderApi.DirectiveMetadata>):List<ChangeDetectorDefinition> {
|
||||||
|
var nestedPvsWithIndex = this._collectNestedProtoViews(rootRenderProtoView);
|
||||||
|
var nestedPvVariableBindings = this._collectNestedProtoViewsVariableBindings(nestedPvsWithIndex);
|
||||||
|
var nestedPvVariableNames = this._collectNestedProtoViewsVariableNames(nestedPvsWithIndex, nestedPvVariableBindings);
|
||||||
|
|
||||||
|
return this._getChangeDetectorDefinitions(
|
||||||
|
hostComponentMetadata,
|
||||||
|
nestedPvsWithIndex,
|
||||||
|
nestedPvVariableNames,
|
||||||
|
allRenderDirectiveMetadata
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
createAppProtoViews(hostComponentBinding:DirectiveBinding,
|
||||||
|
rootRenderProtoView: renderApi.ProtoViewDto, allDirectives:List<DirectiveBinding>):List<AppProtoView> {
|
||||||
|
var allRenderDirectiveMetadata = ListWrapper.map(allDirectives, directiveBinding => directiveBinding.metadata );
|
||||||
|
var nestedPvsWithIndex = this._collectNestedProtoViews(rootRenderProtoView);
|
||||||
|
var nestedPvVariableBindings = this._collectNestedProtoViewsVariableBindings(nestedPvsWithIndex);
|
||||||
|
var nestedPvVariableNames = this._collectNestedProtoViewsVariableNames(nestedPvsWithIndex, nestedPvVariableBindings);
|
||||||
|
var changeDetectorDefs = this._getChangeDetectorDefinitions(
|
||||||
|
hostComponentBinding.metadata, nestedPvsWithIndex, nestedPvVariableNames, allRenderDirectiveMetadata
|
||||||
|
);
|
||||||
|
var protoChangeDetectors = ListWrapper.map(
|
||||||
|
changeDetectorDefs, changeDetectorDef => this._changeDetection.createProtoChangeDetector(changeDetectorDef)
|
||||||
|
);
|
||||||
|
var appProtoViews = ListWrapper.createFixedSize(nestedPvsWithIndex.length);
|
||||||
|
ListWrapper.forEach(nestedPvsWithIndex, (pvWithIndex) => {
|
||||||
|
var appProtoView = this._createAppProtoView(
|
||||||
|
pvWithIndex.renderProtoView,
|
||||||
|
protoChangeDetectors[pvWithIndex.index],
|
||||||
|
nestedPvVariableBindings[pvWithIndex.index],
|
||||||
|
allDirectives
|
||||||
|
);
|
||||||
|
if (isPresent(pvWithIndex.parentIndex)) {
|
||||||
|
var parentView = appProtoViews[pvWithIndex.parentIndex];
|
||||||
|
parentView.elementBinders[pvWithIndex.boundElementIndex].nestedProtoView = appProtoView;
|
||||||
|
}
|
||||||
|
appProtoViews[pvWithIndex.index] = appProtoView;
|
||||||
|
});
|
||||||
|
return appProtoViews;
|
||||||
|
}
|
||||||
|
|
||||||
|
_collectNestedProtoViews(renderProtoView:renderApi.ProtoViewDto, parentIndex:number = null, boundElementIndex = null, result:List<RenderProtoViewWithIndex> = null):List<RenderProtoViewWithIndex> {
|
||||||
|
if (isBlank(result)) {
|
||||||
|
result = [];
|
||||||
|
}
|
||||||
|
ListWrapper.push(result, new RenderProtoViewWithIndex(renderProtoView, result.length, parentIndex, boundElementIndex));
|
||||||
|
var currentIndex = result.length - 1;
|
||||||
|
var childBoundElementIndex = 0;
|
||||||
|
ListWrapper.forEach(renderProtoView.elementBinders, (elementBinder) => {
|
||||||
|
if (isPresent(elementBinder.nestedProtoView)) {
|
||||||
|
this._collectNestedProtoViews(elementBinder.nestedProtoView, currentIndex, childBoundElementIndex, result);
|
||||||
|
}
|
||||||
|
childBoundElementIndex++;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
_getChangeDetectorDefinitions(
|
||||||
|
hostComponentMetadata:renderApi.DirectiveMetadata,
|
||||||
|
nestedPvsWithIndex: List<RenderProtoViewWithIndex>,
|
||||||
|
nestedPvVariableNames: List<List<string>>,
|
||||||
|
allRenderDirectiveMetadata:List<renderApi.DirectiveMetadata>):List<ChangeDetectorDefinition> {
|
||||||
|
return ListWrapper.map(nestedPvsWithIndex, (pvWithIndex) => {
|
||||||
|
var elementBinders = pvWithIndex.renderProtoView.elementBinders;
|
||||||
|
var bindingRecordsCreator = new BindingRecordsCreator();
|
||||||
|
var bindingRecords = bindingRecordsCreator.getBindingRecords(elementBinders, allRenderDirectiveMetadata);
|
||||||
|
var directiveRecords = bindingRecordsCreator.getDirectiveRecords(elementBinders, allRenderDirectiveMetadata);
|
||||||
|
var strategyName = DEFAULT;
|
||||||
|
var typeString;
|
||||||
|
if (pvWithIndex.renderProtoView.type === renderApi.ProtoViewDto.COMPONENT_VIEW_TYPE) {
|
||||||
|
strategyName = hostComponentMetadata.changeDetection;
|
||||||
|
typeString = 'comp';
|
||||||
|
} else if (pvWithIndex.renderProtoView.type === renderApi.ProtoViewDto.HOST_VIEW_TYPE) {
|
||||||
|
typeString = 'host';
|
||||||
|
} else {
|
||||||
|
typeString = 'embedded';
|
||||||
|
}
|
||||||
|
var id = `${hostComponentMetadata.id}_${typeString}_${pvWithIndex.index}`;
|
||||||
|
var variableNames = nestedPvVariableNames[pvWithIndex.index];
|
||||||
|
return new ChangeDetectorDefinition(id, strategyName, variableNames, bindingRecords, directiveRecords);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_createAppProtoView(
|
||||||
|
renderProtoView: renderApi.ProtoViewDto,
|
||||||
|
protoChangeDetector: ProtoChangeDetector,
|
||||||
|
variableBindings: Map<string, string>,
|
||||||
|
allDirectives:List<DirectiveBinding>
|
||||||
|
):AppProtoView {
|
||||||
var elementBinders = renderProtoView.elementBinders;
|
var elementBinders = renderProtoView.elementBinders;
|
||||||
var sortedDirectives = ListWrapper.map(elementBinders, b => new SortedDirectives(b.directives, directives));
|
var protoView = new AppProtoView(renderProtoView.render, protoChangeDetector, variableBindings);
|
||||||
|
|
||||||
var variableBindings = this._createVariableBindings(renderProtoView);
|
|
||||||
var protoLocals = this._createProtoLocals(variableBindings);
|
|
||||||
var variableNames = this._createVariableNames(parentProtoView, protoLocals);
|
|
||||||
|
|
||||||
var protoChangeDetector = this._createProtoChangeDetector(elementBinders, sortedDirectives, componentBinding, variableNames);
|
|
||||||
var protoView = new AppProtoView(renderProtoView.render, protoChangeDetector, variableBindings, protoLocals, variableNames);
|
|
||||||
|
|
||||||
// TODO: vsavkin refactor to pass element binders into proto view
|
// TODO: vsavkin refactor to pass element binders into proto view
|
||||||
this._createElementBinders(protoView, elementBinders, sortedDirectives)
|
this._createElementBinders(protoView, elementBinders, allDirectives);
|
||||||
this._bindDirectiveEvents(protoView, sortedDirectives);
|
this._bindDirectiveEvents(protoView, elementBinders);
|
||||||
|
|
||||||
return protoView;
|
return protoView;
|
||||||
}
|
}
|
||||||
|
|
||||||
_createProtoLocals(varBindings:Map):Map {
|
_collectNestedProtoViewsVariableBindings(
|
||||||
var protoLocals = MapWrapper.create();
|
nestedPvsWithIndex: List<RenderProtoViewWithIndex>
|
||||||
MapWrapper.forEach(varBindings, (mappedName, varName) => {
|
):List<Map<string, string>> {
|
||||||
MapWrapper.set(protoLocals, mappedName, null);
|
return ListWrapper.map(nestedPvsWithIndex, (pvWithIndex) => {
|
||||||
|
return this._createVariableBindings(pvWithIndex.renderProtoView);
|
||||||
});
|
});
|
||||||
return protoLocals;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_createVariableBindings(renderProtoView):Map {
|
_createVariableBindings(renderProtoView):Map {
|
||||||
|
@ -150,42 +242,46 @@ export class ProtoViewFactory {
|
||||||
return variableBindings;
|
return variableBindings;
|
||||||
}
|
}
|
||||||
|
|
||||||
_createVariableNames(parentProtoView, protoLocals):List {
|
_collectNestedProtoViewsVariableNames(
|
||||||
var variableNames = isPresent(parentProtoView) ? ListWrapper.clone(parentProtoView.variableNames) : [];
|
nestedPvsWithIndex: List<RenderProtoViewWithIndex>,
|
||||||
MapWrapper.forEach(protoLocals, (v, local) => {
|
nestedPvVariableBindings:List<Map<string, string>>
|
||||||
|
):List<List<string>> {
|
||||||
|
var nestedPvVariableNames = ListWrapper.createFixedSize(nestedPvsWithIndex.length);
|
||||||
|
ListWrapper.forEach(nestedPvsWithIndex, (pvWithIndex) => {
|
||||||
|
var parentVariableNames = isPresent(pvWithIndex.parentIndex) ? nestedPvVariableNames[pvWithIndex.parentIndex] : null;
|
||||||
|
nestedPvVariableNames[pvWithIndex.index] = this._createVariableNames(
|
||||||
|
parentVariableNames, nestedPvVariableBindings[pvWithIndex.index]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return nestedPvVariableNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
_createVariableNames(parentVariableNames, variableBindings):List {
|
||||||
|
var variableNames = isPresent(parentVariableNames) ? ListWrapper.clone(parentVariableNames) : [];
|
||||||
|
MapWrapper.forEach(variableBindings, (local, v) => {
|
||||||
ListWrapper.push(variableNames, local);
|
ListWrapper.push(variableNames, local);
|
||||||
});
|
});
|
||||||
return variableNames;
|
return variableNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
_createProtoChangeDetector(elementBinders, sortedDirectives, componentBinding, variableNames):ProtoChangeDetector {
|
_createElementBinders(protoView, elementBinders, allDirectiveBindings) {
|
||||||
var bindingRecordsCreator = new BindingRecordsCreator();
|
|
||||||
var bindingRecords = bindingRecordsCreator.getBindingRecords(elementBinders, sortedDirectives);
|
|
||||||
var directiveRecords = bindingRecordsCreator.getDirectiveRecords(sortedDirectives);
|
|
||||||
|
|
||||||
var changeDetection = null;
|
|
||||||
var name = 'root';
|
|
||||||
if (isPresent(componentBinding)) {
|
|
||||||
var componentAnnotation:Component = componentBinding.annotation;
|
|
||||||
changeDetection = componentAnnotation.changeDetection;
|
|
||||||
name = 'dummy';
|
|
||||||
}
|
|
||||||
|
|
||||||
var definition = new ChangeDetectorDefinition(name, changeDetection, variableNames, bindingRecords, directiveRecords);
|
|
||||||
return this._changeDetection.createProtoChangeDetector(definition);
|
|
||||||
}
|
|
||||||
|
|
||||||
_createElementBinders(protoView, elementBinders, sortedDirectives) {
|
|
||||||
for (var i=0; i<elementBinders.length; i++) {
|
for (var i=0; i<elementBinders.length; i++) {
|
||||||
var renderElementBinder = elementBinders[i];
|
var renderElementBinder = elementBinders[i];
|
||||||
var dirs = sortedDirectives[i];
|
var dirs = elementBinders[i].directives;
|
||||||
|
|
||||||
var parentPeiWithDistance = this._findParentProtoElementInjectorWithDistance(
|
var parentPeiWithDistance = this._findParentProtoElementInjectorWithDistance(
|
||||||
i, protoView.elementBinders, elementBinders);
|
i, protoView.elementBinders, elementBinders);
|
||||||
|
var directiveBindings = ListWrapper.map(dirs, (dir) => allDirectiveBindings[dir.directiveIndex] );
|
||||||
|
var componentDirectiveBinding = null;
|
||||||
|
if (directiveBindings.length > 0) {
|
||||||
|
if (directiveBindings[0].metadata.type === renderApi.DirectiveMetadata.COMPONENT_TYPE) {
|
||||||
|
componentDirectiveBinding = directiveBindings[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
var protoElementInjector = this._createProtoElementInjector(
|
var protoElementInjector = this._createProtoElementInjector(
|
||||||
i, parentPeiWithDistance, dirs, renderElementBinder);
|
i, parentPeiWithDistance, renderElementBinder, componentDirectiveBinding, directiveBindings);
|
||||||
|
|
||||||
this._createElementBinder(protoView, i, renderElementBinder, protoElementInjector, dirs);
|
this._createElementBinder(protoView, i, renderElementBinder, protoElementInjector, componentDirectiveBinding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,22 +301,22 @@ export class ProtoViewFactory {
|
||||||
return new ParentProtoElementInjectorWithDistance(null, -1);
|
return new ParentProtoElementInjectorWithDistance(null, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
_createProtoElementInjector(binderIndex, parentPeiWithDistance, sortedDirectives, renderElementBinder) {
|
_createProtoElementInjector(binderIndex, parentPeiWithDistance, renderElementBinder, componentDirectiveBinding, directiveBindings) {
|
||||||
var protoElementInjector = null;
|
var protoElementInjector = null;
|
||||||
// Create a protoElementInjector for any element that either has bindings *or* has one
|
// Create a protoElementInjector for any element that either has bindings *or* has one
|
||||||
// or more var- defined. Elements with a var- defined need a their own element injector
|
// or more var- defined. Elements with a var- defined need a their own element injector
|
||||||
// so that, when hydrating, $implicit can be set to the element.
|
// so that, when hydrating, $implicit can be set to the element.
|
||||||
var hasVariables = MapWrapper.size(renderElementBinder.variableBindings) > 0;
|
var hasVariables = MapWrapper.size(renderElementBinder.variableBindings) > 0;
|
||||||
if (sortedDirectives.directives.length > 0 || hasVariables) {
|
if (directiveBindings.length > 0 || hasVariables) {
|
||||||
protoElementInjector = new ProtoElementInjector(
|
protoElementInjector = new ProtoElementInjector(
|
||||||
parentPeiWithDistance.protoElementInjector, binderIndex,
|
parentPeiWithDistance.protoElementInjector, binderIndex,
|
||||||
sortedDirectives.directives,
|
directiveBindings,
|
||||||
isPresent(sortedDirectives.componentDirective), parentPeiWithDistance.distance
|
isPresent(componentDirectiveBinding), parentPeiWithDistance.distance
|
||||||
);
|
);
|
||||||
protoElementInjector.attributes = renderElementBinder.readAttributes;
|
protoElementInjector.attributes = renderElementBinder.readAttributes;
|
||||||
if (hasVariables) {
|
if (hasVariables) {
|
||||||
protoElementInjector.exportComponent = isPresent(sortedDirectives.componentDirective);
|
protoElementInjector.exportComponent = isPresent(componentDirectiveBinding);
|
||||||
protoElementInjector.exportElement = isBlank(sortedDirectives.componentDirective);
|
protoElementInjector.exportElement = isBlank(componentDirectiveBinding);
|
||||||
|
|
||||||
// experiment
|
// experiment
|
||||||
var exportImplicitName = MapWrapper.get(renderElementBinder.variableBindings, '\$implicit');
|
var exportImplicitName = MapWrapper.get(renderElementBinder.variableBindings, '\$implicit');
|
||||||
|
@ -232,7 +328,7 @@ export class ProtoViewFactory {
|
||||||
return protoElementInjector;
|
return protoElementInjector;
|
||||||
}
|
}
|
||||||
|
|
||||||
_createElementBinder(protoView, boundElementIndex, renderElementBinder, protoElementInjector, sortedDirectives) {
|
_createElementBinder(protoView, boundElementIndex, renderElementBinder, protoElementInjector, componentDirectiveBinding) {
|
||||||
var parent = null;
|
var parent = null;
|
||||||
if (renderElementBinder.parentIndex !== -1) {
|
if (renderElementBinder.parentIndex !== -1) {
|
||||||
parent = protoView.elementBinders[renderElementBinder.parentIndex];
|
parent = protoView.elementBinders[renderElementBinder.parentIndex];
|
||||||
|
@ -241,7 +337,7 @@ export class ProtoViewFactory {
|
||||||
parent,
|
parent,
|
||||||
renderElementBinder.distanceToParent,
|
renderElementBinder.distanceToParent,
|
||||||
protoElementInjector,
|
protoElementInjector,
|
||||||
sortedDirectives.componentDirective
|
componentDirectiveBinding
|
||||||
);
|
);
|
||||||
protoView.bindEvent(renderElementBinder.eventBindings, boundElementIndex, -1);
|
protoView.bindEvent(renderElementBinder.eventBindings, boundElementIndex, -1);
|
||||||
// variables
|
// variables
|
||||||
|
@ -255,9 +351,9 @@ export class ProtoViewFactory {
|
||||||
return elBinder;
|
return elBinder;
|
||||||
}
|
}
|
||||||
|
|
||||||
_bindDirectiveEvents(protoView, sortedDirectives:List<SortedDirectives>) {
|
_bindDirectiveEvents(protoView, elementBinders:List<renderApi.ElementBinder>) {
|
||||||
for (var boundElementIndex = 0; boundElementIndex < sortedDirectives.length; ++boundElementIndex) {
|
for (var boundElementIndex = 0; boundElementIndex < elementBinders.length; ++boundElementIndex) {
|
||||||
var dirs = sortedDirectives[boundElementIndex].renderDirectives;
|
var dirs = elementBinders[boundElementIndex].directives;
|
||||||
for (var i = 0; i < dirs.length; i++) {
|
for (var i = 0; i < dirs.length; i++) {
|
||||||
var directiveBinder = dirs[i];
|
var directiveBinder = dirs[i];
|
||||||
|
|
||||||
|
@ -268,28 +364,16 @@ export class ProtoViewFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SortedDirectives {
|
class RenderProtoViewWithIndex {
|
||||||
componentDirective: DirectiveBinding;
|
renderProtoView:renderApi.ProtoViewDto;
|
||||||
renderDirectives: List<renderApi.DirectiveBinder>;
|
index:number;
|
||||||
directives: List<DirectiveBinding>;
|
parentIndex:number;
|
||||||
|
boundElementIndex:number;
|
||||||
constructor(renderDirectives, allDirectives) {
|
constructor(renderProtoView:renderApi.ProtoViewDto, index:number, parentIndex:number, boundElementIndex:number) {
|
||||||
this.renderDirectives = [];
|
this.renderProtoView = renderProtoView;
|
||||||
this.directives = [];
|
this.index = index;
|
||||||
this.componentDirective = null;
|
this.parentIndex = parentIndex;
|
||||||
ListWrapper.forEach(renderDirectives, (renderDirectiveBinder) => {
|
this.boundElementIndex = boundElementIndex;
|
||||||
var directiveBinding = allDirectives[renderDirectiveBinder.directiveIndex];
|
|
||||||
if (directiveBinding.annotation instanceof Component) {
|
|
||||||
// component directives need to be the first binding in ElementInjectors!
|
|
||||||
this.componentDirective = directiveBinding;
|
|
||||||
ListWrapper.insert(this.renderDirectives, 0, renderDirectiveBinder);
|
|
||||||
ListWrapper.insert(this.directives, 0, directiveBinding);
|
|
||||||
} else {
|
|
||||||
ListWrapper.push(this.renderDirectives, renderDirectiveBinder);
|
|
||||||
ListWrapper.push(this.directives, directiveBinding);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -168,20 +168,21 @@ export class AppProtoView {
|
||||||
variableBindings: Map;
|
variableBindings: Map;
|
||||||
protoLocals:Map;
|
protoLocals:Map;
|
||||||
bindings:List;
|
bindings:List;
|
||||||
variableNames:List;
|
|
||||||
render:renderApi.RenderProtoViewRef;
|
render:renderApi.RenderProtoViewRef;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
render:renderApi.RenderProtoViewRef,
|
render:renderApi.RenderProtoViewRef,
|
||||||
protoChangeDetector:ProtoChangeDetector,
|
protoChangeDetector:ProtoChangeDetector,
|
||||||
variableBindings:Map,
|
variableBindings:Map) {
|
||||||
protoLocals:Map,
|
|
||||||
variableNames:List) {
|
|
||||||
this.render = render;
|
this.render = render;
|
||||||
this.elementBinders = [];
|
this.elementBinders = [];
|
||||||
this.variableBindings = variableBindings;
|
this.variableBindings = variableBindings;
|
||||||
this.protoLocals = protoLocals;
|
this.protoLocals = MapWrapper.create();
|
||||||
this.variableNames = variableNames;
|
if (isPresent(variableBindings)) {
|
||||||
|
MapWrapper.forEach(variableBindings, (templateName, _) => {
|
||||||
|
MapWrapper.set(this.protoLocals, templateName, null);
|
||||||
|
});
|
||||||
|
}
|
||||||
this.protoChangeDetector = protoChangeDetector;
|
this.protoChangeDetector = protoChangeDetector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,15 +7,15 @@ import * as viewModule from './view';
|
||||||
import * as avmModule from './view_manager';
|
import * as avmModule from './view_manager';
|
||||||
import {Renderer} from 'angular2/src/render/api';
|
import {Renderer} from 'angular2/src/render/api';
|
||||||
import {BindingPropagationConfig, Locals} from 'angular2/change_detection';
|
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';
|
import {RenderViewRef} from 'angular2/src/render/api';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AppViewManagerUtils {
|
export class AppViewManagerUtils {
|
||||||
_metadataReader:DirectiveMetadataReader;
|
_directiveResolver:DirectiveResolver;
|
||||||
|
|
||||||
constructor(metadataReader:DirectiveMetadataReader) {
|
constructor(metadataReader:DirectiveResolver) {
|
||||||
this._metadataReader = metadataReader;
|
this._directiveResolver = metadataReader;
|
||||||
}
|
}
|
||||||
|
|
||||||
getComponentInstance(parentView:viewModule.AppView, boundElementIndex:number):any {
|
getComponentInstance(parentView:viewModule.AppView, boundElementIndex:number):any {
|
||||||
|
@ -171,7 +171,7 @@ export class AppViewManagerUtils {
|
||||||
if (isBlank(injector)) {
|
if (isBlank(injector)) {
|
||||||
injector = elementInjector.getLightDomAppInjector();
|
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);
|
var componentDirective = eli.DirectiveBinding.createFromBinding(componentBinding, annotation);
|
||||||
elementInjector.dynamicallyCreateComponent(componentDirective, injector);
|
elementInjector.dynamicallyCreateComponent(componentDirective, injector);
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,7 +93,7 @@ export class ProtoViewDto {
|
||||||
static get COMPONENT_VIEW_TYPE() { return 1; }
|
static get COMPONENT_VIEW_TYPE() { return 1; }
|
||||||
// A view that is embedded into another View via a <template> element
|
// A view that is embedded into another View via a <template> element
|
||||||
// inside of a component view
|
// inside of a component view
|
||||||
static get EMBEDDED_VIEW_TYPE() { return 1; }
|
static get EMBEDDED_VIEW_TYPE() { return 2; }
|
||||||
|
|
||||||
render: RenderProtoViewRef;
|
render: RenderProtoViewRef;
|
||||||
elementBinders:List<ElementBinder>;
|
elementBinders:List<ElementBinder>;
|
||||||
|
@ -114,6 +114,7 @@ export class DirectiveMetadata {
|
||||||
id:any;
|
id:any;
|
||||||
selector:string;
|
selector:string;
|
||||||
compileChildren:boolean;
|
compileChildren:boolean;
|
||||||
|
events:List<string>;
|
||||||
hostListeners:Map<string, string>;
|
hostListeners:Map<string, string>;
|
||||||
hostProperties:Map<string, string>;
|
hostProperties:Map<string, string>;
|
||||||
hostAttributes:Map<string, string>;
|
hostAttributes:Map<string, string>;
|
||||||
|
@ -121,10 +122,19 @@ export class DirectiveMetadata {
|
||||||
properties:Map<string, string>;
|
properties:Map<string, string>;
|
||||||
readAttributes:List<string>;
|
readAttributes:List<string>;
|
||||||
type:number;
|
type:number;
|
||||||
constructor({id, selector, compileChildren, hostListeners, hostProperties, hostAttributes, hostActions, properties, readAttributes, type}) {
|
callOnDestroy:boolean;
|
||||||
|
callOnChange:boolean;
|
||||||
|
callOnAllChangesDone:boolean;
|
||||||
|
changeDetection: string;
|
||||||
|
constructor({id, selector, compileChildren, events, hostListeners, hostProperties,
|
||||||
|
hostAttributes, hostActions, properties, readAttributes, type,
|
||||||
|
callOnDestroy, callOnChange, callOnAllChangesDone,
|
||||||
|
changeDetection
|
||||||
|
}) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.compileChildren = isPresent(compileChildren) ? compileChildren : true;
|
this.compileChildren = isPresent(compileChildren) ? compileChildren : true;
|
||||||
|
this.events = events;
|
||||||
this.hostListeners = hostListeners;
|
this.hostListeners = hostListeners;
|
||||||
this.hostProperties = hostProperties;
|
this.hostProperties = hostProperties;
|
||||||
this.hostAttributes = hostAttributes;
|
this.hostAttributes = hostAttributes;
|
||||||
|
@ -132,6 +142,10 @@ export class DirectiveMetadata {
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
this.readAttributes = readAttributes;
|
this.readAttributes = readAttributes;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
this.callOnDestroy = callOnDestroy;
|
||||||
|
this.callOnChange = callOnChange;
|
||||||
|
this.callOnAllChangesDone = callOnAllChangesDone;
|
||||||
|
this.changeDetection = changeDetection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import {isPresent} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
import {CompileElement} from './compile_element';
|
import {CompileElement} from './compile_element';
|
||||||
import {CompileControl} from './compile_control';
|
import {CompileControl} from './compile_control';
|
||||||
import {CompileStep} from './compile_step';
|
import {CompileStep} from './compile_step';
|
||||||
import {ProtoViewBuilder} from '../view/proto_view_builder';
|
import {ProtoViewBuilder} from '../view/proto_view_builder';
|
||||||
|
import {ProtoViewDto} from '../../api';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CompilePipeline for executing CompileSteps recursively for
|
* CompilePipeline for executing CompileSteps recursively for
|
||||||
|
@ -16,10 +17,13 @@ export class CompilePipeline {
|
||||||
this._control = new CompileControl(steps);
|
this._control = new CompileControl(steps);
|
||||||
}
|
}
|
||||||
|
|
||||||
process(rootElement, compilationCtxtDescription:string = ''):List {
|
process(rootElement, protoViewType:number = null, compilationCtxtDescription:string = ''):List {
|
||||||
|
if (isBlank(protoViewType)) {
|
||||||
|
protoViewType = ProtoViewDto.COMPONENT_VIEW_TYPE;
|
||||||
|
}
|
||||||
var results = ListWrapper.create();
|
var results = ListWrapper.create();
|
||||||
var rootCompileElement = new CompileElement(rootElement, compilationCtxtDescription);
|
var rootCompileElement = new CompileElement(rootElement, compilationCtxtDescription);
|
||||||
rootCompileElement.inheritedProtoView = new ProtoViewBuilder(rootElement);
|
rootCompileElement.inheritedProtoView = new ProtoViewBuilder(rootElement, protoViewType);
|
||||||
rootCompileElement.isViewRoot = true;
|
rootCompileElement.isViewRoot = true;
|
||||||
this._process(results, null, rootCompileElement,
|
this._process(results, null, rootCompileElement,
|
||||||
compilationCtxtDescription
|
compilationCtxtDescription
|
||||||
|
|
|
@ -29,7 +29,7 @@ export class DomCompiler extends RenderCompiler {
|
||||||
compile(template: ViewDefinition):Promise<ProtoViewDto> {
|
compile(template: ViewDefinition):Promise<ProtoViewDto> {
|
||||||
var tplPromise = this._templateLoader.load(template);
|
var tplPromise = this._templateLoader.load(template);
|
||||||
return PromiseWrapper.then(tplPromise,
|
return PromiseWrapper.then(tplPromise,
|
||||||
(el) => this._compileTemplate(template, el),
|
(el) => this._compileTemplate(template, el, ProtoViewDto.COMPONENT_VIEW_TYPE),
|
||||||
(_) => { throw new BaseException(`Failed to load the template "${template.componentId}"`); }
|
(_) => { throw new BaseException(`Failed to load the template "${template.componentId}"`); }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -42,13 +42,13 @@ export class DomCompiler extends RenderCompiler {
|
||||||
directives: [directiveMetadata]
|
directives: [directiveMetadata]
|
||||||
});
|
});
|
||||||
var element = DOM.createElement(directiveMetadata.selector);
|
var element = DOM.createElement(directiveMetadata.selector);
|
||||||
return this._compileTemplate(hostViewDef, element);
|
return this._compileTemplate(hostViewDef, element, ProtoViewDto.HOST_VIEW_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
_compileTemplate(viewDef: ViewDefinition, tplElement):Promise<ProtoViewDto> {
|
_compileTemplate(viewDef: ViewDefinition, tplElement, protoViewType:number):Promise<ProtoViewDto> {
|
||||||
var subTaskPromises = [];
|
var subTaskPromises = [];
|
||||||
var pipeline = new CompilePipeline(this._stepFactory.createSteps(viewDef, subTaskPromises));
|
var pipeline = new CompilePipeline(this._stepFactory.createSteps(viewDef, subTaskPromises));
|
||||||
var compileElements = pipeline.process(tplElement, viewDef.componentId);
|
var compileElements = pipeline.process(tplElement, protoViewType, viewDef.componentId);
|
||||||
|
|
||||||
var protoView = compileElements[0].inheritedProtoView.build();
|
var protoView = compileElements[0].inheritedProtoView.build();
|
||||||
|
|
||||||
|
|
|
@ -57,9 +57,24 @@ export class DirectiveParser extends CompileStep {
|
||||||
});
|
});
|
||||||
|
|
||||||
var componentDirective;
|
var componentDirective;
|
||||||
|
var foundDirectiveIndices = [];
|
||||||
|
var elementBinder = null;
|
||||||
this._selectorMatcher.match(cssSelector, (selector, directiveIndex) => {
|
this._selectorMatcher.match(cssSelector, (selector, directiveIndex) => {
|
||||||
var elementBinder = current.bindElement();
|
elementBinder = current.bindElement();
|
||||||
|
var directive = this._directives[directiveIndex];
|
||||||
|
if (directive.type === DirectiveMetadata.COMPONENT_TYPE) {
|
||||||
|
// components need to go first, so it is easier to locate them in the result.
|
||||||
|
ListWrapper.insert(foundDirectiveIndices, 0, directiveIndex);
|
||||||
|
if (isPresent(componentDirective)) {
|
||||||
|
throw new BaseException(`Only one component directive is allowed per element - check ${current.elementDescription}`);
|
||||||
|
}
|
||||||
|
componentDirective = directive;
|
||||||
|
elementBinder.setComponentId(directive.id);
|
||||||
|
} else {
|
||||||
|
ListWrapper.push(foundDirectiveIndices, directiveIndex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ListWrapper.forEach(foundDirectiveIndices, (directiveIndex) => {
|
||||||
var directive = this._directives[directiveIndex];
|
var directive = this._directives[directiveIndex];
|
||||||
var directiveBinderBuilder = elementBinder.bindDirective(directiveIndex);
|
var directiveBinderBuilder = elementBinder.bindDirective(directiveIndex);
|
||||||
current.compileChildren = current.compileChildren && directive.compileChildren;
|
current.compileChildren = current.compileChildren && directive.compileChildren;
|
||||||
|
@ -95,13 +110,6 @@ export class DirectiveParser extends CompileStep {
|
||||||
elementBinder.readAttribute(attrName);
|
elementBinder.readAttribute(attrName);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (directive.type === DirectiveMetadata.COMPONENT_TYPE) {
|
|
||||||
if (isPresent(componentDirective)) {
|
|
||||||
throw new BaseException(`Only one component directive is allowed per element - check ${current.elementDescription}`);
|
|
||||||
}
|
|
||||||
componentDirective = directive;
|
|
||||||
elementBinder.setComponentId(directive.id);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,13 @@ export class ProtoViewBuilder {
|
||||||
rootElement;
|
rootElement;
|
||||||
variableBindings: Map<string, string>;
|
variableBindings: Map<string, string>;
|
||||||
elements:List<ElementBinderBuilder>;
|
elements:List<ElementBinderBuilder>;
|
||||||
|
type:number;
|
||||||
|
|
||||||
constructor(rootElement) {
|
constructor(rootElement, type:number) {
|
||||||
this.rootElement = rootElement;
|
this.rootElement = rootElement;
|
||||||
this.elements = [];
|
this.elements = [];
|
||||||
this.variableBindings = MapWrapper.create();
|
this.variableBindings = MapWrapper.create();
|
||||||
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
bindElement(element, description = null):ElementBinderBuilder {
|
bindElement(element, description = null):ElementBinderBuilder {
|
||||||
|
@ -104,6 +106,7 @@ export class ProtoViewBuilder {
|
||||||
element: this.rootElement,
|
element: this.rootElement,
|
||||||
elementBinders: renderElementBinders
|
elementBinders: renderElementBinders
|
||||||
})),
|
})),
|
||||||
|
type: this.type,
|
||||||
elementBinders: apiElementBinders,
|
elementBinders: apiElementBinders,
|
||||||
variableBindings: this.variableBindings
|
variableBindings: this.variableBindings
|
||||||
});
|
});
|
||||||
|
@ -169,7 +172,7 @@ export class ElementBinderBuilder {
|
||||||
if (isPresent(this.nestedProtoView)) {
|
if (isPresent(this.nestedProtoView)) {
|
||||||
throw new BaseException('Only one nested view per element is allowed');
|
throw new BaseException('Only one nested view per element is allowed');
|
||||||
}
|
}
|
||||||
this.nestedProtoView = new ProtoViewBuilder(rootElement);
|
this.nestedProtoView = new ProtoViewBuilder(rootElement, api.ProtoViewDto.EMBEDDED_VIEW_TYPE);
|
||||||
return this.nestedProtoView;
|
return this.nestedProtoView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {Parser, Lexer, ChangeDetection, DynamicChangeDetection,
|
||||||
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
|
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
|
||||||
import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
|
import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
|
||||||
import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver';
|
import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver';
|
||||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||||
import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy';
|
import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy';
|
||||||
import {EmulatedUnscopedShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy';
|
import {EmulatedUnscopedShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy';
|
||||||
|
@ -92,7 +92,7 @@ function _getAppBindings() {
|
||||||
bind(ChangeDetection).toClass(DynamicChangeDetection),
|
bind(ChangeDetection).toClass(DynamicChangeDetection),
|
||||||
TemplateLoader,
|
TemplateLoader,
|
||||||
DynamicComponentLoader,
|
DynamicComponentLoader,
|
||||||
DirectiveMetadataReader,
|
DirectiveResolver,
|
||||||
Parser,
|
Parser,
|
||||||
Lexer,
|
Lexer,
|
||||||
ExceptionHandler,
|
ExceptionHandler,
|
||||||
|
|
|
@ -11,7 +11,6 @@ import {
|
||||||
xit,
|
xit,
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
import {bootstrap} from 'angular2/src/core/application';
|
import {bootstrap} from 'angular2/src/core/application';
|
||||||
import {appComponentAnnotatedTypeToken} from 'angular2/src/core/application_tokens';
|
|
||||||
import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations';
|
import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations';
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
@ -114,7 +113,7 @@ export function main() {
|
||||||
it('should resolve an injector promise and contain bindings', inject([AsyncTestCompleter], (async) => {
|
it('should resolve an injector promise and contain bindings', inject([AsyncTestCompleter], (async) => {
|
||||||
var refPromise = bootstrap(HelloRootCmp, testBindings);
|
var refPromise = bootstrap(HelloRootCmp, testBindings);
|
||||||
refPromise.then((ref) => {
|
refPromise.then((ref) => {
|
||||||
expect(ref.injector.get(appComponentAnnotatedTypeToken).type).toBe(HelloRootCmp);
|
expect(ref.injector.get(HelloRootCmp)).toBeAnInstanceOf(HelloRootCmp);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -20,7 +20,7 @@ import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||||
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
|
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
|
||||||
import {AppProtoView} from 'angular2/src/core/compiler/view';
|
import {AppProtoView} from 'angular2/src/core/compiler/view';
|
||||||
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
||||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations';
|
import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations';
|
||||||
import {Attribute} from 'angular2/src/core/annotations_impl/di';
|
import {Attribute} from 'angular2/src/core/annotations_impl/di';
|
||||||
import {View} from 'angular2/src/core/annotations_impl/view';
|
import {View} from 'angular2/src/core/annotations_impl/view';
|
||||||
|
@ -37,10 +37,10 @@ import {RenderCompiler} from 'angular2/src/render/api';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('compiler', function() {
|
describe('compiler', function() {
|
||||||
var reader, tplResolver, renderCompiler, protoViewFactory, cmpUrlMapper, renderCompileRequests;
|
var directiveResolver, tplResolver, renderCompiler, protoViewFactory, cmpUrlMapper, renderCompileRequests;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
reader = new DirectiveMetadataReader();
|
directiveResolver = new DirectiveResolver();
|
||||||
tplResolver = new FakeTemplateResolver();
|
tplResolver = new FakeTemplateResolver();
|
||||||
cmpUrlMapper = new RuntimeComponentUrlMapper();
|
cmpUrlMapper = new RuntimeComponentUrlMapper();
|
||||||
renderCompiler = new SpyRenderCompiler();
|
renderCompiler = new SpyRenderCompiler();
|
||||||
|
@ -56,7 +56,7 @@ export function main() {
|
||||||
|
|
||||||
protoViewFactory = new FakeProtoViewFactory(protoViewFactoryResults)
|
protoViewFactory = new FakeProtoViewFactory(protoViewFactoryResults)
|
||||||
return new Compiler(
|
return new Compiler(
|
||||||
reader,
|
directiveResolver,
|
||||||
new CompilerCache(),
|
new CompilerCache(),
|
||||||
tplResolver,
|
tplResolver,
|
||||||
cmpUrlMapper,
|
cmpUrlMapper,
|
||||||
|
@ -70,7 +70,7 @@ export function main() {
|
||||||
|
|
||||||
function captureTemplate(template:View):Promise<renderApi.ViewDefinition> {
|
function captureTemplate(template:View):Promise<renderApi.ViewDefinition> {
|
||||||
tplResolver.setView(MainComponent, template);
|
tplResolver.setView(MainComponent, template);
|
||||||
var compiler = createCompiler([createRenderProtoView()], [createProtoView()]);
|
var compiler = createCompiler([createRenderProtoView()], [[createProtoView()]]);
|
||||||
return compiler.compile(MainComponent).then( (_) => {
|
return compiler.compile(MainComponent).then( (_) => {
|
||||||
expect(renderCompileRequests.length).toBe(1);
|
expect(renderCompileRequests.length).toBe(1);
|
||||||
return renderCompileRequests[0];
|
return renderCompileRequests[0];
|
||||||
|
@ -219,7 +219,7 @@ export function main() {
|
||||||
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
||||||
var renderProtoView = createRenderProtoView();
|
var renderProtoView = createRenderProtoView();
|
||||||
var expectedProtoView = createProtoView();
|
var expectedProtoView = createProtoView();
|
||||||
var compiler = createCompiler([renderProtoView], [expectedProtoView]);
|
var compiler = createCompiler([renderProtoView], [[expectedProtoView]]);
|
||||||
compiler.compile(MainComponent).then( (_) => {
|
compiler.compile(MainComponent).then( (_) => {
|
||||||
var request = protoViewFactory.requests[0];
|
var request = protoViewFactory.requests[0];
|
||||||
expect(request[1]).toBe(renderProtoView);
|
expect(request[1]).toBe(renderProtoView);
|
||||||
|
@ -229,7 +229,7 @@ export function main() {
|
||||||
|
|
||||||
it('should pass the component binding', inject([AsyncTestCompleter], (async) => {
|
it('should pass the component binding', inject([AsyncTestCompleter], (async) => {
|
||||||
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
||||||
var compiler = createCompiler([createRenderProtoView()], [createProtoView()]);
|
var compiler = createCompiler([createRenderProtoView()], [[createProtoView()]]);
|
||||||
compiler.compile(MainComponent).then( (_) => {
|
compiler.compile(MainComponent).then( (_) => {
|
||||||
var request = protoViewFactory.requests[0];
|
var request = protoViewFactory.requests[0];
|
||||||
expect(request[0].key.token).toBe(MainComponent);
|
expect(request[0].key.token).toBe(MainComponent);
|
||||||
|
@ -244,7 +244,7 @@ export function main() {
|
||||||
directives: [SomeDirective]
|
directives: [SomeDirective]
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
var compiler = createCompiler([createRenderProtoView()], [createProtoView()]);
|
var compiler = createCompiler([createRenderProtoView()], [[createProtoView()]]);
|
||||||
compiler.compile(MainComponent).then( (_) => {
|
compiler.compile(MainComponent).then( (_) => {
|
||||||
var request = protoViewFactory.requests[0];
|
var request = protoViewFactory.requests[0];
|
||||||
var binding = request[2][0];
|
var binding = request[2][0];
|
||||||
|
@ -257,7 +257,7 @@ export function main() {
|
||||||
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
||||||
var renderProtoView = createRenderProtoView();
|
var renderProtoView = createRenderProtoView();
|
||||||
var expectedProtoView = createProtoView();
|
var expectedProtoView = createProtoView();
|
||||||
var compiler = createCompiler([renderProtoView], [expectedProtoView]);
|
var compiler = createCompiler([renderProtoView], [[expectedProtoView]]);
|
||||||
compiler.compile(MainComponent).then( (protoViewRef) => {
|
compiler.compile(MainComponent).then( (protoViewRef) => {
|
||||||
expect(internalProtoView(protoViewRef)).toBe(expectedProtoView);
|
expect(internalProtoView(protoViewRef)).toBe(expectedProtoView);
|
||||||
async.done();
|
async.done();
|
||||||
|
@ -270,7 +270,7 @@ export function main() {
|
||||||
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
||||||
tplResolver.setView(NestedComponent, new View({template: '<div></div>'}));
|
tplResolver.setView(NestedComponent, new View({template: '<div></div>'}));
|
||||||
var mainProtoView = createProtoView([
|
var mainProtoView = createProtoView([
|
||||||
createComponentElementBinder(reader, NestedComponent)
|
createComponentElementBinder(directiveResolver, NestedComponent)
|
||||||
]);
|
]);
|
||||||
var nestedProtoView = createProtoView();
|
var nestedProtoView = createProtoView();
|
||||||
var compiler = createCompiler(
|
var compiler = createCompiler(
|
||||||
|
@ -278,7 +278,7 @@ export function main() {
|
||||||
createRenderProtoView([createRenderComponentElementBinder(0)]),
|
createRenderProtoView([createRenderComponentElementBinder(0)]),
|
||||||
createRenderProtoView()
|
createRenderProtoView()
|
||||||
],
|
],
|
||||||
[mainProtoView, nestedProtoView]
|
[[mainProtoView], [nestedProtoView]]
|
||||||
);
|
);
|
||||||
compiler.compile(MainComponent).then( (protoViewRef) => {
|
compiler.compile(MainComponent).then( (protoViewRef) => {
|
||||||
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
|
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
|
||||||
|
@ -294,7 +294,7 @@ export function main() {
|
||||||
createViewportElementBinder(null)
|
createViewportElementBinder(null)
|
||||||
]);
|
]);
|
||||||
var viewportProtoView = createProtoView([
|
var viewportProtoView = createProtoView([
|
||||||
createComponentElementBinder(reader, NestedComponent)
|
createComponentElementBinder(directiveResolver, NestedComponent)
|
||||||
]);
|
]);
|
||||||
var nestedProtoView = createProtoView();
|
var nestedProtoView = createProtoView();
|
||||||
var compiler = createCompiler(
|
var compiler = createCompiler(
|
||||||
|
@ -303,16 +303,15 @@ export function main() {
|
||||||
createRenderViewportElementBinder(
|
createRenderViewportElementBinder(
|
||||||
createRenderProtoView([
|
createRenderProtoView([
|
||||||
createRenderComponentElementBinder(0)
|
createRenderComponentElementBinder(0)
|
||||||
])
|
], renderApi.ProtoViewDto.EMBEDDED_VIEW_TYPE)
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
createRenderProtoView()
|
createRenderProtoView()
|
||||||
],
|
],
|
||||||
[mainProtoView, viewportProtoView, nestedProtoView]
|
[[mainProtoView, viewportProtoView], [nestedProtoView]]
|
||||||
);
|
);
|
||||||
compiler.compile(MainComponent).then( (protoViewRef) => {
|
compiler.compile(MainComponent).then( (protoViewRef) => {
|
||||||
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
|
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
|
||||||
expect(mainProtoView.elementBinders[0].nestedProtoView).toBe(viewportProtoView);
|
|
||||||
expect(viewportProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
|
expect(viewportProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
|
||||||
|
|
||||||
async.done();
|
async.done();
|
||||||
|
@ -323,7 +322,7 @@ export function main() {
|
||||||
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
||||||
var renderProtoView = createRenderProtoView();
|
var renderProtoView = createRenderProtoView();
|
||||||
var expectedProtoView = createProtoView();
|
var expectedProtoView = createProtoView();
|
||||||
var compiler = createCompiler([renderProtoView], [expectedProtoView]);
|
var compiler = createCompiler([renderProtoView], [[expectedProtoView]]);
|
||||||
compiler.compile(MainComponent).then( (protoViewRef) => {
|
compiler.compile(MainComponent).then( (protoViewRef) => {
|
||||||
expect(internalProtoView(protoViewRef)).toBe(expectedProtoView);
|
expect(internalProtoView(protoViewRef)).toBe(expectedProtoView);
|
||||||
return compiler.compile(MainComponent);
|
return compiler.compile(MainComponent);
|
||||||
|
@ -337,7 +336,7 @@ export function main() {
|
||||||
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
||||||
var renderProtoViewCompleter = PromiseWrapper.completer();
|
var renderProtoViewCompleter = PromiseWrapper.completer();
|
||||||
var expectedProtoView = createProtoView();
|
var expectedProtoView = createProtoView();
|
||||||
var compiler = createCompiler([renderProtoViewCompleter.promise], [expectedProtoView]);
|
var compiler = createCompiler([renderProtoViewCompleter.promise], [[expectedProtoView]]);
|
||||||
renderProtoViewCompleter.resolve(createRenderProtoView());
|
renderProtoViewCompleter.resolve(createRenderProtoView());
|
||||||
PromiseWrapper.all([
|
PromiseWrapper.all([
|
||||||
compiler.compile(MainComponent),
|
compiler.compile(MainComponent),
|
||||||
|
@ -352,13 +351,13 @@ export function main() {
|
||||||
it('should allow recursive components', inject([AsyncTestCompleter], (async) => {
|
it('should allow recursive components', inject([AsyncTestCompleter], (async) => {
|
||||||
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
||||||
var mainProtoView = createProtoView([
|
var mainProtoView = createProtoView([
|
||||||
createComponentElementBinder(reader, MainComponent)
|
createComponentElementBinder(directiveResolver, MainComponent)
|
||||||
]);
|
]);
|
||||||
var compiler = createCompiler(
|
var compiler = createCompiler(
|
||||||
[createRenderProtoView([
|
[createRenderProtoView([
|
||||||
createRenderComponentElementBinder(0)
|
createRenderComponentElementBinder(0)
|
||||||
])],
|
])],
|
||||||
[mainProtoView]
|
[[mainProtoView]]
|
||||||
);
|
);
|
||||||
compiler.compile(MainComponent).then( (protoViewRef) => {
|
compiler.compile(MainComponent).then( (protoViewRef) => {
|
||||||
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
|
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
|
||||||
|
@ -370,19 +369,19 @@ export function main() {
|
||||||
it('should create host proto views', inject([AsyncTestCompleter], (async) => {
|
it('should create host proto views', inject([AsyncTestCompleter], (async) => {
|
||||||
renderCompiler.spy('compileHost').andCallFake( (componentId) => {
|
renderCompiler.spy('compileHost').andCallFake( (componentId) => {
|
||||||
return PromiseWrapper.resolve(
|
return PromiseWrapper.resolve(
|
||||||
createRenderProtoView([createRenderComponentElementBinder(0)])
|
createRenderProtoView([createRenderComponentElementBinder(0)], renderApi.ProtoViewDto.HOST_VIEW_TYPE)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
||||||
var rootProtoView = createProtoView([
|
var rootProtoView = createProtoView([
|
||||||
createComponentElementBinder(reader, MainComponent)
|
createComponentElementBinder(directiveResolver, MainComponent)
|
||||||
]);
|
]);
|
||||||
var mainProtoView = createProtoView();
|
var mainProtoView = createProtoView();
|
||||||
var compiler = createCompiler(
|
var compiler = createCompiler(
|
||||||
[
|
[
|
||||||
createRenderProtoView()
|
createRenderProtoView()
|
||||||
],
|
],
|
||||||
[rootProtoView, mainProtoView]
|
[[rootProtoView], [mainProtoView]]
|
||||||
);
|
);
|
||||||
compiler.compileInHost(MainComponent).then( (protoViewRef) => {
|
compiler.compileInHost(MainComponent).then( (protoViewRef) => {
|
||||||
expect(internalProtoView(protoViewRef)).toBe(rootProtoView);
|
expect(internalProtoView(protoViewRef)).toBe(rootProtoView);
|
||||||
|
@ -400,13 +399,13 @@ export function main() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDirectiveBinding(reader, type) {
|
function createDirectiveBinding(directiveResolver, type) {
|
||||||
var meta = reader.read(type);
|
var annotation = directiveResolver.resolve(type);
|
||||||
return DirectiveBinding.createFromType(meta.type, meta.annotation);
|
return DirectiveBinding.createFromType(type, annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createProtoView(elementBinders = null) {
|
function createProtoView(elementBinders = null) {
|
||||||
var pv = new AppProtoView(null, null, null, null, null);
|
var pv = new AppProtoView(null, null, MapWrapper.create());
|
||||||
if (isBlank(elementBinders)) {
|
if (isBlank(elementBinders)) {
|
||||||
elementBinders = [];
|
elementBinders = [];
|
||||||
}
|
}
|
||||||
|
@ -414,8 +413,8 @@ function createProtoView(elementBinders = null) {
|
||||||
return pv;
|
return pv;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createComponentElementBinder(reader, type) {
|
function createComponentElementBinder(directiveResolver, type) {
|
||||||
var binding = createDirectiveBinding(reader, type);
|
var binding = createDirectiveBinding(directiveResolver, type);
|
||||||
return new ElementBinder(
|
return new ElementBinder(
|
||||||
0, null, 0,
|
0, null, 0,
|
||||||
null, binding
|
null, binding
|
||||||
|
@ -431,12 +430,16 @@ function createViewportElementBinder(nestedProtoView) {
|
||||||
return elBinder;
|
return elBinder;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createRenderProtoView(elementBinders = null) {
|
function createRenderProtoView(elementBinders = null, type:number = null) {
|
||||||
|
if (isBlank(type)) {
|
||||||
|
type = renderApi.ProtoViewDto.COMPONENT_VIEW_TYPE;
|
||||||
|
}
|
||||||
if (isBlank(elementBinders)) {
|
if (isBlank(elementBinders)) {
|
||||||
elementBinders = [];
|
elementBinders = [];
|
||||||
}
|
}
|
||||||
return new renderApi.ProtoViewDto({
|
return new renderApi.ProtoViewDto({
|
||||||
elementBinders: elementBinders
|
elementBinders: elementBinders,
|
||||||
|
type: type
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -549,8 +552,8 @@ class FakeProtoViewFactory extends ProtoViewFactory {
|
||||||
this._results = results;
|
this._results = results;
|
||||||
}
|
}
|
||||||
|
|
||||||
createProtoView(parentProtoView, componentBinding:DirectiveBinding, renderProtoView: renderApi.ProtoViewDto,
|
createAppProtoViews(componentBinding:DirectiveBinding, renderProtoView: renderApi.ProtoViewDto,
|
||||||
directives:List<DirectiveBinding>):AppProtoView {
|
directives:List<DirectiveBinding>):List<AppProtoView> {
|
||||||
ListWrapper.push(this.requests, [componentBinding, renderProtoView, directives]);
|
ListWrapper.push(this.requests, [componentBinding, renderProtoView, directives]);
|
||||||
return ListWrapper.removeAt(this._results, 0);
|
return ListWrapper.removeAt(this._results, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,57 +1,29 @@
|
||||||
import {isPresent} from 'angular2/src/facade/lang';
|
|
||||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
|
||||||
import {ddescribe, describe, it, iit, expect, beforeEach} from 'angular2/test_lib';
|
import {ddescribe, describe, it, iit, expect, beforeEach} from 'angular2/test_lib';
|
||||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
import {Directive, Component} from 'angular2/src/core/annotations_impl/annotations';
|
import {Directive} from 'angular2/src/core/annotations_impl/annotations';
|
||||||
import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata';
|
|
||||||
import {Injectable, Injector} from 'angular2/di';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
class SomeInjectable {}
|
|
||||||
|
|
||||||
@Directive({selector: 'someDirective'})
|
@Directive({selector: 'someDirective'})
|
||||||
class SomeDirective {}
|
class SomeDirective {}
|
||||||
|
|
||||||
@Component({selector: 'someComponent', injectables: [SomeInjectable]})
|
|
||||||
class SomeComponent {}
|
|
||||||
|
|
||||||
class SomeDirectiveWithoutAnnotation {
|
class SomeDirectiveWithoutAnnotation {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe("DirectiveMetadataReader", () => {
|
describe("DirectiveResolver", () => {
|
||||||
var reader;
|
var reader;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
reader = new DirectiveMetadataReader();
|
reader = new DirectiveResolver();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should read out the Directive annotation', () => {
|
it('should read out the Directive annotation', () => {
|
||||||
var directiveMetadata = reader.read(SomeDirective);
|
var directiveMetadata = reader.resolve(SomeDirective);
|
||||||
expect(directiveMetadata).toEqual(
|
expect(directiveMetadata).toEqual(new Directive({selector: 'someDirective'}));
|
||||||
new DirectiveMetadata(SomeDirective, new Directive({selector: 'someDirective'}), null));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should read out the Component annotation', () => {
|
|
||||||
var m = reader.read(SomeComponent);
|
|
||||||
// For some reason `toEqual` fails to compare ResolvedBinding objects.
|
|
||||||
// Have to decompose and compare.
|
|
||||||
expect(m.type).toEqual(SomeComponent);
|
|
||||||
expect(m.annotation)
|
|
||||||
.toEqual(new Component({selector: 'someComponent', injectables: [SomeInjectable]}));
|
|
||||||
var resolvedList = ListWrapper.reduce(m.resolvedInjectables, function(prev, elem) {
|
|
||||||
if (isPresent(elem)) {
|
|
||||||
ListWrapper.push(prev, elem);
|
|
||||||
}
|
|
||||||
return prev;
|
|
||||||
}, []);
|
|
||||||
expect(resolvedList.length).toBe(1);
|
|
||||||
expect(resolvedList[0].key.token).toBe(SomeInjectable);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if not matching annotation is found', () => {
|
it('should throw if not matching annotation is found', () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
reader.read(SomeDirectiveWithoutAnnotation);
|
reader.resolve(SomeDirectiveWithoutAnnotation);
|
||||||
}).toThrowError('No Directive annotation found on SomeDirectiveWithoutAnnotation');
|
}).toThrowError('No Directive annotation found on SomeDirectiveWithoutAnnotation');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -506,7 +506,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should instantiate directives that depend on pre built objects", function () {
|
it("should instantiate directives that depend on pre built objects", function () {
|
||||||
var protoView = new AppProtoView(null, null, null, null, null);
|
var protoView = new AppProtoView(null, null, null);
|
||||||
var inj = injector([NeedsProtoViewRef], null, false, new PreBuiltObjects(null, null, protoView));
|
var inj = injector([NeedsProtoViewRef], null, false, new PreBuiltObjects(null, null, protoView));
|
||||||
|
|
||||||
expect(inj.get(NeedsProtoViewRef).protoViewRef).toEqual(new ProtoViewRef(protoView));
|
expect(inj.get(NeedsProtoViewRef).protoViewRef).toEqual(new ProtoViewRef(protoView));
|
||||||
|
@ -800,7 +800,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should inject ProtoViewRef", function () {
|
it("should inject ProtoViewRef", function () {
|
||||||
var protoView = new AppProtoView(null, null, null, null, null);
|
var protoView = new AppProtoView(null, null, null);
|
||||||
var inj = injector([NeedsProtoViewRef], null, false, new PreBuiltObjects(null, null, protoView));
|
var inj = injector([NeedsProtoViewRef], null, false, new PreBuiltObjects(null, null, protoView));
|
||||||
|
|
||||||
expect(inj.get(NeedsProtoViewRef).protoViewRef).toEqual(new ProtoViewRef(protoView));
|
expect(inj.get(NeedsProtoViewRef).protoViewRef).toEqual(new ProtoViewRef(protoView));
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
import {
|
||||||
|
AsyncTestCompleter,
|
||||||
|
beforeEach,
|
||||||
|
xdescribe,
|
||||||
|
ddescribe,
|
||||||
|
describe,
|
||||||
|
el,
|
||||||
|
expect,
|
||||||
|
iit,
|
||||||
|
inject,
|
||||||
|
IS_DARTIUM,
|
||||||
|
it,
|
||||||
|
SpyObject, proxy
|
||||||
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
|
import {isBlank} from 'angular2/src/facade/lang';
|
||||||
|
import {MapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
|
import {ChangeDetection, ChangeDetectorDefinition} from 'angular2/change_detection';
|
||||||
|
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
|
||||||
|
import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations';
|
||||||
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
|
import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
|
||||||
|
import * as renderApi from 'angular2/src/render/api';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
// TODO(tbosch): add missing tests
|
||||||
|
|
||||||
|
describe('ProtoViewFactory', () => {
|
||||||
|
var changeDetection;
|
||||||
|
var protoViewFactory;
|
||||||
|
var directiveResolver;
|
||||||
|
|
||||||
|
beforeEach( () => {
|
||||||
|
directiveResolver = new DirectiveResolver();
|
||||||
|
changeDetection = new ChangeDetectionSpy();
|
||||||
|
protoViewFactory = new ProtoViewFactory(changeDetection);
|
||||||
|
});
|
||||||
|
|
||||||
|
function bindDirective(type) {
|
||||||
|
return DirectiveBinding.createFromType(type, directiveResolver.resolve(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('getChangeDetectorDefinitions', () => {
|
||||||
|
|
||||||
|
it('should create a ChangeDetectorDefinition for the root render proto view', () => {
|
||||||
|
var renderPv = createRenderProtoView();
|
||||||
|
var defs = protoViewFactory.getChangeDetectorDefinitions(bindDirective(MainComponent).metadata,
|
||||||
|
renderPv, []);
|
||||||
|
expect(defs.length).toBe(1);
|
||||||
|
expect(defs[0].id).toEqual('MainComponent_comp_0');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createAppProtoViews', () => {
|
||||||
|
|
||||||
|
it('should create an AppProtoView for the root render proto view', () => {
|
||||||
|
var renderPv = createRenderProtoView();
|
||||||
|
var pvs = protoViewFactory.createAppProtoViews(bindDirective(MainComponent),
|
||||||
|
renderPv, []);
|
||||||
|
expect(pvs.length).toBe(1);
|
||||||
|
expect(pvs[0].render).toBe(renderPv.render);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRenderProtoView(elementBinders = null, type:number = null) {
|
||||||
|
if (isBlank(type)) {
|
||||||
|
type = renderApi.ProtoViewDto.COMPONENT_VIEW_TYPE;
|
||||||
|
}
|
||||||
|
if (isBlank(elementBinders)) {
|
||||||
|
elementBinders = [];
|
||||||
|
}
|
||||||
|
return new renderApi.ProtoViewDto({
|
||||||
|
elementBinders: elementBinders,
|
||||||
|
type: type,
|
||||||
|
variableBindings: MapWrapper.create()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRenderComponentElementBinder(directiveIndex) {
|
||||||
|
return new renderApi.ElementBinder({
|
||||||
|
directives: [new renderApi.DirectiveBinder({
|
||||||
|
directiveIndex: directiveIndex
|
||||||
|
})]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRenderViewportElementBinder(nestedProtoView) {
|
||||||
|
return new renderApi.ElementBinder({
|
||||||
|
nestedProtoView: nestedProtoView
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
@IMPLEMENTS(ChangeDetection)
|
||||||
|
class ChangeDetectionSpy extends SpyObject {
|
||||||
|
constructor(){super(ChangeDetection);}
|
||||||
|
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'main-comp'
|
||||||
|
})
|
||||||
|
class MainComponent {}
|
|
@ -37,7 +37,7 @@ export function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createProtoView() {
|
function createProtoView() {
|
||||||
return new AppProtoView(null, null, null, null, null);
|
return new AppProtoView(null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createView() {
|
function createView() {
|
||||||
|
|
|
@ -24,7 +24,7 @@ import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
||||||
import {Renderer, RenderViewRef, RenderProtoViewRef, RenderViewContainerRef} from 'angular2/src/render/api';
|
import {Renderer, RenderViewRef, RenderProtoViewRef, RenderViewContainerRef} from 'angular2/src/render/api';
|
||||||
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
||||||
import {DirectiveBinding, ElementInjector} from 'angular2/src/core/compiler/element_injector';
|
import {DirectiveBinding, ElementInjector} from 'angular2/src/core/compiler/element_injector';
|
||||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
import {Component} from 'angular2/src/core/annotations_impl/annotations';
|
import {Component} from 'angular2/src/core/annotations_impl/annotations';
|
||||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||||
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||||
|
@ -38,7 +38,7 @@ export function main() {
|
||||||
var utils;
|
var utils;
|
||||||
var viewPool;
|
var viewPool;
|
||||||
var manager;
|
var manager;
|
||||||
var reader;
|
var directiveResolver;
|
||||||
var createdViews;
|
var createdViews;
|
||||||
var createdRenderViews;
|
var createdRenderViews;
|
||||||
|
|
||||||
|
@ -55,8 +55,8 @@ export function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDirectiveBinding(type) {
|
function createDirectiveBinding(type) {
|
||||||
var meta = reader.read(type);
|
var annotation = directiveResolver.resolve(type);
|
||||||
return DirectiveBinding.createFromType(meta.type, meta.annotation);
|
return DirectiveBinding.createFromType(type, annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEmptyElBinder() {
|
function createEmptyElBinder() {
|
||||||
|
@ -80,7 +80,7 @@ export function main() {
|
||||||
staticChildComponentCount++;
|
staticChildComponentCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var res = new AppProtoView(new MockProtoViewRef(staticChildComponentCount), null, null, null, null);
|
var res = new AppProtoView(new MockProtoViewRef(staticChildComponentCount), null, null);
|
||||||
res.elementBinders = binders;
|
res.elementBinders = binders;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@ export function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach( () => {
|
beforeEach( () => {
|
||||||
reader = new DirectiveMetadataReader();
|
directiveResolver = new DirectiveResolver();
|
||||||
renderer = new SpyRenderer();
|
renderer = new SpyRenderer();
|
||||||
utils = new SpyAppViewManagerUtils();
|
utils = new SpyAppViewManagerUtils();
|
||||||
viewPool = new SpyAppViewPool();
|
viewPool = new SpyAppViewPool();
|
||||||
|
|
|
@ -24,7 +24,7 @@ import {AppProtoView, AppView} from 'angular2/src/core/compiler/view';
|
||||||
import {ChangeDetector} from 'angular2/change_detection';
|
import {ChangeDetector} from 'angular2/change_detection';
|
||||||
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
||||||
import {DirectiveBinding, ElementInjector, PreBuiltObjects} from 'angular2/src/core/compiler/element_injector';
|
import {DirectiveBinding, ElementInjector, PreBuiltObjects} from 'angular2/src/core/compiler/element_injector';
|
||||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
import {Component} from 'angular2/src/core/annotations_impl/annotations';
|
import {Component} from 'angular2/src/core/annotations_impl/annotations';
|
||||||
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ export function main() {
|
||||||
|
|
||||||
describe('AppViewManagerUtils', () => {
|
describe('AppViewManagerUtils', () => {
|
||||||
|
|
||||||
var metadataReader;
|
var directiveResolver;
|
||||||
var utils;
|
var utils;
|
||||||
|
|
||||||
function createInjector() {
|
function createInjector() {
|
||||||
|
@ -42,8 +42,8 @@ export function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDirectiveBinding(type) {
|
function createDirectiveBinding(type) {
|
||||||
var meta = metadataReader.read(type);
|
var annotation = directiveResolver.resolve(type);
|
||||||
return DirectiveBinding.createFromType(meta.type, meta.annotation);
|
return DirectiveBinding.createFromType(type, annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEmptyElBinder() {
|
function createEmptyElBinder() {
|
||||||
|
@ -61,7 +61,7 @@ export function main() {
|
||||||
if (isBlank(binders)) {
|
if (isBlank(binders)) {
|
||||||
binders = [];
|
binders = [];
|
||||||
}
|
}
|
||||||
var res = new AppProtoView(null, null, null, null, null);
|
var res = new AppProtoView(null, null, null);
|
||||||
res.elementBinders = binders;
|
res.elementBinders = binders;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -100,8 +100,8 @@ export function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach( () => {
|
beforeEach( () => {
|
||||||
metadataReader = new DirectiveMetadataReader();
|
directiveResolver = new DirectiveResolver();
|
||||||
utils = new AppViewManagerUtils(metadataReader);
|
utils = new AppViewManagerUtils(directiveResolver);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('hydrateDynamicComponentInElementInjector', () => {
|
describe('hydrateDynamicComponentInElementInjector', () => {
|
||||||
|
|
|
@ -26,7 +26,7 @@ export function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createProtoView() {
|
function createProtoView() {
|
||||||
return new AppProtoView(null, null, null, null, null);
|
return new AppProtoView(null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createView(pv) {
|
function createView(pv) {
|
||||||
|
|
|
@ -41,99 +41,126 @@ export function runCompilerCommonTests() {
|
||||||
return new DomCompiler(mockStepFactory, tplLoader);
|
return new DomCompiler(mockStepFactory, tplLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should run the steps and build the AppProtoView of the root element', inject([AsyncTestCompleter], (async) => {
|
describe('compile', () => {
|
||||||
var compiler = createCompiler((parent, current, control) => {
|
|
||||||
current.inheritedProtoView.bindVariable('b', 'a');
|
|
||||||
});
|
|
||||||
compiler.compile(new ViewDefinition({
|
|
||||||
componentId: 'someComponent',
|
|
||||||
template: '<div></div>'
|
|
||||||
})).then( (protoView) => {
|
|
||||||
expect(protoView.variableBindings).toEqual(MapWrapper.createFromStringMap({
|
|
||||||
'a': 'b'
|
|
||||||
}));
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should run the steps and build the proto view', inject([AsyncTestCompleter], (async) => {
|
it('should run the steps and build the AppProtoView of the root element', inject([AsyncTestCompleter], (async) => {
|
||||||
var compiler = createCompiler((parent, current, control) => {
|
var compiler = createCompiler((parent, current, control) => {
|
||||||
current.inheritedProtoView.bindVariable('b', 'a');
|
current.inheritedProtoView.bindVariable('b', 'a');
|
||||||
});
|
});
|
||||||
|
compiler.compile(new ViewDefinition({
|
||||||
var dirMetadata = new DirectiveMetadata({id: 'id', selector: 'CUSTOM', type: DirectiveMetadata.COMPONENT_TYPE});
|
componentId: 'someComponent',
|
||||||
compiler.compileHost(dirMetadata).then( (protoView) => {
|
template: '<div></div>'
|
||||||
expect(DOM.tagName(resolveInternalDomProtoView(protoView.render).element)).toEqual('CUSTOM')
|
})).then( (protoView) => {
|
||||||
expect(mockStepFactory.viewDef.directives).toEqual([dirMetadata]);
|
expect(protoView.variableBindings).toEqual(MapWrapper.createFromStringMap({
|
||||||
expect(protoView.variableBindings).toEqual(MapWrapper.createFromStringMap({
|
'a': 'b'
|
||||||
'a': 'b'
|
}));
|
||||||
}));
|
async.done();
|
||||||
async.done();
|
});
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should use the inline template and compile in sync', inject([AsyncTestCompleter], (async) => {
|
|
||||||
var compiler = createCompiler(EMPTY_STEP);
|
|
||||||
compiler.compile(new ViewDefinition({
|
|
||||||
componentId: 'someId',
|
|
||||||
template: 'inline component'
|
|
||||||
})).then( (protoView) => {
|
|
||||||
expect(DOM.getInnerHTML(resolveInternalDomProtoView(protoView.render).element)).toEqual('inline component');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should load url templates', inject([AsyncTestCompleter], (async) => {
|
|
||||||
var urlData = MapWrapper.createFromStringMap({
|
|
||||||
'someUrl': 'url component'
|
|
||||||
});
|
|
||||||
var compiler = createCompiler(EMPTY_STEP, urlData);
|
|
||||||
compiler.compile(new ViewDefinition({
|
|
||||||
componentId: 'someId',
|
|
||||||
absUrl: 'someUrl'
|
|
||||||
})).then( (protoView) => {
|
|
||||||
expect(DOM.getInnerHTML(resolveInternalDomProtoView(protoView.render).element)).toEqual('url component');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should report loading errors', inject([AsyncTestCompleter], (async) => {
|
|
||||||
var compiler = createCompiler(EMPTY_STEP, MapWrapper.create());
|
|
||||||
PromiseWrapper.catchError(compiler.compile(new ViewDefinition({
|
|
||||||
componentId: 'someId',
|
|
||||||
absUrl: 'someUrl'
|
|
||||||
})), (e) => {
|
|
||||||
expect(e.message).toContain(`Failed to load the template "someId"`);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should wait for async subtasks to be resolved', inject([AsyncTestCompleter], (async) => {
|
|
||||||
var subTasksCompleted = false;
|
|
||||||
|
|
||||||
var completer = PromiseWrapper.completer();
|
|
||||||
|
|
||||||
var compiler = createCompiler( (parent, current, control) => {
|
|
||||||
ListWrapper.push(mockStepFactory.subTaskPromises, completer.promise.then((_) => {
|
|
||||||
subTasksCompleted = true;
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
// It should always return a Promise because the subtask is async
|
|
||||||
var pvPromise = compiler.compile(new ViewDefinition({
|
|
||||||
componentId: 'someId',
|
|
||||||
template: 'some component'
|
|
||||||
}));
|
}));
|
||||||
expect(pvPromise).toBePromise();
|
|
||||||
expect(subTasksCompleted).toEqual(false);
|
|
||||||
|
|
||||||
// The Promise should resolve after the subtask is ready
|
it('should run the steps and build the proto view', inject([AsyncTestCompleter], (async) => {
|
||||||
completer.resolve(null);
|
var compiler = createCompiler((parent, current, control) => {
|
||||||
pvPromise.then((protoView) => {
|
current.inheritedProtoView.bindVariable('b', 'a');
|
||||||
expect(subTasksCompleted).toEqual(true);
|
});
|
||||||
async.done();
|
|
||||||
});
|
var dirMetadata = new DirectiveMetadata({id: 'id', selector: 'CUSTOM', type: DirectiveMetadata.COMPONENT_TYPE});
|
||||||
}));
|
compiler.compileHost(dirMetadata).then( (protoView) => {
|
||||||
|
expect(DOM.tagName(resolveInternalDomProtoView(protoView.render).element)).toEqual('CUSTOM')
|
||||||
|
expect(mockStepFactory.viewDef.directives).toEqual([dirMetadata]);
|
||||||
|
expect(protoView.variableBindings).toEqual(MapWrapper.createFromStringMap({
|
||||||
|
'a': 'b'
|
||||||
|
}));
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should use the inline template and compile in sync', inject([AsyncTestCompleter], (async) => {
|
||||||
|
var compiler = createCompiler(EMPTY_STEP);
|
||||||
|
compiler.compile(new ViewDefinition({
|
||||||
|
componentId: 'someId',
|
||||||
|
template: 'inline component'
|
||||||
|
})).then( (protoView) => {
|
||||||
|
expect(DOM.getInnerHTML(resolveInternalDomProtoView(protoView.render).element)).toEqual('inline component');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should load url templates', inject([AsyncTestCompleter], (async) => {
|
||||||
|
var urlData = MapWrapper.createFromStringMap({
|
||||||
|
'someUrl': 'url component'
|
||||||
|
});
|
||||||
|
var compiler = createCompiler(EMPTY_STEP, urlData);
|
||||||
|
compiler.compile(new ViewDefinition({
|
||||||
|
componentId: 'someId',
|
||||||
|
absUrl: 'someUrl'
|
||||||
|
})).then( (protoView) => {
|
||||||
|
expect(DOM.getInnerHTML(resolveInternalDomProtoView(protoView.render).element)).toEqual('url component');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should report loading errors', inject([AsyncTestCompleter], (async) => {
|
||||||
|
var compiler = createCompiler(EMPTY_STEP, MapWrapper.create());
|
||||||
|
PromiseWrapper.catchError(compiler.compile(new ViewDefinition({
|
||||||
|
componentId: 'someId',
|
||||||
|
absUrl: 'someUrl'
|
||||||
|
})), (e) => {
|
||||||
|
expect(e.message).toContain(`Failed to load the template "someId"`);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should wait for async subtasks to be resolved', inject([AsyncTestCompleter], (async) => {
|
||||||
|
var subTasksCompleted = false;
|
||||||
|
|
||||||
|
var completer = PromiseWrapper.completer();
|
||||||
|
|
||||||
|
var compiler = createCompiler( (parent, current, control) => {
|
||||||
|
ListWrapper.push(mockStepFactory.subTaskPromises, completer.promise.then((_) => {
|
||||||
|
subTasksCompleted = true;
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
// It should always return a Promise because the subtask is async
|
||||||
|
var pvPromise = compiler.compile(new ViewDefinition({
|
||||||
|
componentId: 'someId',
|
||||||
|
template: 'some component'
|
||||||
|
}));
|
||||||
|
expect(pvPromise).toBePromise();
|
||||||
|
expect(subTasksCompleted).toEqual(false);
|
||||||
|
|
||||||
|
// The Promise should resolve after the subtask is ready
|
||||||
|
completer.resolve(null);
|
||||||
|
pvPromise.then((protoView) => {
|
||||||
|
expect(subTasksCompleted).toEqual(true);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should return ProtoViews of type COMPONENT_VIEW_TYPE', inject([AsyncTestCompleter], (async) => {
|
||||||
|
var compiler = createCompiler(EMPTY_STEP);
|
||||||
|
compiler.compile(new ViewDefinition({
|
||||||
|
componentId: 'someId',
|
||||||
|
template: 'inline component'
|
||||||
|
})).then( (protoView) => {
|
||||||
|
expect(protoView.type).toEqual(ProtoViewDto.COMPONENT_VIEW_TYPE);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('compileHost', () => {
|
||||||
|
|
||||||
|
it('should return ProtoViews of type HOST_VIEW_TYPE', inject([AsyncTestCompleter], (async) => {
|
||||||
|
var compiler = createCompiler(EMPTY_STEP);
|
||||||
|
compiler.compileHost(someComponent).then( (protoView) => {
|
||||||
|
expect(protoView.type).toEqual(ProtoViewDto.HOST_VIEW_TYPE);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -195,3 +222,9 @@ class FakeTemplateLoader extends TemplateLoader {
|
||||||
return PromiseWrapper.reject('Load failed');
|
return PromiseWrapper.reject('Load failed');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var someComponent = new DirectiveMetadata({
|
||||||
|
selector: 'some-comp',
|
||||||
|
id: 'someComponent',
|
||||||
|
type: DirectiveMetadata.COMPONENT_TYPE
|
||||||
|
});
|
||||||
|
|
|
@ -204,6 +204,14 @@ export function main() {
|
||||||
);
|
);
|
||||||
}).toThrowError(new RegExp('Only one component directive is allowed per element' ));
|
}).toThrowError(new RegExp('Only one component directive is allowed per element' ));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should sort the directives and store the component as the first directive', () => {
|
||||||
|
var results = process(
|
||||||
|
el('<some-comp some-decor></some-comp>')
|
||||||
|
);
|
||||||
|
expect(annotatedDirectives[results[0].directives[0].directiveIndex].id).toEqual('someComponent');
|
||||||
|
expect(annotatedDirectives[results[0].directives[1].directiveIndex].id).toEqual('someDirective');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -239,6 +247,7 @@ var someComponent2 = new DirectiveMetadata({
|
||||||
|
|
||||||
var someDirective = new DirectiveMetadata({
|
var someDirective = new DirectiveMetadata({
|
||||||
selector: '[some-decor]',
|
selector: '[some-decor]',
|
||||||
|
id: 'someDirective',
|
||||||
type: DirectiveMetadata.DIRECTIVE_TYPE
|
type: DirectiveMetadata.DIRECTIVE_TYPE
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {CompileStep} from 'angular2/src/render/dom/compiler/compile_step'
|
||||||
import {CompileControl} from 'angular2/src/render/dom/compiler/compile_control';
|
import {CompileControl} from 'angular2/src/render/dom/compiler/compile_control';
|
||||||
|
|
||||||
import {ProtoViewBuilder} from 'angular2/src/render/dom/view/proto_view_builder';
|
import {ProtoViewBuilder} from 'angular2/src/render/dom/view/proto_view_builder';
|
||||||
|
import {ProtoViewDto} from 'angular2/src/render/api';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('compile_pipeline', () => {
|
describe('compile_pipeline', () => {
|
||||||
|
@ -39,7 +40,7 @@ export function main() {
|
||||||
var element = el('<div><div><span viewroot><span></span></span></div></div>');
|
var element = el('<div><div><span viewroot><span></span></span></div></div>');
|
||||||
var pipeline = new CompilePipeline([new MockStep((parent, current, control) => {
|
var pipeline = new CompilePipeline([new MockStep((parent, current, control) => {
|
||||||
if (isPresent(DOM.getAttribute(current.element, 'viewroot'))) {
|
if (isPresent(DOM.getAttribute(current.element, 'viewroot'))) {
|
||||||
current.inheritedProtoView = new ProtoViewBuilder(current.element);
|
current.inheritedProtoView = new ProtoViewBuilder(current.element, ProtoViewDto.EMBEDDED_VIEW_TYPE);
|
||||||
}
|
}
|
||||||
})]);
|
})]);
|
||||||
var results = pipeline.process(element);
|
var results = pipeline.process(element);
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {MapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
import {ViewSplitter} from 'angular2/src/render/dom/compiler/view_splitter';
|
import {ViewSplitter} from 'angular2/src/render/dom/compiler/view_splitter';
|
||||||
import {CompilePipeline} from 'angular2/src/render/dom/compiler/compile_pipeline';
|
import {CompilePipeline} from 'angular2/src/render/dom/compiler/compile_pipeline';
|
||||||
|
import {ProtoViewDto} from 'angular2/src/render/api';
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
|
|
||||||
import {Lexer, Parser} from 'angular2/change_detection';
|
import {Lexer, Parser} from 'angular2/change_detection';
|
||||||
|
@ -56,6 +57,7 @@ export function main() {
|
||||||
var results = createPipeline().process(rootElement);
|
var results = createPipeline().process(rootElement);
|
||||||
expect(results[2].inheritedProtoView).not.toBe(null);
|
expect(results[2].inheritedProtoView).not.toBe(null);
|
||||||
expect(results[2].inheritedProtoView).toBe(results[1].inheritedElementBinder.nestedProtoView);
|
expect(results[2].inheritedProtoView).toBe(results[1].inheritedElementBinder.nestedProtoView);
|
||||||
|
expect(results[2].inheritedProtoView.type).toBe(ProtoViewDto.EMBEDDED_VIEW_TYPE);
|
||||||
expect(DOM.getOuterHTML(results[2].inheritedProtoView.rootElement)).toEqual('<template>a</template>');
|
expect(DOM.getOuterHTML(results[2].inheritedProtoView.rootElement)).toEqual('<template>a</template>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
import {SpyLocation} from 'angular2/src/mock/location_mock';
|
import {SpyLocation} from 'angular2/src/mock/location_mock';
|
||||||
import {Location} from 'angular2/src/router/location';
|
import {Location} from 'angular2/src/router/location';
|
||||||
import {RouteRegistry} from 'angular2/src/router/route_registry';
|
import {RouteRegistry} from 'angular2/src/router/route_registry';
|
||||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
|
|
||||||
var teamCmpCount;
|
var teamCmpCount;
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ export function main() {
|
||||||
beforeEachBindings(() => [
|
beforeEachBindings(() => [
|
||||||
Pipeline,
|
Pipeline,
|
||||||
RouteRegistry,
|
RouteRegistry,
|
||||||
DirectiveMetadataReader,
|
DirectiveResolver,
|
||||||
bind(Location).toClass(SpyLocation),
|
bind(Location).toClass(SpyLocation),
|
||||||
bind(Router).toFactory((registry, pipeline, location) => {
|
bind(Router).toFactory((registry, pipeline, location) => {
|
||||||
return new RootRouter(registry, pipeline, location, MyComp);
|
return new RootRouter(registry, pipeline, location, MyComp);
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {SpyLocation} from 'angular2/src/mock/location_mock'
|
||||||
import {Location} from 'angular2/src/router/location';
|
import {Location} from 'angular2/src/router/location';
|
||||||
|
|
||||||
import {RouteRegistry} from 'angular2/src/router/route_registry';
|
import {RouteRegistry} from 'angular2/src/router/route_registry';
|
||||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
|
|
||||||
import {bind} from 'angular2/di';
|
import {bind} from 'angular2/di';
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ export function main() {
|
||||||
beforeEachBindings(() => [
|
beforeEachBindings(() => [
|
||||||
Pipeline,
|
Pipeline,
|
||||||
RouteRegistry,
|
RouteRegistry,
|
||||||
DirectiveMetadataReader,
|
DirectiveResolver,
|
||||||
bind(Location).toClass(SpyLocation),
|
bind(Location).toClass(SpyLocation),
|
||||||
bind(Router).toFactory((registry, pipeline, location) => {
|
bind(Router).toFactory((registry, pipeline, location) => {
|
||||||
return new RootRouter(registry, pipeline, location, AppCmp);
|
return new RootRouter(registry, pipeline, location, AppCmp);
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {NativeShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/native
|
||||||
import {Parser, Lexer, DynamicChangeDetection} from 'angular2/change_detection';
|
import {Parser, Lexer, DynamicChangeDetection} from 'angular2/change_detection';
|
||||||
|
|
||||||
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
|
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
|
||||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
|
|
||||||
import {Component} from 'angular2/src/core/annotations_impl/annotations';
|
import {Component} from 'angular2/src/core/annotations_impl/annotations';
|
||||||
import {Directive} from 'angular2/src/core/annotations_impl/annotations';
|
import {Directive} from 'angular2/src/core/annotations_impl/annotations';
|
||||||
|
@ -57,7 +57,7 @@ export function main() {
|
||||||
var count = getIntParameter('elements');
|
var count = getIntParameter('elements');
|
||||||
|
|
||||||
setupReflector();
|
setupReflector();
|
||||||
var reader = new DirectiveMetadataReader();
|
var reader = new DirectiveResolver();
|
||||||
var cache = new CompilerCache();
|
var cache = new CompilerCache();
|
||||||
var templateResolver = new FakeTemplateResolver();
|
var templateResolver = new FakeTemplateResolver();
|
||||||
var urlResolver = new UrlResolver();
|
var urlResolver = new UrlResolver();
|
||||||
|
|
Loading…
Reference in New Issue