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:
Tobias Bosch 2015-05-11 17:59:39 -07:00
parent 5114411749
commit ecb068019b
33 changed files with 685 additions and 436 deletions

View File

@ -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])
]; ];

View File

@ -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() {

View File

@ -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');

View File

@ -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.`);
} }
} }

View File

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

View File

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

View File

@ -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.

View File

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

View File

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

View File

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

View File

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

View File

@ -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

View File

@ -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();

View File

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

View File

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

View File

@ -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,

View File

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

View File

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

View File

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

View File

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

View File

@ -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 {}

View File

@ -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() {

View File

@ -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();

View File

@ -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', () => {

View File

@ -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) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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();