From 5dbe2926154d8b55a4bd526287637be1b276d3f4 Mon Sep 17 00:00:00 2001 From: vsavkin Date: Thu, 17 Sep 2015 18:45:14 -0700 Subject: [PATCH] feat(core): add support for ContentChildren and ViewChildren --- .../src/core/compiler/element_injector.ts | 69 +++++++++++----- modules/angular2/src/core/metadata.dart | 20 +++++ modules/angular2/src/core/metadata.ts | 47 ++++++++++- modules/angular2/src/core/metadata/di.ts | 54 +++++++++++++ .../angular2/src/core/metadata/directives.ts | 45 ++++++++++- modules/angular2/src/core/render/api.ts | 15 ++-- .../src/mock/directive_resolver_mock.ts | 4 +- .../core/compiler/query_integration_spec.ts | 78 ++++++++++++++++++- modules/angular2/test/public_api_spec.ts | 48 ++++++++++++ 9 files changed, 348 insertions(+), 32 deletions(-) diff --git a/modules/angular2/src/core/compiler/element_injector.ts b/modules/angular2/src/core/compiler/element_injector.ts index d1f7451c35..d3e8eb6d66 100644 --- a/modules/angular2/src/core/compiler/element_injector.ts +++ b/modules/angular2/src/core/compiler/element_injector.ts @@ -47,6 +47,7 @@ import { } 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 {RenderDirectiveMetadata} from 'angular2/src/core/render/api'; import {EventConfig} from 'angular2/src/core/render/event_config'; import {PipeBinding} from '../pipes/pipe_binding'; @@ -138,6 +139,17 @@ export class DirectiveBinding extends ResolvedBinding { get callOnDestroy(): boolean { return this.metadata.callOnDestroy; } + 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.events) ? this.metadata.events : []; } @@ -151,6 +163,7 @@ export class DirectiveBinding extends ResolvedBinding { var rf = rb.resolvedFactories[0]; var deps = rf.dependencies.map(DirectiveDependency.createFrom); var token = binding.token; + var metadata = RenderDirectiveMetadata.create({ id: stringify(binding.token), type: meta instanceof ComponentMetadata ? RenderDirectiveMetadata.COMPONENT_TYPE : @@ -161,6 +174,7 @@ export class DirectiveBinding extends ResolvedBinding { host: isPresent(meta.host) ? MapWrapper.createFromStringMap(meta.host) : null, properties: meta.properties, readAttributes: DirectiveBinding._readAttributes(deps), + queries: meta.queries, callOnDestroy: hasLifecycleHook(LifecycleHooks.OnDestroy, token), callOnChanges: hasLifecycleHook(LifecycleHooks.OnChanges, token), @@ -203,6 +217,10 @@ export class PreBuiltObjects { 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) {} @@ -214,17 +232,6 @@ export class EventEmitterAccessor { } } -export class HostActionAccessor { - constructor(public methodName: string, public getter: Function) {} - - subscribe(view: viewModule.AppView, boundElementIndex: number, directive: Object): Object { - var eventEmitter = this.getter(directive); - return ObservableWrapper.subscribe( - eventEmitter, - actionArgs => view.invokeElementMethod(boundElementIndex, this.methodName, actionArgs)); - } -} - function _createEventEmitterAccessors(bwv: BindingWithVisibility): EventEmitterAccessor[] { var binding = bwv.binding; if (!(binding instanceof DirectiveBinding)) return []; @@ -554,19 +561,25 @@ export class ElementInjector extends TreeNode implements Depend for (var i = 0; i < deps.length; i++) { var dep = deps[i]; if (isPresent(dep.queryDecorator)) { - this._createQueryRef(dep.queryDecorator); + this._createQueryRef(null, null, dep.queryDecorator); } } } + _buildQueriesForDirective(dirIndex: number, meta: QueryMetadataWithSetter[]): void { + for (var i = 0; i < meta.length; i++) { + var m = meta[i]; + this._createQueryRef(dirIndex, m.setter, m.metadata); + } + } - private _createQueryRef(query: QueryMetadata): void { + private _createQueryRef(dirIndex: number, setter: SetterFn, query: QueryMetadata): void { var queryList = new QueryList(); if (isBlank(this._query0)) { - this._query0 = new QueryRef(query, queryList, this); + this._query0 = new QueryRef(dirIndex, setter, query, queryList, this); } else if (isBlank(this._query1)) { - this._query1 = new QueryRef(query, queryList, this); + this._query1 = new QueryRef(dirIndex, setter, query, queryList, this); } else if (isBlank(this._query2)) { - this._query2 = new QueryRef(query, queryList, this); + this._query2 = new QueryRef(dirIndex, setter, query, queryList, this); } else { throw new QueryError(); } @@ -758,42 +771,54 @@ class ElementInjectorInlineStrategy implements _ElementInjectorStrategy { if (p.binding0 instanceof DirectiveBinding) { this._ei._buildQueriesForDeps( p.binding0.resolvedFactories[0].dependencies); + + this._ei._buildQueriesForDirective(0, (p.binding0).queries); } if (p.binding1 instanceof DirectiveBinding) { this._ei._buildQueriesForDeps( p.binding1.resolvedFactories[0].dependencies); + + this._ei._buildQueriesForDirective(1, (p.binding1).queries); } if (p.binding2 instanceof DirectiveBinding) { this._ei._buildQueriesForDeps( p.binding2.resolvedFactories[0].dependencies); + this._ei._buildQueriesForDirective(2, (p.binding2).queries); } if (p.binding3 instanceof DirectiveBinding) { this._ei._buildQueriesForDeps( p.binding3.resolvedFactories[0].dependencies); + this._ei._buildQueriesForDirective(3, (p.binding3).queries); } if (p.binding4 instanceof DirectiveBinding) { this._ei._buildQueriesForDeps( p.binding4.resolvedFactories[0].dependencies); + this._ei._buildQueriesForDirective(4, (p.binding4).queries); } if (p.binding5 instanceof DirectiveBinding) { this._ei._buildQueriesForDeps( p.binding5.resolvedFactories[0].dependencies); + this._ei._buildQueriesForDirective(5, (p.binding5).queries); } if (p.binding6 instanceof DirectiveBinding) { this._ei._buildQueriesForDeps( p.binding6.resolvedFactories[0].dependencies); + this._ei._buildQueriesForDirective(6, (p.binding6).queries); } if (p.binding7 instanceof DirectiveBinding) { this._ei._buildQueriesForDeps( p.binding7.resolvedFactories[0].dependencies); + this._ei._buildQueriesForDirective(7, (p.binding7).queries); } if (p.binding8 instanceof DirectiveBinding) { this._ei._buildQueriesForDeps( p.binding8.resolvedFactories[0].dependencies); + this._ei._buildQueriesForDirective(8, (p.binding8).queries); } if (p.binding9 instanceof DirectiveBinding) { this._ei._buildQueriesForDeps( p.binding9.resolvedFactories[0].dependencies); + this._ei._buildQueriesForDirective(9, (p.binding9).queries); } } @@ -896,6 +921,7 @@ class ElementInjectorDynamicStrategy implements _ElementInjectorStrategy { if (p.bindings[i] instanceof DirectiveBinding) { this._ei._buildQueriesForDeps( p.bindings[i].resolvedFactory.dependencies); + this._ei._buildQueriesForDirective(i, (p.bindings[i]).queries); } } } @@ -927,8 +953,9 @@ export class QueryError extends BaseException { } export class QueryRef { - constructor(public query: QueryMetadata, public list: QueryList, - public originator: ElementInjector, public dirty: boolean = true) {} + constructor(public dirIndex: number, public setter: SetterFn, public query: QueryMetadata, + public list: QueryList, public originator: ElementInjector, + public dirty: boolean = true) {} get isViewQuery(): boolean { return this.query.isViewQuery; } @@ -936,6 +963,12 @@ export class QueryRef { if (!this.dirty) return; this._update(); this.dirty = false; + + // TODO delete the check once only field queries are supported + if (isPresent(this.dirIndex)) { + var dir = this.originator.getDirectiveAtIndex(this.dirIndex); + this.setter(dir, this.list); + } } private _update(): void { diff --git a/modules/angular2/src/core/metadata.dart b/modules/angular2/src/core/metadata.dart index 4afc466aef..06df4f3fae 100644 --- a/modules/angular2/src/core/metadata.dart +++ b/modules/angular2/src/core/metadata.dart @@ -17,6 +17,7 @@ class Directive extends DirectiveMetadata { const Directive({String selector, List properties, List events, Map host, List bindings, String exportAs, String moduleId, + Map queries, bool compileChildren: true}) : super( selector: selector, @@ -26,6 +27,7 @@ class Directive extends DirectiveMetadata { bindings: bindings, exportAs: exportAs, moduleId: moduleId, + queries: queries, compileChildren: compileChildren); } @@ -36,6 +38,7 @@ class Component extends ComponentMetadata { const Component({String selector, List properties, List events, Map host, bool dynamicLoadable, List bindings, String exportAs, String moduleId, + Map queries, bool compileChildren, List viewBindings, ChangeDetectionStrategy changeDetection}) : super( selector: selector, @@ -48,6 +51,7 @@ class Component extends ComponentMetadata { moduleId: moduleId, compileChildren: compileChildren, viewBindings: viewBindings, + queries: queries, changeDetection: changeDetection); } @@ -90,6 +94,14 @@ class Query extends QueryMetadata { : super(selector, descendants: descendants); } +/** + * See: [ContentChildrenMetadata] for docs. + */ +class ContentChildren extends ContentChildrenMetadata { + const ContentChildren(dynamic /*Type | string*/ selector, {bool descendants: false}) + : super(selector, descendants: descendants); +} + /** * See: [ViewQueryMetadata] for docs. */ @@ -98,6 +110,14 @@ class ViewQuery extends ViewQueryMetadata { : super(selector, descendants: true); } +/** + * See: [ViewChildrenMetadata] for docs. + */ +class ViewChildren extends ViewChildrenMetadata { + const ViewChildren(dynamic /*Type | string*/ selector) + : super(selector); +} + /** * See: [PropertyMetadata] for docs. */ diff --git a/modules/angular2/src/core/metadata.ts b/modules/angular2/src/core/metadata.ts index 02d4e30d76..dd0070807c 100644 --- a/modules/angular2/src/core/metadata.ts +++ b/modules/angular2/src/core/metadata.ts @@ -5,6 +5,8 @@ export { QueryMetadata, + ContentChildrenMetadata, + ViewChildrenMetadata, ViewQueryMetadata, AttributeMetadata, } from './metadata/di'; @@ -23,6 +25,8 @@ export {ViewMetadata, ViewEncapsulation} from './metadata/view'; import { QueryMetadata, + ContentChildrenMetadata, + ViewChildrenMetadata, ViewQueryMetadata, AttributeMetadata, } from './metadata/di'; @@ -138,12 +142,26 @@ export interface ViewDecorator extends TypeDecorator { */ export interface DirectiveFactory { (obj: { - selector?: string, properties?: string[], events?: string[], host?: StringMap, - bindings?: any[], exportAs?: string, moduleId?: string, compileChildren?: boolean; + selector?: string, + properties?: string[], + events?: string[], + host?: StringMap, + bindings?: any[], + exportAs?: string, + moduleId?: string, + compileChildren?: boolean, + queries?: StringMap }): DirectiveDecorator; new (obj: { - selector?: string, properties?: string[], events?: string[], host?: StringMap, - bindings?: any[], exportAs?: string, moduleId?: string, compileChildren?: boolean; + selector?: string, + properties?: string[], + events?: string[], + host?: StringMap, + bindings?: any[], + exportAs?: string, + moduleId?: string, + compileChildren?: boolean, + queries?: StringMap }): DirectiveMetadata; } @@ -201,6 +219,7 @@ export interface ComponentFactory { exportAs?: string, moduleId?: string, compileChildren?: boolean, + queries?: StringMap, viewBindings?: any[], changeDetection?: ChangeDetectionStrategy, }): ComponentDecorator; @@ -214,6 +233,7 @@ export interface ComponentFactory { exportAs?: string, moduleId?: string, compileChildren?: boolean, + queries?: StringMap, viewBindings?: any[], changeDetection?: ChangeDetectionStrategy, }): ComponentMetadata; @@ -383,6 +403,16 @@ export interface QueryFactory { new (selector: Type | string, {descendants}?: {descendants?: boolean}): QueryMetadata; } +export interface ContentChildrenFactory { + (selector: Type | string, {descendants}?: {descendants?: boolean}): any; + new (selector: Type | string, {descendants}?: {descendants?: boolean}): ContentChildrenMetadata; +} + +export interface ViewChildrenFactory { + (selector: Type | string): any; + new (selector: Type | string): ViewChildrenMetadata; +} + /** * {@link PipeMetadata} factory for creating decorators. * @@ -511,6 +541,15 @@ export var Attribute: AttributeFactory = makeParamDecorator(AttributeMetadata); */ export var Query: QueryFactory = makeParamDecorator(QueryMetadata); +/** + * {@link ContentChildrenMetadata} factory function. + */ +export var ContentChildren: ContentChildrenFactory = makePropDecorator(ContentChildrenMetadata); + +/** + * {@link ViewChildrenMetadata} factory function. + */ +export var ViewChildren: ViewChildrenFactory = makePropDecorator(ViewChildrenMetadata); /** * {@link ViewQueryMetadata} factory function. diff --git a/modules/angular2/src/core/metadata/di.ts b/modules/angular2/src/core/metadata/di.ts index 8b2ea0a502..85539047ef 100644 --- a/modules/angular2/src/core/metadata/di.ts +++ b/modules/angular2/src/core/metadata/di.ts @@ -201,6 +201,34 @@ export class QueryMetadata extends DependencyMetadata { toString(): string { return `@Query(${stringify(this.selector)})`; } } +// TODO: add an example after ContentChildren and ViewChildren are in master +/** + * Configures a content query. + * + * Content queries are set before the `afterContentInit` callback is called. + * + * ### Example + * + * ``` + * @Directive({ + * selector: 'someDir' + * }) + * class SomeDir { + * @ContentChildren(ChildDirective) contentChildren: QueryList; + * + * afterContentInit() { + * // contentChildren is set + * } + * } + * ``` + */ +@CONST() +export class ContentChildrenMetadata extends QueryMetadata { + constructor(_selector: Type | string, {descendants = false}: {descendants?: boolean} = {}) { + super(_selector, {descendants: descendants}); + } +} + /** * Similar to {@link QueryMetadata}, but querying the component view, instead of * the content children. @@ -248,3 +276,29 @@ export class ViewQueryMetadata extends QueryMetadata { get isViewQuery() { return true; } toString(): string { return `@ViewQuery(${stringify(this.selector)})`; } } + +/** + * Configures a view query. + * + * View queries are set before the `afterViewInit` callback is called. + * + * ### Example + * + * ``` + * @Component({ + * selector: 'someDir' + * }) + * @View({templateUrl: 'someTemplate', directives: [ItemDirective]}) + * class SomeDir { + * @ViewChildren(ItemDirective) viewChildren: QueryList; + * + * afterViewInit() { + * // viewChildren is set + * } + * } + * ``` + */ +@CONST() +export class ViewChildrenMetadata extends ViewQueryMetadata { + constructor(_selector: Type | string) { super(_selector, {descendants: true}); } +} diff --git a/modules/angular2/src/core/metadata/directives.ts b/modules/angular2/src/core/metadata/directives.ts index e60f92ebc5..d7119dc8c4 100644 --- a/modules/angular2/src/core/metadata/directives.ts +++ b/modules/angular2/src/core/metadata/directives.ts @@ -719,8 +719,45 @@ export class DirectiveMetadata extends InjectableMetadata { */ moduleId: string; + // TODO: add an example after ContentChildren and ViewChildren are in master + /** + * Configures the queries that will be injected into the directive. + * + * Content queries are set before the `afterContentInit` callback is called. + * View queries are set before the `afterViewInit` callback is called. + * + * ### Example + * + * ``` + * @Component({ + * selector: 'someDir', + * queries: { + * contentChildren: new ContentChildren(ChildDirective), + * viewChildren: new ViewChildren(ChildDirective) + * } + * }) + * @View({ + * template: '', + * directives: [ChildDirective] + * }) + * class SomeDir { + * contentChildren: QueryList, + * viewChildren: QueryList + * + * afterContentInit() { + * // contentChildren is set + * } + * + * afterViewInit() { + * // viewChildren is set + * } + * } + * ``` + */ + queries: StringMap; + constructor({ - selector, properties, events, host, bindings, exportAs, moduleId, + selector, properties, events, host, bindings, exportAs, moduleId, queries, compileChildren = true, }: { selector?: string, @@ -730,6 +767,7 @@ export class DirectiveMetadata extends InjectableMetadata { bindings?: any[], exportAs?: string, moduleId?: string, + queries?: StringMap, compileChildren?: boolean, } = {}) { super(); @@ -739,6 +777,7 @@ export class DirectiveMetadata extends InjectableMetadata { this.host = host; this.exportAs = exportAs; this.moduleId = moduleId; + this.queries = queries; this.compileChildren = compileChildren; this.bindings = bindings; } @@ -862,7 +901,7 @@ export class ComponentMetadata extends DirectiveMetadata { viewBindings: any[]; constructor({selector, properties, events, host, dynamicLoadable, exportAs, moduleId, bindings, - viewBindings, changeDetection = ChangeDetectionStrategy.Default, + queries, viewBindings, changeDetection = ChangeDetectionStrategy.Default, compileChildren = true}: { selector?: string, properties?: string[], @@ -874,6 +913,7 @@ export class ComponentMetadata extends DirectiveMetadata { moduleId?: string, compileChildren?: boolean, viewBindings?: any[], + queries?: StringMap, changeDetection?: ChangeDetectionStrategy, } = {}) { super({ @@ -884,6 +924,7 @@ export class ComponentMetadata extends DirectiveMetadata { exportAs: exportAs, moduleId: moduleId, bindings: bindings, + queries: queries, compileChildren: compileChildren }); diff --git a/modules/angular2/src/core/render/api.ts b/modules/angular2/src/core/render/api.ts index bb39861c56..b2389cb64f 100644 --- a/modules/angular2/src/core/render/api.ts +++ b/modules/angular2/src/core/render/api.ts @@ -160,6 +160,7 @@ export class RenderDirectiveMetadata { hostListeners: Map; hostProperties: Map; hostAttributes: Map; + queries: StringMap; // group 1: "property" from "[property]" // group 2: "event" from "(event)" private static _hostRegExp = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g; @@ -167,7 +168,7 @@ export class RenderDirectiveMetadata { constructor({id, selector, compileChildren, events, hostListeners, hostProperties, hostAttributes, properties, readAttributes, type, callOnDestroy, callOnChanges, callDoCheck, callOnInit, callAfterContentInit, callAfterContentChecked, callAfterViewInit, - callAfterViewChecked, changeDetection, exportAs}: { + callAfterViewChecked, changeDetection, exportAs, queries}: { id?: string, selector?: string, compileChildren?: boolean, @@ -187,7 +188,8 @@ export class RenderDirectiveMetadata { callAfterViewInit?: boolean, callAfterViewChecked?: boolean, changeDetection?: ChangeDetectionStrategy, - exportAs?: string + exportAs?: string, + queries?: StringMap }) { this.id = id; this.selector = selector; @@ -209,12 +211,13 @@ export class RenderDirectiveMetadata { this.callAfterViewChecked = callAfterViewChecked; this.changeDetection = changeDetection; this.exportAs = exportAs; + this.queries = queries; } static create({id, selector, compileChildren, events, host, properties, readAttributes, type, callOnDestroy, callOnChanges, callDoCheck, callOnInit, callAfterContentInit, callAfterContentChecked, callAfterViewInit, callAfterViewChecked, changeDetection, - exportAs}: { + exportAs, queries}: { id?: string, selector?: string, compileChildren?: boolean, @@ -232,7 +235,8 @@ export class RenderDirectiveMetadata { callAfterViewInit?: boolean, callAfterViewChecked?: boolean, changeDetection?: ChangeDetectionStrategy, - exportAs?: string + exportAs?: string, + queries?: StringMap }): RenderDirectiveMetadata { let hostListeners = new Map(); let hostProperties = new Map(); @@ -271,7 +275,8 @@ export class RenderDirectiveMetadata { callAfterViewInit: callAfterViewInit, callAfterViewChecked: callAfterViewChecked, changeDetection: changeDetection, - exportAs: exportAs + exportAs: exportAs, + queries: queries }); } } diff --git a/modules/angular2/src/mock/directive_resolver_mock.ts b/modules/angular2/src/mock/directive_resolver_mock.ts index c4d7cf2cfd..4c41225d16 100644 --- a/modules/angular2/src/mock/directive_resolver_mock.ts +++ b/modules/angular2/src/mock/directive_resolver_mock.ts @@ -34,6 +34,7 @@ export class MockDirectiveResolver extends DirectiveResolver { exportAs: dm.exportAs, moduleId: dm.moduleId, compileChildren: dm.compileChildren, + queries: dm.queries, changeDetection: dm.changeDetection, viewBindings: viewBindings }); @@ -47,7 +48,8 @@ export class MockDirectiveResolver extends DirectiveResolver { bindings: bindings, exportAs: dm.exportAs, moduleId: dm.moduleId, - compileChildren: dm.compileChildren + compileChildren: dm.compileChildren, + queries: dm.queries }); } diff --git a/modules/angular2/test/core/compiler/query_integration_spec.ts b/modules/angular2/test/core/compiler/query_integration_spec.ts index 7c6569df83..00e22f6c2e 100644 --- a/modules/angular2/test/core/compiler/query_integration_spec.ts +++ b/modules/angular2/test/core/compiler/query_integration_spec.ts @@ -24,7 +24,11 @@ import { Query, QueryList, View, - ViewQuery + ViewQuery, + ContentChildren, + ViewChildren, + AfterContentInit, + AfterViewInit } from 'angular2/core'; import {asNativeElements} from 'angular2/src/core/debug'; @@ -36,7 +40,7 @@ export function main() { describe('Query API', () => { describe("querying by directive type", () => { - it('should contain all direct child directives in the light dom', + it('should contain all direct child directives in the light dom (constructor)', inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { var template = '
' + '
' + @@ -56,6 +60,27 @@ export function main() { }); })); + it('should contain all direct child directives in the content dom', + inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { + var template = + '
'; + + tcb.overrideTemplate(MyComp, template) + .createAsync(MyComp) + .then((view) => { + view.detectChanges(); + + var q = view.debugElement.componentViewChildren[0].getLocal('q'); + + view.detectChanges(); + + expect(q.textDirChildren.length).toEqual(1); + expect(q.numberOfChildrenAfterContentInit).toEqual(1); + + async.done(); + }); + })); + it('should contain all directives in the light dom when descendants flag is used', inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { var template = '
' + @@ -385,6 +410,27 @@ export function main() { async.done(); }); })); + + it('should contain all child directives in the view dom', + inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { + var template = ''; + + tcb.overrideTemplate(MyComp, template) + .createAsync(MyComp) + .then((view) => { + view.detectChanges(); + + var q = view.debugElement.componentViewChildren[0].getLocal('q'); + + view.detectChanges(); + + expect(q.textDirChildren.length).toEqual(1); + expect(q.numberOfChildrenAfterViewInit).toEqual(1); + + async.done(); + }); + })); + }); describe("querying in the view", () => { @@ -552,6 +598,32 @@ class TextDirective { constructor() {} } +@Component({ + selector: 'needs-content-children', + queries: {'textDirChildren': new ContentChildren(TextDirective)} +}) +@View({template: ''}) +class NeedsContentChildren implements AfterContentInit { + textDirChildren: QueryList; + numberOfChildrenAfterContentInit: number; + + afterContentInit() { this.numberOfChildrenAfterContentInit = this.textDirChildren.length; } +} + +@Component({ + selector: 'needs-view-children', + queries: { + 'textDirChildren': new ViewChildren(TextDirective), + } +}) +@View({template: '
', directives: [TextDirective]}) +class NeedsViewChildren implements AfterViewInit { + textDirChildren: QueryList; + numberOfChildrenAfterViewInit: number; + + afterViewInit() { this.numberOfChildrenAfterViewInit = this.textDirChildren.length; } +} + @Directive({selector: '[dir]'}) @Injectable() class InertDirective { @@ -718,6 +790,8 @@ class NeedsTpl { NeedsViewQueryOrder, NeedsViewQueryByLabel, NeedsViewQueryOrderWithParent, + NeedsContentChildren, + NeedsViewChildren, NeedsTpl, TextDirective, InertDirective, diff --git a/modules/angular2/test/public_api_spec.ts b/modules/angular2/test/public_api_spec.ts index 36414996aa..2a8a60dca6 100644 --- a/modules/angular2/test/public_api_spec.ts +++ b/modules/angular2/test/public_api_spec.ts @@ -180,6 +180,30 @@ const NG_API = [ 'ComponentUrlMapper', 'ComponentUrlMapper.getUrl', + 'ContentChildren', + 'ContentChildren.constructor', + 'ContentChildren.constructor.constructor', + 'ContentChildren.constructor.isVarBindingQuery', + 'ContentChildren.constructor.isViewQuery', + 'ContentChildren.constructor.selector', + 'ContentChildren.constructor.toString', + 'ContentChildren.constructor.token', + 'ContentChildren.constructor.varBindings', + 'ContentChildren.isVarBindingQuery', + 'ContentChildren.isViewQuery', + 'ContentChildren.selector', + 'ContentChildren.toString', + 'ContentChildren.token', + 'ContentChildren.varBindings', + 'ContentChildrenMetadata', + 'ContentChildrenMetadata.constructor', + 'ContentChildrenMetadata.isVarBindingQuery', + 'ContentChildrenMetadata.isViewQuery', + 'ContentChildrenMetadata.selector', + 'ContentChildrenMetadata.toString', + 'ContentChildrenMetadata.token', + 'ContentChildrenMetadata.varBindings', + 'Control', 'Control.constructor', 'Control.dirty', @@ -836,6 +860,30 @@ const NG_API = [ 'View', + 'ViewChildren', + 'ViewChildren.constructor', + 'ViewChildren.constructor.constructor', + 'ViewChildren.constructor.isVarBindingQuery', + 'ViewChildren.constructor.isViewQuery', + 'ViewChildren.constructor.selector', + 'ViewChildren.constructor.toString', + 'ViewChildren.constructor.token', + 'ViewChildren.constructor.varBindings', + 'ViewChildren.isVarBindingQuery', + 'ViewChildren.isViewQuery', + 'ViewChildren.selector', + 'ViewChildren.toString', + 'ViewChildren.token', + 'ViewChildren.varBindings', + 'ViewChildrenMetadata', + 'ViewChildrenMetadata.constructor', + 'ViewChildrenMetadata.isVarBindingQuery', + 'ViewChildrenMetadata.isViewQuery', + 'ViewChildrenMetadata.selector', + 'ViewChildrenMetadata.toString', + 'ViewChildrenMetadata.token', + 'ViewChildrenMetadata.varBindings', + 'ViewContainerRef', 'ViewContainerRef.clear', 'ViewContainerRef.createEmbeddedView',