/** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import {resolveForwardRef} from '../di/forward_ref'; import {DependencyMetadata} from '../di/metadata'; import {OpaqueToken} from '../di/opaque_token'; import {StringWrapper, Type, isString, stringify} from '../facade/lang'; /** * This token can be used to create a virtual provider that will populate the * `precompile` fields of components and ng modules based on its `useValue`. * All components that are referenced in the `useValue` value (either directly * or in a nested array or map) will be added to the `precompile` property. * * ### Example * The following example shows how the router can populate the `precompile` * field of an NgModule based on the router configuration which refers * to components. * * ```typescript * // helper function inside the router * function provideRoutes(routes) { * return [ * {provide: ROUTES, useValue: routes}, * {provide: ANALYZE_FOR_PRECOMPILE, useValue: routes, multi: true} * ]; * } * * // user code * let routes = [ * {path: '/root', component: RootComp}, * {path: /teams', component: TeamsComp} * ]; * * @NgModule({ * providers: [provideRoutes(routes)] * }) * class ModuleWithRoutes {} * ``` * * @experimental */ export const ANALYZE_FOR_PRECOMPILE = new OpaqueToken('AnalyzeForPrecompile'); /** * Specifies that a constant attribute value should be injected. * * The directive can inject constant string literals of host element attributes. * * ### Example * * Suppose we have an `` element and want to know its `type`. * * ```html * * ``` * * A decorator can inject string literal `text` like so: * * {@example core/ts/metadata/metadata.ts region='attributeMetadata'} * @ts2dart_const * @stable */ export class AttributeMetadata extends DependencyMetadata { constructor(public attributeName: string) { super(); } get token(): AttributeMetadata { // Normally one would default a token to a type of an injected value but here // the type of a variable is "string" and we can't use primitive type as a return value // so we use instance of Attribute instead. This doesn't matter much in practice as arguments // with @Attribute annotation are injected by ElementInjector that doesn't take tokens into // account. return this; } toString(): string { return `@Attribute(${stringify(this.attributeName)})`; } } /** * Declares an injectable parameter to be a live list of directives or variable * bindings from the content children of a directive. * * ### Example ([live demo](http://plnkr.co/edit/lY9m8HLy7z06vDoUaSN2?p=preview)) * * Assume that `` component would like to get a list its children `` * components as shown in this example: * * ```html * * ... * {{o.text}} * * ``` * * The preferred solution is to query for `Pane` directives using this decorator. * * ```javascript * @Component({ * selector: 'pane', * inputs: ['title'] * }) * class Pane { * title:string; * } * * @Component({ * selector: 'tabs', * template: ` * * * ` * }) * class Tabs { * panes: QueryList; * constructor(@Query(Pane) panes:QueryList) { * this.panes = panes; * } * } * ``` * * A query can look for variable bindings by passing in a string with desired binding symbol. * * ### Example ([live demo](http://plnkr.co/edit/sT2j25cH1dURAyBRCKx1?p=preview)) * ```html * *
...
*
* * @Component({ selector: 'seeker' }) * class Seeker { * constructor(@Query('findme') elList: QueryList) {...} * } * ``` * * In this case the object that is injected depend on the type of the variable * binding. It can be an ElementRef, a directive or a component. * * Passing in a comma separated list of variable bindings will query for all of them. * * ```html * *
...
*
...
*
* * @Component({ * selector: 'seeker' * }) * class Seeker { * constructor(@Query('findMe, findMeToo') elList: QueryList) {...} * } * ``` * * Configure whether query looks for direct children or all descendants * of the querying element, by using the `descendants` parameter. * It is set to `false` by default. * * ### Example ([live demo](http://plnkr.co/edit/wtGeB977bv7qvA5FTYl9?p=preview)) * ```html * * a * b * * c * * * ``` * * When querying for items, the first container will see only `a` and `b` by default, * but with `Query(TextDirective, {descendants: true})` it will see `c` too. * * The queried directives are kept in a depth-first pre-order with respect to their * positions in the DOM. * * Query does not look deep into any subcomponent views. * * Query is updated as part of the change-detection cycle. Since change detection * happens after construction of a directive, QueryList will always be empty when observed in the * constructor. * * The injected object is an unmodifiable live list. * See {@link QueryList} for more details. * @ts2dart_const * @deprecated */ export class QueryMetadata extends DependencyMetadata { /** * whether we want to query only direct children (false) or all * children (true). */ descendants: boolean; first: boolean; /** * The DI token to read from an element that matches the selector. */ read: any; constructor(private _selector: Type|string, {descendants = false, first = false, read = null}: { descendants?: boolean, first?: boolean, read?: any } = {}) { super(); this.descendants = descendants; this.first = first; this.read = read; } /** * always `false` to differentiate it with {@link ViewQueryMetadata}. */ get isViewQuery(): boolean { return false; } /** * what this is querying for. */ get selector() { return resolveForwardRef(this._selector); } /** * whether this is querying for a variable binding or a directive. */ get isVarBindingQuery(): boolean { return isString(this.selector); } /** * returns a list of variable bindings this is querying for. * Only applicable if this is a variable bindings query. */ get varBindings(): string[] { return StringWrapper.split(this.selector, /\s*,\s*/g); } 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 `ngAfterContentInit` callback is called. * * ### Example * * ``` * @Directive({ * selector: 'someDir' * }) * class SomeDir { * @ContentChildren(ChildDirective) contentChildren: QueryList; * * ngAfterContentInit() { * // contentChildren is set * } * } * ``` * @ts2dart_const * @stable */ export class ContentChildrenMetadata extends QueryMetadata { constructor( _selector: Type|string, {descendants = false, read = null}: {descendants?: boolean, read?: any} = {}) { super(_selector, {descendants: descendants, read: read}); } } // TODO: add an example after ContentChild and ViewChild are in master /** * Configures a content query. * * Content queries are set before the `ngAfterContentInit` callback is called. * * ### Example * * ``` * @Directive({ * selector: 'someDir' * }) * class SomeDir { * @ContentChild(ChildDirective) contentChild; * * ngAfterContentInit() { * // contentChild is set * } * } * ``` * @ts2dart_const * @stable */ export class ContentChildMetadata extends QueryMetadata { constructor(_selector: Type|string, {read = null}: {read?: any} = {}) { super(_selector, {descendants: true, first: true, read: read}); } } /** * Similar to {@link QueryMetadata}, but querying the component view, instead of * the content children. * * ### Example ([live demo](http://plnkr.co/edit/eNsFHDf7YjyM6IzKxM1j?p=preview)) * * ```javascript * @Component({ * ..., * template: ` * a * b * c * ` * }) * class MyComponent { * shown: boolean; * * constructor(private @ViewQuery(Item) items:QueryList) { * items.changes.subscribe(() => console.log(items.length)); * } * } * ``` * * Supports the same querying parameters as {@link QueryMetadata}, except * `descendants`. This always queries the whole view. * * As `shown` is flipped between true and false, items will contain zero of one * items. * * Specifies that a {@link QueryList} should be injected. * * The injected object is an iterable and observable live list. * See {@link QueryList} for more details. * @ts2dart_const * @deprecated */ export class ViewQueryMetadata extends QueryMetadata { constructor( _selector: Type|string, {descendants = false, first = false, read = null}: {descendants?: boolean, first?: boolean, read?: any} = {}) { super(_selector, {descendants: descendants, first: first, read: read}); } /** * always `true` to differentiate it with {@link QueryMetadata}. */ get isViewQuery() { return true; } toString(): string { return `@ViewQuery(${stringify(this.selector)})`; } } /** * Declares a list of child element references. * * Angular automatically updates the list when the DOM is updated. * * `ViewChildren` takes an argument to select elements. * * - If the argument is a type, directives or components with the type will be bound. * * - If the argument is a string, the string is interpreted as a list of comma-separated selectors. * For each selector, an element containing the matching template variable (e.g. `#child`) will be * bound. * * View children are set before the `ngAfterViewInit` callback is called. * * ### Example * * With type selector: * * ``` * @Component({ * selector: 'child-cmp', * template: '

child

' * }) * class ChildCmp { * doSomething() {} * } * * @Component({ * selector: 'some-cmp', * template: ` * * * * `, * directives: [ChildCmp] * }) * class SomeCmp { * @ViewChildren(ChildCmp) children:QueryList; * * ngAfterViewInit() { * // children are set * this.children.toArray().forEach((child)=>child.doSomething()); * } * } * ``` * * With string selector: * * ``` * @Component({ * selector: 'child-cmp', * template: '

child

' * }) * class ChildCmp { * doSomething() {} * } * * @Component({ * selector: 'some-cmp', * template: ` * * * * `, * directives: [ChildCmp] * }) * class SomeCmp { * @ViewChildren('child1,child2,child3') children:QueryList; * * ngAfterViewInit() { * // children are set * this.children.toArray().forEach((child)=>child.doSomething()); * } * } * ``` * @ts2dart_const * @stable */ export class ViewChildrenMetadata extends ViewQueryMetadata { constructor(_selector: Type|string, {read = null}: {read?: any} = {}) { super(_selector, {descendants: true, read: read}); } } /** * * Declares a reference of child element. * * `ViewChildren` takes an argument to select elements. * * - If the argument is a type, a directive or a component with the type will be bound. * If the argument is a string, the string is interpreted as a selector. An element containing the matching template variable (e.g. `#child`) will be bound. * * In either case, `@ViewChild()` assigns the first (looking from above) element if there are multiple matches. * * View child is set before the `ngAfterViewInit` callback is called. * * ### Example * * With type selector: * * ``` * @Component({ * selector: 'child-cmp', * template: '

child

' * }) * class ChildCmp { * doSomething() {} * } * * @Component({ * selector: 'some-cmp', * template: '', * directives: [ChildCmp] * }) * class SomeCmp { * @ViewChild(ChildCmp) child:ChildCmp; * * ngAfterViewInit() { * // child is set * this.child.doSomething(); * } * } * ``` * * With string selector: * * ``` * @Component({ * selector: 'child-cmp', * template: '

child

' * }) * class ChildCmp { * doSomething() {} * } * * @Component({ * selector: 'some-cmp', * template: '', * directives: [ChildCmp] * }) * class SomeCmp { * @ViewChild('child') child:ChildCmp; * * ngAfterViewInit() { * // child is set * this.child.doSomething(); * } * } * ``` * @ts2dart_const * @stable */ export class ViewChildMetadata extends ViewQueryMetadata { constructor(_selector: Type|string, {read = null}: {read?: any} = {}) { super(_selector, {descendants: true, first: true, read: read}); } }