UNPKG

angular2

Version:

Angular 2 - a web framework for modern web apps

1,087 lines (914 loc) 39 kB
import { isPresent, isBlank, Type, stringify, CONST_EXPR, StringWrapper } from 'angular2/src/facade/lang'; import {BaseException} from 'angular2/src/facade/exceptions'; import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async'; import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection'; import { Injector, Key, Dependency, provide, Provider, ResolvedProvider, NoProviderError, AbstractProviderError, CyclicDependencyError, resolveForwardRef } from 'angular2/src/core/di'; import { UNDEFINED, ProtoInjector, Visibility, InjectorInlineStrategy, InjectorDynamicStrategy, ProviderWithVisibility, DependencyProvider } from 'angular2/src/core/di/injector'; import {resolveProvider, ResolvedFactory, ResolvedProvider_} from 'angular2/src/core/di/provider'; import {AttributeMetadata, QueryMetadata} from '../metadata/di'; import {AppViewContainer, AppView} from './view'; /* circular */ import * as avmModule from './view_manager'; import {ViewContainerRef} from './view_container_ref'; import {ElementRef} from './element_ref'; import {TemplateRef} from './template_ref'; import {DirectiveMetadata, ComponentMetadata} from '../metadata/directives'; import {hasLifecycleHook} from './directive_lifecycle_reflector'; import { ChangeDetector, ChangeDetectorRef } from 'angular2/src/core/change_detection/change_detection'; import {QueryList} from './query_list'; import {reflector} from 'angular2/src/core/reflection/reflection'; import {SetterFn} from 'angular2/src/core/reflection/types'; import {EventConfig} from 'angular2/src/core/linker/event_config'; import {AfterViewChecked} from 'angular2/src/core/linker/interfaces'; import {PipeProvider} from 'angular2/src/core/pipes/pipe_provider'; import {LifecycleHooks} from './interfaces'; import {ViewContainerRef_} from "./view_container_ref"; var _staticKeys; export class StaticKeys { viewManagerId: number; templateRefId: number; viewContainerId: number; changeDetectorRefId: number; elementRefId: number; constructor() { this.viewManagerId = Key.get(avmModule.AppViewManager).id; this.templateRefId = Key.get(TemplateRef).id; this.viewContainerId = Key.get(ViewContainerRef).id; this.changeDetectorRefId = Key.get(ChangeDetectorRef).id; this.elementRefId = Key.get(ElementRef).id; } static instance(): StaticKeys { if (isBlank(_staticKeys)) _staticKeys = new StaticKeys(); return _staticKeys; } } export class TreeNode<T extends TreeNode<any>> { /** @internal */ _parent: T; constructor(parent: T) { if (isPresent(parent)) { parent.addChild(this); } else { this._parent = null; } } addChild(child: T): void { child._parent = this; } remove(): void { this._parent = null; } get parent() { return this._parent; } } export class DirectiveDependency extends Dependency { constructor(key: Key, optional: boolean, lowerBoundVisibility: Object, upperBoundVisibility: Object, properties: any[], public attributeName: string, public queryDecorator: QueryMetadata) { super(key, optional, lowerBoundVisibility, upperBoundVisibility, properties); this._verify(); } /** @internal */ _verify(): void { var count = 0; if (isPresent(this.queryDecorator)) count++; if (isPresent(this.attributeName)) count++; if (count > 1) throw new BaseException( 'A directive injectable can contain only one of the following @Attribute or @Query.'); } static createFrom(d: Dependency): Dependency { return new DirectiveDependency( d.key, d.optional, d.lowerBoundVisibility, d.upperBoundVisibility, d.properties, DirectiveDependency._attributeName(d.properties), DirectiveDependency._query(d.properties)); } /** @internal */ static _attributeName(properties: any[]): string { var p = <AttributeMetadata>properties.find(p => p instanceof AttributeMetadata); return isPresent(p) ? p.attributeName : null; } /** @internal */ static _query(properties: any[]): QueryMetadata { return <QueryMetadata>properties.find(p => p instanceof QueryMetadata); } } export class DirectiveProvider extends ResolvedProvider_ { public callOnDestroy: boolean; constructor(key: Key, factory: Function, deps: Dependency[], public metadata: DirectiveMetadata, public providers: Array<Type | Provider | any[]>, public viewProviders: Array<Type | Provider | any[]>) { super(key, [new ResolvedFactory(factory, deps)], false); this.callOnDestroy = hasLifecycleHook(LifecycleHooks.OnDestroy, key.token); } get displayName(): string { return this.key.displayName; } get queries(): QueryMetadataWithSetter[] { if (isBlank(this.metadata.queries)) return []; var res = []; StringMapWrapper.forEach(this.metadata.queries, (meta, fieldName) => { var setter = reflector.setter(fieldName); res.push(new QueryMetadataWithSetter(setter, meta)); }); return res; } get eventEmitters(): string[] { return isPresent(this.metadata) && isPresent(this.metadata.outputs) ? this.metadata.outputs : []; } static createFromProvider(provider: Provider, meta: DirectiveMetadata): DirectiveProvider { if (isBlank(meta)) { meta = new DirectiveMetadata(); } var rb = resolveProvider(provider); var rf = rb.resolvedFactories[0]; var deps = rf.dependencies.map(DirectiveDependency.createFrom); var providers = isPresent(meta.providers) ? meta.providers : []; var viewBindigs = meta instanceof ComponentMetadata && isPresent(meta.viewProviders) ? meta.viewProviders : []; return new DirectiveProvider(rb.key, rf.factory, deps, meta, providers, viewBindigs); } static createFromType(type: Type, annotation: DirectiveMetadata): DirectiveProvider { var provider = new Provider(type, {useClass: type}); return DirectiveProvider.createFromProvider(provider, annotation); } } // TODO(rado): benchmark and consider rolling in as ElementInjector fields. export class PreBuiltObjects { nestedView: AppView = null; constructor(public viewManager: avmModule.AppViewManager, public view: AppView, public elementRef: ElementRef, public templateRef: TemplateRef) {} } export class QueryMetadataWithSetter { constructor(public setter: SetterFn, public metadata: QueryMetadata) {} } export class EventEmitterAccessor { constructor(public eventName: string, public getter: Function) {} subscribe(view: AppView, boundElementIndex: number, directive: Object): Object { var eventEmitter = this.getter(directive); return ObservableWrapper.subscribe<Event>( eventEmitter, eventObj => view.triggerEventHandlers(this.eventName, eventObj, boundElementIndex)); } } function _createEventEmitterAccessors(bwv: ProviderWithVisibility): EventEmitterAccessor[] { var provider = bwv.provider; if (!(provider instanceof DirectiveProvider)) return []; var db = <DirectiveProvider>provider; return db.eventEmitters.map(eventConfig => { var parsedEvent = EventConfig.parse(eventConfig); return new EventEmitterAccessor(parsedEvent.eventName, reflector.getter(parsedEvent.fieldName)); }); } function _createProtoQueryRefs(providers: ProviderWithVisibility[]): ProtoQueryRef[] { var res = []; ListWrapper.forEachWithIndex(providers, (b, i) => { if (b.provider instanceof DirectiveProvider) { var directiveProvider = <DirectiveProvider>b.provider; // field queries var queries: QueryMetadataWithSetter[] = directiveProvider.queries; queries.forEach(q => res.push(new ProtoQueryRef(i, q.setter, q.metadata))); // queries passed into the constructor. // TODO: remove this after constructor queries are no longer supported var deps: DirectiveDependency[] = <DirectiveDependency[]>directiveProvider.resolvedFactory.dependencies; deps.forEach(d => { if (isPresent(d.queryDecorator)) res.push(new ProtoQueryRef(i, null, d.queryDecorator)); }); } }); return res; } export class ProtoElementInjector { view: AppView; attributes: Map<string, string>; eventEmitterAccessors: EventEmitterAccessor[][]; protoQueryRefs: ProtoQueryRef[]; protoInjector: ProtoInjector; static create(parent: ProtoElementInjector, index: number, providers: DirectiveProvider[], firstProviderIsComponent: boolean, distanceToParent: number, directiveVariableBindings: Map<string, number>): ProtoElementInjector { var bd = []; ProtoElementInjector._createDirectiveProviderWithVisibility(providers, bd, firstProviderIsComponent); if (firstProviderIsComponent) { ProtoElementInjector._createViewProvidersWithVisibility(providers, bd); } ProtoElementInjector._createProvidersWithVisibility(providers, bd); return new ProtoElementInjector(parent, index, bd, distanceToParent, firstProviderIsComponent, directiveVariableBindings); } private static _createDirectiveProviderWithVisibility(dirProviders: DirectiveProvider[], bd: ProviderWithVisibility[], firstProviderIsComponent: boolean) { dirProviders.forEach(dirProvider => { bd.push(ProtoElementInjector._createProviderWithVisibility( firstProviderIsComponent, dirProvider, dirProviders, dirProvider)); }); } private static _createProvidersWithVisibility(dirProviders: DirectiveProvider[], bd: ProviderWithVisibility[]) { var providersFromAllDirectives = []; dirProviders.forEach(dirProvider => { providersFromAllDirectives = ListWrapper.concat(providersFromAllDirectives, dirProvider.providers); }); var resolved = Injector.resolve(providersFromAllDirectives); resolved.forEach(b => bd.push(new ProviderWithVisibility(b, Visibility.Public))); } private static _createProviderWithVisibility(firstProviderIsComponent: boolean, dirProvider: DirectiveProvider, dirProviders: DirectiveProvider[], provider: ResolvedProvider) { var isComponent = firstProviderIsComponent && dirProviders[0] === dirProvider; return new ProviderWithVisibility( provider, isComponent ? Visibility.PublicAndPrivate : Visibility.Public); } private static _createViewProvidersWithVisibility(dirProviders: DirectiveProvider[], bd: ProviderWithVisibility[]) { var resolvedViewProviders = Injector.resolve(dirProviders[0].viewProviders); resolvedViewProviders.forEach(b => bd.push(new ProviderWithVisibility(b, Visibility.Private))); } /** @internal */ public _firstProviderIsComponent: boolean; constructor(public parent: ProtoElementInjector, public index: number, bwv: ProviderWithVisibility[], public distanceToParent: number, _firstProviderIsComponent: boolean, public directiveVariableBindings: Map<string, number>) { this._firstProviderIsComponent = _firstProviderIsComponent; var length = bwv.length; this.protoInjector = new ProtoInjector(bwv); this.eventEmitterAccessors = ListWrapper.createFixedSize(length); for (var i = 0; i < length; ++i) { this.eventEmitterAccessors[i] = _createEventEmitterAccessors(bwv[i]); } this.protoQueryRefs = _createProtoQueryRefs(bwv); } instantiate(parent: ElementInjector): ElementInjector { return new ElementInjector(this, parent); } directParent(): ProtoElementInjector { return this.distanceToParent < 2 ? this.parent : null; } get hasBindings(): boolean { return this.eventEmitterAccessors.length > 0; } getProviderAtIndex(index: number): any { return this.protoInjector.getProviderAtIndex(index); } } class _Context { constructor(public element: any, public componentElement: any, public injector: any) {} } export class ElementInjector extends TreeNode<ElementInjector> implements DependencyProvider, AfterViewChecked { private _host: ElementInjector; private _preBuiltObjects: PreBuiltObjects = null; private _queryStrategy: _QueryStrategy; hydrated: boolean; private _injector: Injector; private _strategy: _ElementInjectorStrategy; /** @internal */ public _proto: ProtoElementInjector; constructor(_proto: ProtoElementInjector, parent: ElementInjector) { super(parent); this._proto = _proto; this._injector = new Injector(this._proto.protoInjector, null, this, () => this._debugContext()); // we couple ourselves to the injector strategy to avoid polymoprhic calls var injectorStrategy = <any>this._injector.internalStrategy; this._strategy = injectorStrategy instanceof InjectorInlineStrategy ? new ElementInjectorInlineStrategy(injectorStrategy, this) : new ElementInjectorDynamicStrategy(injectorStrategy, this); this.hydrated = false; this._queryStrategy = this._buildQueryStrategy(); } dehydrate(): void { this.hydrated = false; this._host = null; this._preBuiltObjects = null; this._strategy.callOnDestroy(); this._strategy.dehydrate(); this._queryStrategy.dehydrate(); } hydrate(imperativelyCreatedInjector: Injector, host: ElementInjector, preBuiltObjects: PreBuiltObjects): void { this._host = host; this._preBuiltObjects = preBuiltObjects; this._reattachInjectors(imperativelyCreatedInjector); this._queryStrategy.hydrate(); this._strategy.hydrate(); this.hydrated = true; } private _debugContext(): any { var p = this._preBuiltObjects; var index = p.elementRef.boundElementIndex - p.view.elementOffset; var c = this._preBuiltObjects.view.getDebugContext(index, null); return isPresent(c) ? new _Context(c.element, c.componentElement, c.injector) : null; } private _reattachInjectors(imperativelyCreatedInjector: Injector): void { // Dynamically-loaded component in the template. Not a root ElementInjector. if (isPresent(this._parent)) { if (isPresent(imperativelyCreatedInjector)) { // The imperative injector is similar to having an element between // the dynamic-loaded component and its parent => no boundaries. this._reattachInjector(this._injector, imperativelyCreatedInjector, false); this._reattachInjector(imperativelyCreatedInjector, this._parent._injector, false); } else { this._reattachInjector(this._injector, this._parent._injector, false); } // Dynamically-loaded component in the template. A root ElementInjector. } else if (isPresent(this._host)) { // The imperative injector is similar to having an element between // the dynamic-loaded component and its parent => no boundary between // the component and imperativelyCreatedInjector. // But since it is a root ElementInjector, we need to create a boundary // between imperativelyCreatedInjector and _host. if (isPresent(imperativelyCreatedInjector)) { this._reattachInjector(this._injector, imperativelyCreatedInjector, false); this._reattachInjector(imperativelyCreatedInjector, this._host._injector, true); } else { this._reattachInjector(this._injector, this._host._injector, true); } // Bootstrap } else { if (isPresent(imperativelyCreatedInjector)) { this._reattachInjector(this._injector, imperativelyCreatedInjector, true); } } } private _reattachInjector(injector: Injector, parentInjector: Injector, isBoundary: boolean) { injector.internalStrategy.attach(parentInjector, isBoundary); } hasVariableBinding(name: string): boolean { var vb = this._proto.directiveVariableBindings; return isPresent(vb) && vb.has(name); } getVariableBinding(name: string): any { var index = this._proto.directiveVariableBindings.get(name); return isPresent(index) ? this.getDirectiveAtIndex(<number>index) : this.getElementRef(); } get(token: any): any { return this._injector.get(token); } hasDirective(type: Type): boolean { return isPresent(this._injector.getOptional(type)); } getEventEmitterAccessors(): EventEmitterAccessor[][] { return this._proto.eventEmitterAccessors; } getDirectiveVariableBindings(): Map<string, number> { return this._proto.directiveVariableBindings; } getComponent(): any { return this._strategy.getComponent(); } getInjector(): Injector { return this._injector; } getElementRef(): ElementRef { return this._preBuiltObjects.elementRef; } getViewContainerRef(): ViewContainerRef { return new ViewContainerRef_(this._preBuiltObjects.viewManager, this.getElementRef()); } getNestedView(): AppView { return this._preBuiltObjects.nestedView; } getView(): AppView { return this._preBuiltObjects.view; } directParent(): ElementInjector { return this._proto.distanceToParent < 2 ? this.parent : null; } isComponentKey(key: Key): boolean { return this._strategy.isComponentKey(key); } getDependency(injector: Injector, provider: ResolvedProvider, dep: Dependency): any { var key: Key = dep.key; if (provider instanceof DirectiveProvider) { var dirDep = <DirectiveDependency>dep; var dirProvider = provider; var staticKeys = StaticKeys.instance(); if (key.id === staticKeys.viewManagerId) return this._preBuiltObjects.viewManager; if (isPresent(dirDep.attributeName)) return this._buildAttribute(dirDep); if (isPresent(dirDep.queryDecorator)) return this._queryStrategy.findQuery(dirDep.queryDecorator).list; if (dirDep.key.id === StaticKeys.instance().changeDetectorRefId) { // We provide the component's view change detector to components and // the surrounding component's change detector to directives. if (dirProvider.metadata instanceof ComponentMetadata) { var componentView = this._preBuiltObjects.view.getNestedView( this._preBuiltObjects.elementRef.boundElementIndex); return componentView.changeDetector.ref; } else { return this._preBuiltObjects.view.changeDetector.ref; } } if (dirDep.key.id === StaticKeys.instance().elementRefId) { return this.getElementRef(); } if (dirDep.key.id === StaticKeys.instance().viewContainerId) { return this.getViewContainerRef(); } if (dirDep.key.id === StaticKeys.instance().templateRefId) { if (isBlank(this._preBuiltObjects.templateRef)) { if (dirDep.optional) { return null; } throw new NoProviderError(null, dirDep.key); } return this._preBuiltObjects.templateRef; } } else if (provider instanceof PipeProvider) { if (dep.key.id === StaticKeys.instance().changeDetectorRefId) { var componentView = this._preBuiltObjects.view.getNestedView( this._preBuiltObjects.elementRef.boundElementIndex); return componentView.changeDetector.ref; } } return UNDEFINED; } private _buildAttribute(dep: DirectiveDependency): string { var attributes = this._proto.attributes; if (isPresent(attributes) && attributes.has(dep.attributeName)) { return attributes.get(dep.attributeName); } else { return null; } } addDirectivesMatchingQuery(query: QueryMetadata, list: any[]): void { var templateRef = isBlank(this._preBuiltObjects) ? null : this._preBuiltObjects.templateRef; if (query.selector === TemplateRef && isPresent(templateRef)) { list.push(templateRef); } this._strategy.addDirectivesMatchingQuery(query, list); } private _buildQueryStrategy(): _QueryStrategy { if (this._proto.protoQueryRefs.length === 0) { return _emptyQueryStrategy; } else if (this._proto.protoQueryRefs.length <= InlineQueryStrategy.NUMBER_OF_SUPPORTED_QUERIES) { return new InlineQueryStrategy(this); } else { return new DynamicQueryStrategy(this); } } link(parent: ElementInjector): void { parent.addChild(this); } unlink(): void { this.remove(); } getDirectiveAtIndex(index: number): any { return this._injector.getAt(index); } hasInstances(): boolean { return this._proto.hasBindings && this.hydrated; } getHost(): ElementInjector { return this._host; } getBoundElementIndex(): number { return this._proto.index; } getRootViewInjectors(): ElementInjector[] { if (!this.hydrated) return []; var view = this._preBuiltObjects.view; var nestedView = view.getNestedView(view.elementOffset + this.getBoundElementIndex()); return isPresent(nestedView) ? nestedView.rootElementInjectors : []; } ngAfterViewChecked(): void { this._queryStrategy.updateViewQueries(); } ngAfterContentChecked(): void { this._queryStrategy.updateContentQueries(); } traverseAndSetQueriesAsDirty(): void { var inj = this; while (isPresent(inj)) { inj._setQueriesAsDirty(); inj = inj.parent; } } private _setQueriesAsDirty(): void { this._queryStrategy.setContentQueriesAsDirty(); if (isPresent(this._host)) this._host._queryStrategy.setViewQueriesAsDirty(); } } interface _QueryStrategy { setContentQueriesAsDirty(): void; setViewQueriesAsDirty(): void; hydrate(): void; dehydrate(): void; updateContentQueries(): void; updateViewQueries(): void; findQuery(query: QueryMetadata): QueryRef; } class _EmptyQueryStrategy implements _QueryStrategy { setContentQueriesAsDirty(): void {} setViewQueriesAsDirty(): void {} hydrate(): void {} dehydrate(): void {} updateContentQueries(): void {} updateViewQueries(): void {} findQuery(query: QueryMetadata): QueryRef { throw new BaseException(`Cannot find query for directive ${query}.`); } } var _emptyQueryStrategy = new _EmptyQueryStrategy(); class InlineQueryStrategy implements _QueryStrategy { static NUMBER_OF_SUPPORTED_QUERIES = 3; query0: QueryRef; query1: QueryRef; query2: QueryRef; constructor(ei: ElementInjector) { var protoRefs = ei._proto.protoQueryRefs; if (protoRefs.length > 0) this.query0 = new QueryRef(protoRefs[0], ei); if (protoRefs.length > 1) this.query1 = new QueryRef(protoRefs[1], ei); if (protoRefs.length > 2) this.query2 = new QueryRef(protoRefs[2], ei); } setContentQueriesAsDirty(): void { if (isPresent(this.query0) && !this.query0.isViewQuery) this.query0.dirty = true; if (isPresent(this.query1) && !this.query1.isViewQuery) this.query1.dirty = true; if (isPresent(this.query2) && !this.query2.isViewQuery) this.query2.dirty = true; } setViewQueriesAsDirty(): void { if (isPresent(this.query0) && this.query0.isViewQuery) this.query0.dirty = true; if (isPresent(this.query1) && this.query1.isViewQuery) this.query1.dirty = true; if (isPresent(this.query2) && this.query2.isViewQuery) this.query2.dirty = true; } hydrate(): void { if (isPresent(this.query0)) this.query0.hydrate(); if (isPresent(this.query1)) this.query1.hydrate(); if (isPresent(this.query2)) this.query2.hydrate(); } dehydrate(): void { if (isPresent(this.query0)) this.query0.dehydrate(); if (isPresent(this.query1)) this.query1.dehydrate(); if (isPresent(this.query2)) this.query2.dehydrate(); } updateContentQueries() { if (isPresent(this.query0) && !this.query0.isViewQuery) { this.query0.update(); } if (isPresent(this.query1) && !this.query1.isViewQuery) { this.query1.update(); } if (isPresent(this.query2) && !this.query2.isViewQuery) { this.query2.update(); } } updateViewQueries() { if (isPresent(this.query0) && this.query0.isViewQuery) { this.query0.update(); } if (isPresent(this.query1) && this.query1.isViewQuery) { this.query1.update(); } if (isPresent(this.query2) && this.query2.isViewQuery) { this.query2.update(); } } findQuery(query: QueryMetadata): QueryRef { if (isPresent(this.query0) && this.query0.protoQueryRef.query === query) { return this.query0; } if (isPresent(this.query1) && this.query1.protoQueryRef.query === query) { return this.query1; } if (isPresent(this.query2) && this.query2.protoQueryRef.query === query) { return this.query2; } throw new BaseException(`Cannot find query for directive ${query}.`); } } class DynamicQueryStrategy implements _QueryStrategy { queries: QueryRef[]; constructor(ei: ElementInjector) { this.queries = ei._proto.protoQueryRefs.map(p => new QueryRef(p, ei)); } setContentQueriesAsDirty(): void { for (var i = 0; i < this.queries.length; ++i) { var q = this.queries[i]; if (!q.isViewQuery) q.dirty = true; } } setViewQueriesAsDirty(): void { for (var i = 0; i < this.queries.length; ++i) { var q = this.queries[i]; if (q.isViewQuery) q.dirty = true; } } hydrate(): void { for (var i = 0; i < this.queries.length; ++i) { var q = this.queries[i]; q.hydrate(); } } dehydrate(): void { for (var i = 0; i < this.queries.length; ++i) { var q = this.queries[i]; q.dehydrate(); } } updateContentQueries() { for (var i = 0; i < this.queries.length; ++i) { var q = this.queries[i]; if (!q.isViewQuery) { q.update(); } } } updateViewQueries() { for (var i = 0; i < this.queries.length; ++i) { var q = this.queries[i]; if (q.isViewQuery) { q.update(); } } } findQuery(query: QueryMetadata): QueryRef { for (var i = 0; i < this.queries.length; ++i) { var q = this.queries[i]; if (q.protoQueryRef.query === query) { return q; } } throw new BaseException(`Cannot find query for directive ${query}.`); } } interface _ElementInjectorStrategy { callOnDestroy(): void; getComponent(): any; isComponentKey(key: Key): boolean; addDirectivesMatchingQuery(q: QueryMetadata, res: any[]): void; hydrate(): void; dehydrate(): void; } /** * Strategy used by the `ElementInjector` when the number of providers is 10 or less. * In such a case, inlining fields is beneficial for performances. */ class ElementInjectorInlineStrategy implements _ElementInjectorStrategy { constructor(public injectorStrategy: InjectorInlineStrategy, public _ei: ElementInjector) {} hydrate(): void { var i = this.injectorStrategy; var p = i.protoStrategy; i.resetConstructionCounter(); if (p.provider0 instanceof DirectiveProvider && isPresent(p.keyId0) && i.obj0 === UNDEFINED) i.obj0 = i.instantiateProvider(p.provider0, p.visibility0); if (p.provider1 instanceof DirectiveProvider && isPresent(p.keyId1) && i.obj1 === UNDEFINED) i.obj1 = i.instantiateProvider(p.provider1, p.visibility1); if (p.provider2 instanceof DirectiveProvider && isPresent(p.keyId2) && i.obj2 === UNDEFINED) i.obj2 = i.instantiateProvider(p.provider2, p.visibility2); if (p.provider3 instanceof DirectiveProvider && isPresent(p.keyId3) && i.obj3 === UNDEFINED) i.obj3 = i.instantiateProvider(p.provider3, p.visibility3); if (p.provider4 instanceof DirectiveProvider && isPresent(p.keyId4) && i.obj4 === UNDEFINED) i.obj4 = i.instantiateProvider(p.provider4, p.visibility4); if (p.provider5 instanceof DirectiveProvider && isPresent(p.keyId5) && i.obj5 === UNDEFINED) i.obj5 = i.instantiateProvider(p.provider5, p.visibility5); if (p.provider6 instanceof DirectiveProvider && isPresent(p.keyId6) && i.obj6 === UNDEFINED) i.obj6 = i.instantiateProvider(p.provider6, p.visibility6); if (p.provider7 instanceof DirectiveProvider && isPresent(p.keyId7) && i.obj7 === UNDEFINED) i.obj7 = i.instantiateProvider(p.provider7, p.visibility7); if (p.provider8 instanceof DirectiveProvider && isPresent(p.keyId8) && i.obj8 === UNDEFINED) i.obj8 = i.instantiateProvider(p.provider8, p.visibility8); if (p.provider9 instanceof DirectiveProvider && isPresent(p.keyId9) && i.obj9 === UNDEFINED) i.obj9 = i.instantiateProvider(p.provider9, p.visibility9); } dehydrate() { var i = this.injectorStrategy; i.obj0 = UNDEFINED; i.obj1 = UNDEFINED; i.obj2 = UNDEFINED; i.obj3 = UNDEFINED; i.obj4 = UNDEFINED; i.obj5 = UNDEFINED; i.obj6 = UNDEFINED; i.obj7 = UNDEFINED; i.obj8 = UNDEFINED; i.obj9 = UNDEFINED; } callOnDestroy(): void { var i = this.injectorStrategy; var p = i.protoStrategy; if (p.provider0 instanceof DirectiveProvider && (<DirectiveProvider>p.provider0).callOnDestroy) { i.obj0.ngOnDestroy(); } if (p.provider1 instanceof DirectiveProvider && (<DirectiveProvider>p.provider1).callOnDestroy) { i.obj1.ngOnDestroy(); } if (p.provider2 instanceof DirectiveProvider && (<DirectiveProvider>p.provider2).callOnDestroy) { i.obj2.ngOnDestroy(); } if (p.provider3 instanceof DirectiveProvider && (<DirectiveProvider>p.provider3).callOnDestroy) { i.obj3.ngOnDestroy(); } if (p.provider4 instanceof DirectiveProvider && (<DirectiveProvider>p.provider4).callOnDestroy) { i.obj4.ngOnDestroy(); } if (p.provider5 instanceof DirectiveProvider && (<DirectiveProvider>p.provider5).callOnDestroy) { i.obj5.ngOnDestroy(); } if (p.provider6 instanceof DirectiveProvider && (<DirectiveProvider>p.provider6).callOnDestroy) { i.obj6.ngOnDestroy(); } if (p.provider7 instanceof DirectiveProvider && (<DirectiveProvider>p.provider7).callOnDestroy) { i.obj7.ngOnDestroy(); } if (p.provider8 instanceof DirectiveProvider && (<DirectiveProvider>p.provider8).callOnDestroy) { i.obj8.ngOnDestroy(); } if (p.provider9 instanceof DirectiveProvider && (<DirectiveProvider>p.provider9).callOnDestroy) { i.obj9.ngOnDestroy(); } } getComponent(): any { return this.injectorStrategy.obj0; } isComponentKey(key: Key): boolean { return this._ei._proto._firstProviderIsComponent && isPresent(key) && key.id === this.injectorStrategy.protoStrategy.keyId0; } addDirectivesMatchingQuery(query: QueryMetadata, list: any[]): void { var i = this.injectorStrategy; var p = i.protoStrategy; if (isPresent(p.provider0) && p.provider0.key.token === query.selector) { if (i.obj0 === UNDEFINED) i.obj0 = i.instantiateProvider(p.provider0, p.visibility0); list.push(i.obj0); } if (isPresent(p.provider1) && p.provider1.key.token === query.selector) { if (i.obj1 === UNDEFINED) i.obj1 = i.instantiateProvider(p.provider1, p.visibility1); list.push(i.obj1); } if (isPresent(p.provider2) && p.provider2.key.token === query.selector) { if (i.obj2 === UNDEFINED) i.obj2 = i.instantiateProvider(p.provider2, p.visibility2); list.push(i.obj2); } if (isPresent(p.provider3) && p.provider3.key.token === query.selector) { if (i.obj3 === UNDEFINED) i.obj3 = i.instantiateProvider(p.provider3, p.visibility3); list.push(i.obj3); } if (isPresent(p.provider4) && p.provider4.key.token === query.selector) { if (i.obj4 === UNDEFINED) i.obj4 = i.instantiateProvider(p.provider4, p.visibility4); list.push(i.obj4); } if (isPresent(p.provider5) && p.provider5.key.token === query.selector) { if (i.obj5 === UNDEFINED) i.obj5 = i.instantiateProvider(p.provider5, p.visibility5); list.push(i.obj5); } if (isPresent(p.provider6) && p.provider6.key.token === query.selector) { if (i.obj6 === UNDEFINED) i.obj6 = i.instantiateProvider(p.provider6, p.visibility6); list.push(i.obj6); } if (isPresent(p.provider7) && p.provider7.key.token === query.selector) { if (i.obj7 === UNDEFINED) i.obj7 = i.instantiateProvider(p.provider7, p.visibility7); list.push(i.obj7); } if (isPresent(p.provider8) && p.provider8.key.token === query.selector) { if (i.obj8 === UNDEFINED) i.obj8 = i.instantiateProvider(p.provider8, p.visibility8); list.push(i.obj8); } if (isPresent(p.provider9) && p.provider9.key.token === query.selector) { if (i.obj9 === UNDEFINED) i.obj9 = i.instantiateProvider(p.provider9, p.visibility9); list.push(i.obj9); } } } /** * Strategy used by the `ElementInjector` when the number of bindings is 11 or more. * In such a case, there are too many fields to inline (see ElementInjectorInlineStrategy). */ class ElementInjectorDynamicStrategy implements _ElementInjectorStrategy { constructor(public injectorStrategy: InjectorDynamicStrategy, public _ei: ElementInjector) {} hydrate(): void { var inj = this.injectorStrategy; var p = inj.protoStrategy; inj.resetConstructionCounter(); for (var i = 0; i < p.keyIds.length; i++) { if (p.providers[i] instanceof DirectiveProvider && isPresent(p.keyIds[i]) && inj.objs[i] === UNDEFINED) { inj.objs[i] = inj.instantiateProvider(p.providers[i], p.visibilities[i]); } } } dehydrate(): void { var inj = this.injectorStrategy; ListWrapper.fill(inj.objs, UNDEFINED); } callOnDestroy(): void { var ist = this.injectorStrategy; var p = ist.protoStrategy; for (var i = 0; i < p.providers.length; i++) { if (p.providers[i] instanceof DirectiveProvider && (<DirectiveProvider>p.providers[i]).callOnDestroy) { ist.objs[i].ngOnDestroy(); } } } getComponent(): any { return this.injectorStrategy.objs[0]; } isComponentKey(key: Key): boolean { var p = this.injectorStrategy.protoStrategy; return this._ei._proto._firstProviderIsComponent && isPresent(key) && key.id === p.keyIds[0]; } addDirectivesMatchingQuery(query: QueryMetadata, list: any[]): void { var ist = this.injectorStrategy; var p = ist.protoStrategy; for (var i = 0; i < p.providers.length; i++) { if (p.providers[i].key.token === query.selector) { if (ist.objs[i] === UNDEFINED) { ist.objs[i] = ist.instantiateProvider(p.providers[i], p.visibilities[i]); } list.push(ist.objs[i]); } } } } export class ProtoQueryRef { constructor(public dirIndex: number, public setter: SetterFn, public query: QueryMetadata) {} get usesPropertySyntax(): boolean { return isPresent(this.setter); } } export class QueryRef { public list: QueryList<any>; public dirty: boolean; constructor(public protoQueryRef: ProtoQueryRef, private originator: ElementInjector) {} get isViewQuery(): boolean { return this.protoQueryRef.query.isViewQuery; } update(): void { if (!this.dirty) return; this._update(); this.dirty = false; // TODO delete the check once only field queries are supported if (this.protoQueryRef.usesPropertySyntax) { var dir = this.originator.getDirectiveAtIndex(this.protoQueryRef.dirIndex); if (this.protoQueryRef.query.first) { this.protoQueryRef.setter(dir, this.list.length > 0 ? this.list.first : null); } else { this.protoQueryRef.setter(dir, this.list); } } this.list.notifyOnChanges(); } private _update(): void { var aggregator = []; if (this.protoQueryRef.query.isViewQuery) { var view = this.originator.getView(); // intentionally skipping originator for view queries. var nestedView = view.getNestedView(view.elementOffset + this.originator.getBoundElementIndex()); if (isPresent(nestedView)) this._visitView(nestedView, aggregator); } else { this._visit(this.originator, aggregator); } this.list.reset(aggregator); }; private _visit(inj: ElementInjector, aggregator: any[]): void { var view = inj.getView(); var startIdx = view.elementOffset + inj._proto.index; for (var i = startIdx; i < view.elementOffset + view.ownBindersCount; i++) { var curInj = view.elementInjectors[i]; if (isBlank(curInj)) continue; // The first injector after inj, that is outside the subtree rooted at // inj has to have a null parent or a parent that is an ancestor of inj. if (i > startIdx && (isBlank(curInj) || isBlank(curInj.parent) || view.elementOffset + curInj.parent._proto.index < startIdx)) { break; } if (!this.protoQueryRef.query.descendants && !(curInj.parent == this.originator || curInj == this.originator)) continue; // We visit the view container(VC) views right after the injector that contains // the VC. Theoretically, that might not be the right order if there are // child injectors of said injector. Not clear whether if such case can // even be constructed with the current apis. this._visitInjector(curInj, aggregator); var vc = view.viewContainers[i]; if (isPresent(vc)) this._visitViewContainer(vc, aggregator); } } private _visitInjector(inj: ElementInjector, aggregator: any[]) { if (this.protoQueryRef.query.isVarBindingQuery) { this._aggregateVariableBinding(inj, aggregator); } else { this._aggregateDirective(inj, aggregator); } } private _visitViewContainer(vc: AppViewContainer, aggregator: any[]) { for (var j = 0; j < vc.views.length; j++) { this._visitView(vc.views[j], aggregator); } } private _visitView(view: AppView, aggregator: any[]) { for (var i = view.elementOffset; i < view.elementOffset + view.ownBindersCount; i++) { var inj = view.elementInjectors[i]; if (isBlank(inj)) continue; this._visitInjector(inj, aggregator); var vc = view.viewContainers[i]; if (isPresent(vc)) this._visitViewContainer(vc, aggregator); } } private _aggregateVariableBinding(inj: ElementInjector, aggregator: any[]): void { var vb = this.protoQueryRef.query.varBindings; for (var i = 0; i < vb.length; ++i) { if (inj.hasVariableBinding(vb[i])) { aggregator.push(inj.getVariableBinding(vb[i])); } } } private _aggregateDirective(inj: ElementInjector, aggregator: any[]): void { inj.addDirectivesMatchingQuery(this.protoQueryRef.query, aggregator); } dehydrate(): void { this.list = null; } hydrate(): void { this.list = new QueryList<any>(); this.dirty = true; } }