├── app ├── bootstrap.js ├── bootstrap.ts ├── components │ ├── app.js │ ├── app.ts │ ├── artist │ │ ├── artist.js │ │ └── artist.ts │ └── search │ │ ├── search.js │ │ └── search.ts ├── services │ ├── spotify.js │ └── spotify.ts └── utils │ ├── fetch.js │ └── fetch.ts ├── index.html └── typings ├── _custom.d.ts ├── angular2 ├── angular2.d.ts └── router.d.ts ├── es6-promise └── es6-promise.d.ts └── rx └── rx.d.ts /app/bootstrap.js: -------------------------------------------------------------------------------- 1 | /// 2 | var angular2_1 = require('angular2/angular2'); 3 | var router_1 = require('angular2/router'); 4 | var spotify_1 = require('./services/spotify'); 5 | var app_1 = require('./components/app'); 6 | var universalInjectables = [ 7 | router_1.routerInjectables, 8 | spotify_1.Spotify, 9 | angular2_1.bind(router_1.LocationStrategy).toClass(router_1.HashLocationStrategy) 10 | ]; 11 | angular2_1.bootstrap(app_1.App, [universalInjectables]); 12 | -------------------------------------------------------------------------------- /app/bootstrap.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { bootstrap, bind } from 'angular2/angular2'; 4 | 5 | import { routerInjectables, LocationStrategy, HashLocationStrategy } from 'angular2/router'; 6 | 7 | import { Spotify } from './services/spotify'; 8 | import { App } from './components/app'; 9 | 10 | 11 | var universalInjectables = [ 12 | routerInjectables, 13 | Spotify, 14 | bind(LocationStrategy).toClass(HashLocationStrategy) 15 | ]; 16 | 17 | bootstrap(App, [universalInjectables]); -------------------------------------------------------------------------------- /app/components/app.js: -------------------------------------------------------------------------------- 1 | /// 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") return Reflect.decorate(decorators, target, key, desc); 4 | switch (arguments.length) { 5 | case 2: return decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target); 6 | case 3: return decorators.reduceRight(function(o, d) { return (d && d(target, key)), void 0; }, void 0); 7 | case 4: return decorators.reduceRight(function(o, d) { return (d && d(target, key, o)) || o; }, desc); 8 | } 9 | }; 10 | var __metadata = (this && this.__metadata) || function (k, v) { 11 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 12 | }; 13 | var angular2_1 = require('angular2/angular2'); 14 | var router_1 = require('angular2/router'); 15 | var search_1 = require('../components/search/search'); 16 | var artist_1 = require('../components/artist/artist'); 17 | var App = (function () { 18 | function App() { 19 | this.title = 'App title'; 20 | } 21 | App = __decorate([ 22 | angular2_1.Component({ 23 | selector: 'app' 24 | }), 25 | angular2_1.View({ 26 | directives: [router_1.RouterLink, router_1.RouterOutlet], 27 | template: "\n\t\t
\n\t\t\t\n\t\t
\n\t\t\n\t\t
\n\t\t\t

{{title}}

\n\t\t\t\n\t\t
\n\t" 28 | }), 29 | router_1.RouteConfig([ 30 | { path: '/', redirectTo: '/search' }, 31 | { path: '/search', as: 'search', component: search_1.Search }, 32 | { path: '/artist/:id', as: 'artist', component: artist_1.Artist } 33 | ]), 34 | __metadata('design:paramtypes', []) 35 | ], App); 36 | return App; 37 | })(); 38 | exports.App = App; 39 | -------------------------------------------------------------------------------- /app/components/app.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { Component, View } from 'angular2/angular2'; 4 | import { RouteConfig, RouterLink, RouterOutlet } from 'angular2/router'; 5 | 6 | import { Search } from '../components/search/search'; 7 | import { Artist } from '../components/artist/artist'; 8 | 9 | @Component({ 10 | selector: 'app' 11 | }) 12 | 13 | @View({ 14 | directives: [RouterLink, RouterOutlet], 15 | template: ` 16 |
17 | 24 |
25 | 26 |
27 |

{{title}}

28 | 29 |
30 | ` 31 | }) 32 | 33 | @RouteConfig([ 34 | { path: '/', redirectTo: '/search' }, 35 | { path: '/search', as: 'search', component: Search }, 36 | { path: '/artist/:id', as: 'artist', component: Artist } 37 | ]) 38 | 39 | export class App { 40 | title: string; 41 | constructor() { 42 | this.title = 'App title'; 43 | } 44 | } -------------------------------------------------------------------------------- /app/components/artist/artist.js: -------------------------------------------------------------------------------- 1 | /// 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") return Reflect.decorate(decorators, target, key, desc); 4 | switch (arguments.length) { 5 | case 2: return decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target); 6 | case 3: return decorators.reduceRight(function(o, d) { return (d && d(target, key)), void 0; }, void 0); 7 | case 4: return decorators.reduceRight(function(o, d) { return (d && d(target, key, o)) || o; }, desc); 8 | } 9 | }; 10 | var __metadata = (this && this.__metadata) || function (k, v) { 11 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 12 | }; 13 | var angular2_1 = require('angular2/angular2'); 14 | var router_1 = require('angular2/router'); 15 | var spotify_1 = require('../../services/spotify'); 16 | var fetch_1 = require('../../utils/fetch'); 17 | var Artist = (function () { 18 | function Artist(service, routeParam) { 19 | this.service = service; 20 | this.routeParam = routeParam; 21 | this.getArtist(); 22 | } 23 | Artist.prototype.getArtist = function () { 24 | var _this = this; 25 | this.service.getArtistById(this.routeParam.params.id) 26 | .then(fetch_1.status) 27 | .then(fetch_1.json) 28 | .then(function (response) { 29 | _this.artist = response; 30 | _this.image = response.images[0].url; 31 | }); 32 | }; 33 | Artist = __decorate([ 34 | angular2_1.Component({ 35 | selector: 'artist', 36 | viewInjector: [spotify_1.Spotify] 37 | }), 38 | angular2_1.View({ 39 | directives: [angular2_1.NgIf], 40 | template: "\n\t\t
\n\t\t\t

{{artist.name}}

\n\t\t\t\n\t\t
\n\t" 41 | }), 42 | __metadata('design:paramtypes', [spotify_1.Spotify, router_1.RouteParams]) 43 | ], Artist); 44 | return Artist; 45 | })(); 46 | exports.Artist = Artist; 47 | -------------------------------------------------------------------------------- /app/components/artist/artist.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { Component, View, NgIf } from 'angular2/angular2'; 4 | import { RouterLink, RouteParams } from 'angular2/router'; 5 | import { Spotify } from '../../services/spotify'; 6 | import { status, json } from '../../utils/fetch' 7 | 8 | @Component({ 9 | selector: 'artist', 10 | viewInjector: [Spotify] 11 | }) 12 | @View({ 13 | directives: [NgIf], 14 | template: ` 15 |
16 |

{{artist.name}}

17 | 18 |
19 | ` 20 | }) 21 | 22 | export class Artist { 23 | artist: Object; 24 | service: Spotify; 25 | routeParam: RouteParams; 26 | image: string; 27 | constructor(service: Spotify, routeParam: RouteParams) { 28 | this.service = service; 29 | this.routeParam = routeParam; 30 | this.getArtist(); 31 | } 32 | getArtist() { 33 | this.service.getArtistById(this.routeParam.params.id) 34 | .then(status) 35 | .then(json) 36 | .then((response) => { 37 | this.artist = response; 38 | this.image = response.images[0].url; 39 | }) 40 | } 41 | } -------------------------------------------------------------------------------- /app/components/search/search.js: -------------------------------------------------------------------------------- 1 | /// 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") return Reflect.decorate(decorators, target, key, desc); 4 | switch (arguments.length) { 5 | case 2: return decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target); 6 | case 3: return decorators.reduceRight(function(o, d) { return (d && d(target, key)), void 0; }, void 0); 7 | case 4: return decorators.reduceRight(function(o, d) { return (d && d(target, key, o)) || o; }, desc); 8 | } 9 | }; 10 | var __metadata = (this && this.__metadata) || function (k, v) { 11 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 12 | }; 13 | var angular2_1 = require('angular2/angular2'); 14 | var router_1 = require('angular2/router'); 15 | var spotify_1 = require('../../services/spotify'); 16 | var fetch_1 = require('../../utils/fetch'); 17 | var Search = (function () { 18 | function Search(service) { 19 | this.service = service; 20 | } 21 | Search.prototype.searchArtist = function ($event, value) { 22 | var _this = this; 23 | if (!value) { 24 | return; 25 | } 26 | if (this.timeoutId) 27 | clearTimeout(this.timeoutId); 28 | this.timeoutId = setTimeout(function () { 29 | _this.service.searchArtist(value) 30 | .then(fetch_1.status) 31 | .then(fetch_1.json) 32 | .then(function (response) { 33 | _this.setResults(response.artists.items); 34 | }); 35 | }, 250); 36 | }; 37 | Search.prototype.setResults = function (artists) { 38 | this.artists = artists; 39 | }; 40 | Search = __decorate([ 41 | angular2_1.Component({ 42 | selector: 'search', 43 | viewInjector: [spotify_1.Spotify] 44 | }), 45 | angular2_1.View({ 46 | directives: [angular2_1.NgFor, router_1.RouterLink], 47 | template: "\n\t\t\n\t\t\n\t\t

Results

\n\t\t\n\t" 48 | }), 49 | __metadata('design:paramtypes', [spotify_1.Spotify]) 50 | ], Search); 51 | return Search; 52 | })(); 53 | exports.Search = Search; 54 | -------------------------------------------------------------------------------- /app/components/search/search.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { Component, View, NgFor, Inject } from 'angular2/angular2'; 4 | import { RouterLink, RouteParams } from 'angular2/router'; 5 | import { Spotify } from '../../services/spotify'; 6 | import { status, json } from '../../utils/fetch' 7 | 8 | @Component({ 9 | selector: 'search', 10 | viewInjector: [Spotify] 11 | }) 12 | @View({ 13 | directives: [NgFor, RouterLink], 14 | template: ` 15 | 16 | 17 |

Results

18 | 24 | ` 25 | }) 26 | 27 | export class Search { 28 | timeoutId: number; 29 | artists: Object; 30 | service: Spotify; 31 | constructor(service: Spotify) { 32 | this.service = service; 33 | } 34 | searchArtist($event, value) { 35 | if (!value) { 36 | return; 37 | } 38 | if (this.timeoutId) clearTimeout(this.timeoutId); 39 | this.timeoutId = setTimeout(() => { 40 | this.service.searchArtist(value) 41 | .then(status) 42 | .then(json) 43 | .then((response) => { 44 | this.setResults(response.artists.items); 45 | }) 46 | }, 250); 47 | } 48 | setResults(artists: Array) { 49 | this.artists = artists; 50 | } 51 | } -------------------------------------------------------------------------------- /app/services/spotify.js: -------------------------------------------------------------------------------- 1 | /// 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") return Reflect.decorate(decorators, target, key, desc); 4 | switch (arguments.length) { 5 | case 2: return decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target); 6 | case 3: return decorators.reduceRight(function(o, d) { return (d && d(target, key)), void 0; }, void 0); 7 | case 4: return decorators.reduceRight(function(o, d) { return (d && d(target, key, o)) || o; }, desc); 8 | } 9 | }; 10 | var __metadata = (this && this.__metadata) || function (k, v) { 11 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 12 | }; 13 | var angular2_1 = require('angular2/angular2'); 14 | var Spotify = (function () { 15 | function Spotify() { 16 | this.url = 'https://api.spotify.com/v1/'; 17 | } 18 | Spotify.prototype.searchArtist = function (value) { 19 | return window.fetch(this.url + 'search?type=artist&q=' + value); 20 | }; 21 | Spotify.prototype.getArtistById = function (id) { 22 | return window.fetch(this.url + 'artists/' + id); 23 | }; 24 | Spotify = __decorate([ 25 | angular2_1.Injectable(), 26 | __metadata('design:paramtypes', []) 27 | ], Spotify); 28 | return Spotify; 29 | })(); 30 | exports.Spotify = Spotify; 31 | -------------------------------------------------------------------------------- /app/services/spotify.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { Injectable } from 'angular2/angular2'; 4 | 5 | @Injectable() 6 | export class Spotify { 7 | url: string; 8 | constructor() { 9 | this.url = 'https://api.spotify.com/v1/'; 10 | } 11 | 12 | public searchArtist (value) { 13 | return window.fetch(this.url + 'search?type=artist&q=' + value); 14 | } 15 | 16 | public getArtistById (id) { 17 | return window.fetch(this.url + 'artists/' + id); 18 | } 19 | } -------------------------------------------------------------------------------- /app/utils/fetch.js: -------------------------------------------------------------------------------- 1 | function status(response) { 2 | if (response.status >= 200 && response.status < 300) { 3 | return Promise.resolve(response); 4 | } 5 | return response.text().then(function (text) { 6 | throw new Error(text); 7 | }); 8 | } 9 | exports.status = status; 10 | function text(response) { 11 | return response.text(); 12 | } 13 | exports.text = text; 14 | function json(response) { 15 | return response.json(); 16 | } 17 | exports.json = json; 18 | -------------------------------------------------------------------------------- /app/utils/fetch.ts: -------------------------------------------------------------------------------- 1 | export function status(response) { 2 | if (response.status >= 200 && response.status < 300) { 3 | return Promise.resolve(response); 4 | } 5 | return response.text().then(function(text) { 6 | throw new Error(text); 7 | }); 8 | } 9 | 10 | export function text(response) { 11 | return response.text(); 12 | } 13 | 14 | export function json(response) { 15 | return response.json(); 16 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | My App 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /typings/_custom.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// -------------------------------------------------------------------------------- /typings/angular2/angular2.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for Angular v2.0.0-alpha.35 2 | // Project: http://angular.io/ 3 | // Definitions by: angular team 4 | // Definitions: https://github.com/borisyankov/DefinitelyTyped 5 | 6 | // *********************************************************** 7 | // This file is generated by the Angular build process. 8 | // Please do not create manual edits or send pull requests 9 | // modifying this file. 10 | // *********************************************************** 11 | 12 | // angular2/angular2 depends transitively on these libraries. 13 | // If you don't have them installed you can install them using TSD 14 | // https://github.com/DefinitelyTyped/tsd 15 | 16 | /// 17 | /// 18 | 19 | 20 | interface List extends Array {} 21 | interface Map {} 22 | interface StringMap extends Map {} 23 | 24 | declare module ng { 25 | // See https://github.com/Microsoft/TypeScript/issues/1168 26 | class BaseException /* extends Error */ { 27 | message: string; 28 | stack: string; 29 | toString(): string; 30 | } 31 | interface InjectableReference {} 32 | } 33 | 34 | 35 | 36 | 37 | /** 38 | * The `angular2` is the single place to import all of the individual types. 39 | */ 40 | declare module ng { 41 | 42 | /** 43 | * Declare reusable UI building blocks for an application. 44 | * 45 | * Each Angular component requires a single `@Component` and at least one `@View` annotation. The 46 | * `@Component` 47 | * annotation specifies when a component is instantiated, and which properties and hostListeners it 48 | * binds to. 49 | * 50 | * When a component is instantiated, Angular 51 | * - creates a shadow DOM for the component. 52 | * - loads the selected template into the shadow DOM. 53 | * - creates all the injectable objects configured with `bindings` and `viewBindings`. 54 | * 55 | * All template expressions and statements are then evaluated against the component instance. 56 | * 57 | * For details on the `@View` annotation, see {@link ViewMetadata}. 58 | * 59 | * ## Example 60 | * 61 | * ``` 62 | * @Component({ 63 | * selector: 'greet' 64 | * }) 65 | * @View({ 66 | * template: 'Hello {{name}}!' 67 | * }) 68 | * class Greet { 69 | * name: string; 70 | * 71 | * constructor() { 72 | * this.name = 'World'; 73 | * } 74 | * } 75 | * ``` 76 | */ 77 | class ComponentMetadata extends DirectiveMetadata { 78 | 79 | 80 | /** 81 | * Defines the used change detection strategy. 82 | * 83 | * When a component is instantiated, Angular creates a change detector, which is responsible for 84 | * propagating 85 | * the component's bindings. 86 | * 87 | * The `changeDetection` property defines, whether the change detection will be checked every time 88 | * or only when the component 89 | * tells it to do so. 90 | */ 91 | changeDetection: string; 92 | 93 | 94 | /** 95 | * Defines the set of injectable objects that are visible to its view dom children. 96 | * 97 | * ## Simple Example 98 | * 99 | * Here is an example of a class that can be injected: 100 | * 101 | * ``` 102 | * class Greeter { 103 | * greet(name:string) { 104 | * return 'Hello ' + name + '!'; 105 | * } 106 | * } 107 | * 108 | * @Directive({ 109 | * selector: 'needs-greeter' 110 | * }) 111 | * class NeedsGreeter { 112 | * greeter:Greeter; 113 | * 114 | * constructor(greeter:Greeter) { 115 | * this.greeter = greeter; 116 | * } 117 | * } 118 | * 119 | * @Component({ 120 | * selector: 'greet', 121 | * viewBindings: [ 122 | * Greeter 123 | * ] 124 | * }) 125 | * @View({ 126 | * template: ``, 127 | * directives: [NeedsGreeter] 128 | * }) 129 | * class HelloWorld { 130 | * } 131 | * 132 | * ``` 133 | */ 134 | viewBindings: List; 135 | } 136 | 137 | 138 | /** 139 | * Directives allow you to attach behavior to elements in the DOM. 140 | * 141 | * {@link DirectiveMetadata}s with an embedded view are called {@link ComponentMetadata}s. 142 | * 143 | * A directive consists of a single directive annotation and a controller class. When the 144 | * directive's `selector` matches 145 | * elements in the DOM, the following steps occur: 146 | * 147 | * 1. For each directive, the `ElementInjector` attempts to resolve the directive's constructor 148 | * arguments. 149 | * 2. Angular instantiates directives for each matched element using `ElementInjector` in a 150 | * depth-first order, 151 | * as declared in the HTML. 152 | * 153 | * ## Understanding How Injection Works 154 | * 155 | * There are three stages of injection resolution. 156 | * - *Pre-existing Injectors*: 157 | * - The terminal {@link Injector} cannot resolve dependencies. It either throws an error or, if 158 | * the dependency was 159 | * specified as `@Optional`, returns `null`. 160 | * - The platform injector resolves browser singleton resources, such as: cookies, title, 161 | * location, and others. 162 | * - *Component Injectors*: Each component instance has its own {@link Injector}, and they follow 163 | * the same parent-child hierarchy 164 | * as the component instances in the DOM. 165 | * - *Element Injectors*: Each component instance has a Shadow DOM. Within the Shadow DOM each 166 | * element has an `ElementInjector` 167 | * which follow the same parent-child hierarchy as the DOM elements themselves. 168 | * 169 | * When a template is instantiated, it also must instantiate the corresponding directives in a 170 | * depth-first order. The 171 | * current `ElementInjector` resolves the constructor dependencies for each directive. 172 | * 173 | * Angular then resolves dependencies as follows, according to the order in which they appear in the 174 | * {@link ViewMetadata}: 175 | * 176 | * 1. Dependencies on the current element 177 | * 2. Dependencies on element injectors and their parents until it encounters a Shadow DOM boundary 178 | * 3. Dependencies on component injectors and their parents until it encounters the root component 179 | * 4. Dependencies on pre-existing injectors 180 | * 181 | * 182 | * The `ElementInjector` can inject other directives, element-specific special objects, or it can 183 | * delegate to the parent 184 | * injector. 185 | * 186 | * To inject other directives, declare the constructor parameter as: 187 | * - `directive:DirectiveType`: a directive on the current element only 188 | * - `@Host() directive:DirectiveType`: any directive that matches the type between the current 189 | * element and the 190 | * Shadow DOM root. 191 | * - `@Query(DirectiveType) query:QueryList`: A live collection of direct child 192 | * directives. 193 | * - `@QueryDescendants(DirectiveType) query:QueryList`: A live collection of any 194 | * child directives. 195 | * 196 | * To inject element-specific special objects, declare the constructor parameter as: 197 | * - `element: ElementRef` to obtain a reference to logical element in the view. 198 | * - `viewContainer: ViewContainerRef` to control child template instantiation, for 199 | * {@link DirectiveMetadata} directives only 200 | * - `bindingPropagation: BindingPropagation` to control change detection in a more granular way. 201 | * 202 | * ## Example 203 | * 204 | * The following example demonstrates how dependency injection resolves constructor arguments in 205 | * practice. 206 | * 207 | * 208 | * Assume this HTML template: 209 | * 210 | * ``` 211 | *
212 | *
213 | *
214 | *
215 | *
216 | *
217 | *
218 | *
219 | *
220 | *
221 | * ``` 222 | * 223 | * With the following `dependency` decorator and `SomeService` injectable class. 224 | * 225 | * ``` 226 | * @Injectable() 227 | * class SomeService { 228 | * } 229 | * 230 | * @Directive({ 231 | * selector: '[dependency]', 232 | * properties: [ 233 | * 'id: dependency' 234 | * ] 235 | * }) 236 | * class Dependency { 237 | * id:string; 238 | * } 239 | * ``` 240 | * 241 | * Let's step through the different ways in which `MyDirective` could be declared... 242 | * 243 | * 244 | * ### No injection 245 | * 246 | * Here the constructor is declared with no arguments, therefore nothing is injected into 247 | * `MyDirective`. 248 | * 249 | * ``` 250 | * @Directive({ selector: '[my-directive]' }) 251 | * class MyDirective { 252 | * constructor() { 253 | * } 254 | * } 255 | * ``` 256 | * 257 | * This directive would be instantiated with no dependencies. 258 | * 259 | * 260 | * ### Component-level injection 261 | * 262 | * Directives can inject any injectable instance from the closest component injector or any of its 263 | * parents. 264 | * 265 | * Here, the constructor declares a parameter, `someService`, and injects the `SomeService` type 266 | * from the parent 267 | * component's injector. 268 | * ``` 269 | * @Directive({ selector: '[my-directive]' }) 270 | * class MyDirective { 271 | * constructor(someService: SomeService) { 272 | * } 273 | * } 274 | * ``` 275 | * 276 | * This directive would be instantiated with a dependency on `SomeService`. 277 | * 278 | * 279 | * ### Injecting a directive from the current element 280 | * 281 | * Directives can inject other directives declared on the current element. 282 | * 283 | * ``` 284 | * @Directive({ selector: '[my-directive]' }) 285 | * class MyDirective { 286 | * constructor(dependency: Dependency) { 287 | * expect(dependency.id).toEqual(3); 288 | * } 289 | * } 290 | * ``` 291 | * This directive would be instantiated with `Dependency` declared at the same element, in this case 292 | * `dependency="3"`. 293 | * 294 | * ### Injecting a directive from any ancestor elements 295 | * 296 | * Directives can inject other directives declared on any ancestor element (in the current Shadow 297 | * DOM), i.e. on the current element, the 298 | * parent element, or its parents. 299 | * ``` 300 | * @Directive({ selector: '[my-directive]' }) 301 | * class MyDirective { 302 | * constructor(@Host() dependency: Dependency) { 303 | * expect(dependency.id).toEqual(2); 304 | * } 305 | * } 306 | * ``` 307 | * 308 | * `@Host` checks the current element, the parent, as well as its parents recursively. If 309 | * `dependency="2"` didn't 310 | * exist on the direct parent, this injection would 311 | * have returned 312 | * `dependency="1"`. 313 | * 314 | * 315 | * ### Injecting a live collection of direct child directives 316 | * 317 | * 318 | * A directive can also query for other child directives. Since parent directives are instantiated 319 | * before child directives, a directive can't simply inject the list of child directives. Instead, 320 | * the directive injects a {@link QueryList}, which updates its contents as children are added, 321 | * removed, or moved by a directive that uses a {@link ViewContainerRef} such as a `ng-for`, an 322 | * `ng-if`, or an `ng-switch`. 323 | * 324 | * ``` 325 | * @Directive({ selector: '[my-directive]' }) 326 | * class MyDirective { 327 | * constructor(@Query(Dependency) dependencies:QueryList) { 328 | * } 329 | * } 330 | * ``` 331 | * 332 | * This directive would be instantiated with a {@link QueryList} which contains `Dependency` 4 and 333 | * 6. Here, `Dependency` 5 would not be included, because it is not a direct child. 334 | * 335 | * ### Injecting a live collection of descendant directives 336 | * 337 | * By passing the descendant flag to `@Query` above, we can include the children of the child 338 | * elements. 339 | * 340 | * ``` 341 | * @Directive({ selector: '[my-directive]' }) 342 | * class MyDirective { 343 | * constructor(@Query(Dependency, {descendants: true}) dependencies:QueryList) { 344 | * } 345 | * } 346 | * ``` 347 | * 348 | * This directive would be instantiated with a Query which would contain `Dependency` 4, 5 and 6. 349 | * 350 | * ### Optional injection 351 | * 352 | * The normal behavior of directives is to return an error when a specified dependency cannot be 353 | * resolved. If you 354 | * would like to inject `null` on unresolved dependency instead, you can annotate that dependency 355 | * with `@Optional()`. 356 | * This explicitly permits the author of a template to treat some of the surrounding directives as 357 | * optional. 358 | * 359 | * ``` 360 | * @Directive({ selector: '[my-directive]' }) 361 | * class MyDirective { 362 | * constructor(@Optional() dependency:Dependency) { 363 | * } 364 | * } 365 | * ``` 366 | * 367 | * This directive would be instantiated with a `Dependency` directive found on the current element. 368 | * If none can be 369 | * found, the injector supplies `null` instead of throwing an error. 370 | * 371 | * ## Example 372 | * 373 | * Here we use a decorator directive to simply define basic tool-tip behavior. 374 | * 375 | * ``` 376 | * @Directive({ 377 | * selector: '[tooltip]', 378 | * properties: [ 379 | * 'text: tooltip' 380 | * ], 381 | * host: { 382 | * '(mouseenter)': 'onMouseEnter()', 383 | * '(mouseleave)': 'onMouseLeave()' 384 | * } 385 | * }) 386 | * class Tooltip{ 387 | * text:string; 388 | * overlay:Overlay; // NOT YET IMPLEMENTED 389 | * overlayManager:OverlayManager; // NOT YET IMPLEMENTED 390 | * 391 | * constructor(overlayManager:OverlayManager) { 392 | * this.overlay = overlay; 393 | * } 394 | * 395 | * onMouseEnter() { 396 | * // exact signature to be determined 397 | * this.overlay = this.overlayManager.open(text, ...); 398 | * } 399 | * 400 | * onMouseLeave() { 401 | * this.overlay.close(); 402 | * this.overlay = null; 403 | * } 404 | * } 405 | * ``` 406 | * In our HTML template, we can then add this behavior to a `
` or any other element with the 407 | * `tooltip` selector, 408 | * like so: 409 | * 410 | * ``` 411 | *
412 | * ``` 413 | * 414 | * Directives can also control the instantiation, destruction, and positioning of inline template 415 | * elements: 416 | * 417 | * A directive uses a {@link ViewContainerRef} to instantiate, insert, move, and destroy views at 418 | * runtime. 419 | * The {@link ViewContainerRef} is created as a result of `