import {CONST, Type, stringify, isPresent, isString} from 'angular2/src/facade/lang'; import {resolveForwardRef} from 'angular2/src/core/di'; import {DependencyMetadata} from 'angular2/src/core/di/metadata'; /** * 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'} */ @CONST() 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. */ @CONST() export class QueryMetadata extends DependencyMetadata { /** * whether we want to query only direct children (false) or all * children (true). */ descendants: boolean; first: boolean; constructor(private _selector: Type | string, {descendants = false, first = false}: {descendants?: boolean, first?: boolean} = {}) { super(); this.descendants = descendants; this.first = first; } /** * 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 this.selector.split(','); } 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 * } * } * ``` */ @CONST() export class ContentChildrenMetadata extends QueryMetadata { constructor(_selector: Type | string, {descendants = false}: {descendants?: boolean} = {}) { super(_selector, {descendants: descendants}); } } // 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 * } * } * ``` */ @CONST() export class ContentChildMetadata extends QueryMetadata { constructor(_selector: Type | string) { super(_selector, {descendants: true, first: true}); } } /** * 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. */ @CONST() export class ViewQueryMetadata extends QueryMetadata { constructor(_selector: Type | string, {descendants = false, first = false}: {descendants?: boolean, first?: boolean} = {}) { super(_selector, {descendants: descendants, first: first}); } /** * 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()); * } * } * ``` */ @CONST() export class ViewChildrenMetadata extends ViewQueryMetadata { constructor(_selector: Type | string) { super(_selector, {descendants: true}); } } /** * * 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(); * } * } * ``` */ @CONST() export class ViewChildMetadata extends ViewQueryMetadata { constructor(_selector: Type | string) { super(_selector, {descendants: true, first: true}); } }