Introduces the new `ANALYZE_FOR_PRECOMPILE` token. This token can be used to create a virtual provider that will populate the `precompile` fields of components and app 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. closes #9874 related to #9726
		
			
				
	
	
		
			512 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			512 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| /**
 | |
|  * @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 app 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 AppModule 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}
 | |
|  * ];
 | |
|  *
 | |
|  * @AppModule({
 | |
|  *   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 `<input>` element and want to know its `type`.
 | |
|  *
 | |
|  * ```html
 | |
|  * <input type="text">
 | |
|  * ```
 | |
|  *
 | |
|  * 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 `<tabs>` component would like to get a list its children `<pane>`
 | |
|  * components as shown in this example:
 | |
|  *
 | |
|  * ```html
 | |
|  * <tabs>
 | |
|  *   <pane title="Overview">...</pane>
 | |
|  *   <pane *ngFor="let o of objects" [title]="o.title">{{o.text}}</pane>
 | |
|  * </tabs>
 | |
|  * ```
 | |
|  *
 | |
|  * 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: `
 | |
|  *    <ul>
 | |
|  *      <li *ngFor="let pane of panes">{{pane.title}}</li>
 | |
|  *    </ul>
 | |
|  *    <ng-content></ng-content>
 | |
|  *  `
 | |
|  * })
 | |
|  * class Tabs {
 | |
|  *   panes: QueryList<Pane>;
 | |
|  *   constructor(@Query(Pane) panes:QueryList<Pane>) {
 | |
|   *    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
 | |
|  * <seeker>
 | |
|  *   <div #findme>...</div>
 | |
|  * </seeker>
 | |
|  *
 | |
|  * @Component({ selector: 'seeker' })
 | |
|  * class Seeker {
 | |
|  *   constructor(@Query('findme') elList: QueryList<ElementRef>) {...}
 | |
|  * }
 | |
|  * ```
 | |
|  *
 | |
|  * 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
 | |
|  * <seeker>
 | |
|  *   <div #find-me>...</div>
 | |
|  *   <div #find-me-too>...</div>
 | |
|  * </seeker>
 | |
|  *
 | |
|  *  @Component({
 | |
|  *   selector: 'seeker'
 | |
|  * })
 | |
|  * class Seeker {
 | |
|  *   constructor(@Query('findMe, findMeToo') elList: QueryList<ElementRef>) {...}
 | |
|  * }
 | |
|  * ```
 | |
|  *
 | |
|  * 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
 | |
|  * <container #first>
 | |
|  *   <item>a</item>
 | |
|  *   <item>b</item>
 | |
|  *   <container #second>
 | |
|  *     <item>c</item>
 | |
|  *   </container>
 | |
|  * </container>
 | |
|  * ```
 | |
|  *
 | |
|  * 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<ChildDirective>;
 | |
|  *
 | |
|  *   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: `
 | |
|  *     <item> a </item>
 | |
|  *     <item> b </item>
 | |
|  *     <item> c </item>
 | |
|  *   `
 | |
|  * })
 | |
|  * class MyComponent {
 | |
|  *   shown: boolean;
 | |
|  *
 | |
|  *   constructor(private @ViewQuery(Item) items:QueryList<Item>) {
 | |
|  *     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: '<p>child</p>'
 | |
|  * })
 | |
|  * class ChildCmp {
 | |
|  *   doSomething() {}
 | |
|  * }
 | |
|  *
 | |
|  * @Component({
 | |
|  *   selector: 'some-cmp',
 | |
|  *   template: `
 | |
|  *     <child-cmp></child-cmp>
 | |
|  *     <child-cmp></child-cmp>
 | |
|  *     <child-cmp></child-cmp>
 | |
|  *   `,
 | |
|  *   directives: [ChildCmp]
 | |
|  * })
 | |
|  * class SomeCmp {
 | |
|  *   @ViewChildren(ChildCmp) children:QueryList<ChildCmp>;
 | |
|  *
 | |
|  *   ngAfterViewInit() {
 | |
|  *     // children are set
 | |
|  *     this.children.toArray().forEach((child)=>child.doSomething());
 | |
|  *   }
 | |
|  * }
 | |
|  * ```
 | |
|  *
 | |
|  * With string selector:
 | |
|  *
 | |
|  * ```
 | |
|  * @Component({
 | |
|  *   selector: 'child-cmp',
 | |
|  *   template: '<p>child</p>'
 | |
|  * })
 | |
|  * class ChildCmp {
 | |
|  *   doSomething() {}
 | |
|  * }
 | |
|  *
 | |
|  * @Component({
 | |
|  *   selector: 'some-cmp',
 | |
|  *   template: `
 | |
|  *     <child-cmp #child1></child-cmp>
 | |
|  *     <child-cmp #child2></child-cmp>
 | |
|  *     <child-cmp #child3></child-cmp>
 | |
|  *   `,
 | |
|  *   directives: [ChildCmp]
 | |
|  * })
 | |
|  * class SomeCmp {
 | |
|  *   @ViewChildren('child1,child2,child3') children:QueryList<ChildCmp>;
 | |
|  *
 | |
|  *   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: '<p>child</p>'
 | |
|  * })
 | |
|  * class ChildCmp {
 | |
|  *   doSomething() {}
 | |
|  * }
 | |
|  *
 | |
|  * @Component({
 | |
|  *   selector: 'some-cmp',
 | |
|  *   template: '<child-cmp></child-cmp>',
 | |
|  *   directives: [ChildCmp]
 | |
|  * })
 | |
|  * class SomeCmp {
 | |
|  *   @ViewChild(ChildCmp) child:ChildCmp;
 | |
|  *
 | |
|  *   ngAfterViewInit() {
 | |
|  *     // child is set
 | |
|  *     this.child.doSomething();
 | |
|  *   }
 | |
|  * }
 | |
|  * ```
 | |
|  *
 | |
|  * With string selector:
 | |
|  *
 | |
|  * ```
 | |
|  * @Component({
 | |
|  *   selector: 'child-cmp',
 | |
|  *   template: '<p>child</p>'
 | |
|  * })
 | |
|  * class ChildCmp {
 | |
|  *   doSomething() {}
 | |
|  * }
 | |
|  *
 | |
|  * @Component({
 | |
|  *   selector: 'some-cmp',
 | |
|  *   template: '<child-cmp #child></child-cmp>',
 | |
|  *   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});
 | |
|   }
 | |
| }
 |