(docs) Move ToH-pt2 to Jade and break toh-pt1 out of the tutorial index
This commit is contained in:
		
							parent
							
								
									b2de9a1067
								
							
						
					
					
						commit
						c25314a2c4
					
				
							
								
								
									
										7
									
								
								public/docs/ts/latest/guide/template-syntax.jade
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								public/docs/ts/latest/guide/template-syntax.jade
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| include ../../../../_includes/_util-fns | ||||
| 
 | ||||
| .l-main-section | ||||
|   :markdown | ||||
|     # Template Syntax | ||||
|      | ||||
|     <h1>Coming Soon</h1>   | ||||
| @ -2,7 +2,15 @@ | ||||
|   "_listtype": "ordered", | ||||
| 
 | ||||
|   "index": { | ||||
|     "title": "Tutorial", | ||||
|     "title": "Tutorial: Tour of Heroes", | ||||
|     "intro": "The Tour of Heroes tutorial takes us through the steps of creating an Angular application in TypeScript." | ||||
|   }, | ||||
|   "toh-pt1": { | ||||
|     "title": "The Hero Editor", | ||||
|     "intro": "We build a simple hero editor" | ||||
|   }, | ||||
|   "toh-pt2": { | ||||
|     "title": "Master/Detail", | ||||
|     "intro": "We build a master/detail page with a list of heroes " | ||||
|   } | ||||
| } | ||||
| @ -1,11 +1,10 @@ | ||||
| include ../../../../_includes/_util-fns | ||||
| 
 | ||||
| .l-main-section | ||||
| 
 | ||||
|   :markdown | ||||
|     # Tour of Heroes | ||||
|     # Tour of Heroes: the vision | ||||
| 
 | ||||
|     Our grand vision is to build an app to help a staffing agency manage its stable of heroes. | ||||
|     Our grand plan is to build an app to help a staffing agency manage its stable of heroes. | ||||
|     Even heroes need to find work. | ||||
| 
 | ||||
|     Of course we'll only make a little progress in this tutorial. What we do build will | ||||
| @ -28,7 +27,7 @@ include ../../../../_includes/_util-fns | ||||
| 
 | ||||
|     ## The End Game | ||||
| 
 | ||||
|     Let's get a visual idea of where we're going in this tour, beginning with the "Heroes" | ||||
|     Here's a visual idea of where we're going in this tour, beginning with the "Heroes" | ||||
|     view and its list of heroes: | ||||
| 
 | ||||
|   figure.image-display | ||||
| @ -57,311 +56,4 @@ include ../../../../_includes/_util-fns | ||||
| 
 | ||||
|     And we’ll meet many of the core fundamentals of Angular along the way. | ||||
|   | ||||
| .l-main-section | ||||
| 
 | ||||
|   :markdown | ||||
|     # Once Upon a Time | ||||
| 
 | ||||
|     Every story starts somewhere. Our story starts where the [QuickStart](../quickstart) ends. | ||||
| 
 | ||||
|     Follow the "QuickStart" steps. They provide the prerequisites, the folder structure, | ||||
|     and the core files for our Tour of Heroes. | ||||
| 
 | ||||
|     Copy the "QuickStart" code to a new folder and rename the folder `angular2-tour-of-heroes`. | ||||
|     We should have the following structure: | ||||
| 
 | ||||
|   code-example. | ||||
|       angular2-tour-of-heroes | ||||
|         ├── node_modules | ||||
|         ├── src | ||||
|         |    ├── app | ||||
|         |    |    └── app.ts | ||||
|         |    ├── index.html | ||||
|         |    └── tsconfig.json | ||||
|         └── package.json | ||||
| 
 | ||||
|   :markdown | ||||
|     ## Keep the App Running | ||||
|     Start the TypeScript compiler and have it watch for changes in one terminal window by typing | ||||
| 
 | ||||
|   pre.prettyprint.lang-bash | ||||
|     code tsc -p src -w | ||||
| 
 | ||||
|   :markdown | ||||
|     Now open another terminal window and start the server by typing | ||||
| 
 | ||||
|   pre.prettyprint.lang-bash | ||||
|     code live-server --open=src | ||||
| 
 | ||||
|   :markdown | ||||
|     This command starts the server, launches the app in a browser, | ||||
|     and keeps the app running while we continue to build the Tour of Heroes. | ||||
| 
 | ||||
|   .alert.is-helpful | ||||
|     :markdown | ||||
|       These two steps watch all project files. They recompile TypeScript files and re-run | ||||
|       the app when any file changes. | ||||
|       If the watchers fail to detect renamed or new files, | ||||
|       stop these commands in each terminal by typing `CTRL+C` and then re-run them. | ||||
| 
 | ||||
| .l-main-section | ||||
| 
 | ||||
|   :markdown | ||||
|     # Show our Hero | ||||
|     We want to display Hero data in our app | ||||
| 
 | ||||
|     Let's add two properties to our `AppComponent`, a `title` property for the application name and a `hero` property | ||||
|     for a hero named "Windstorm". | ||||
| 
 | ||||
|     ``` | ||||
|     class AppComponent { | ||||
|       public title = 'Tour of Heroes'; | ||||
|       public hero = 'Windstorm'; | ||||
|     } | ||||
|     ``` | ||||
| 
 | ||||
|   :markdown | ||||
|     Now we update the template in the `@Component` decoration with data bindings to these new properties. | ||||
| 
 | ||||
|   code-example(format="linenums"). | ||||
|     template: '<h1>{{title}}</h1><h2>{{hero}} details!</h2>' | ||||
|   :markdown | ||||
|     The browser should refresh and display our title and hero. | ||||
| 
 | ||||
|     The double curly braces tell our app to read the `title` and `hero` properties from the component and render them. | ||||
|     This is the "interpolation" form of one-way data binding; | ||||
|     we can learn more about interpolation in the [Displaying Data chapter](displaying-data). | ||||
| 
 | ||||
|     ## Hero Object | ||||
| 
 | ||||
|     At the moment, our hero is just a name.  Our hero needs more properties. | ||||
|     Let's convert the `hero` from a literal string to a class. | ||||
| 
 | ||||
|     Create a `Hero` class with `id` and `name` properties. | ||||
|     Keep this near the top of hte `app.ts` file for now. | ||||
| 
 | ||||
|     ``` | ||||
|     class Hero { | ||||
|       id: number; | ||||
|       name: string; | ||||
|     } | ||||
|     ``` | ||||
| 
 | ||||
|     Now that we have a `Hero` class, let’s refactor our component’s `hero` property to be of type `Hero`. | ||||
|     Then initialize it with an id of `1` and the name, "Windstorm". | ||||
| 
 | ||||
|     ``` | ||||
|     public hero: Hero = { | ||||
|       id: 1, | ||||
|       name: 'Windstorm' | ||||
|     }; | ||||
|     ``` | ||||
| 
 | ||||
|     Because we changed the hero from a string to an object, | ||||
|     we update the binding in the template to refer to the hero’s `name` property. | ||||
| 
 | ||||
|   code-example(format="linenums"). | ||||
|     template: '<h1>{{title}}</h1><h2>{{hero.name}} details!</h2>' | ||||
|   :markdown | ||||
|     The browser refreshes and continues to display our hero’s name. | ||||
| 
 | ||||
|     ## **Adding more HTML** | ||||
|     Displaying a name is good, but we want to see all of our hero’s properties. | ||||
|     We’ll add a `<div>` for our hero’s `id` property and another `<div>` for our hero’s `name`. | ||||
| 
 | ||||
|   code-example(format="linenums"). | ||||
|     template: '<h1>{{title}}</h1><h2>{{hero.name}} details!</h2><div><label>id: </label>{{hero.id}}</div><div><label>name: </label>{{hero.name}}</div>' | ||||
|   :markdown | ||||
|     Uh oh, our template string is getting long. We better take care of that to avoid the risk of making a typo in the template. | ||||
| 
 | ||||
|     ### Multi-line Template Strings | ||||
| 
 | ||||
|     We could make a more readable template with string concatenation | ||||
|     but that gets ugly fast, it is harder to read, and | ||||
|     it is easy to make a spelling error. Instead, | ||||
|     let’s take advantage of the template strings feature | ||||
|     in ES2015 and TypeScript to maintain our sanity. | ||||
| 
 | ||||
|     Change the quotes around the template to back-ticks and | ||||
|     put the `<h1>`, `<h2>` and `<div>` elements on their own lines. | ||||
| 
 | ||||
|   code-example(format="linenums"). | ||||
|     template:` | ||||
|       <h1>{{title}}</h1> | ||||
|       <h2>{{hero.name}} details!</h2> | ||||
|       <div><label>id: </label>{{hero.id}}</div> | ||||
|       <div><label>name: </label>{{hero.name}}</div> | ||||
|       ` | ||||
| 
 | ||||
|   .callout.is-important | ||||
|     header A back-tick is not a single quote | ||||
|     :markdown | ||||
|       **Be careful!** A back-tick (`) looks a lot like a single quote ('). | ||||
|       It's actually a completely different character. | ||||
|       Back-ticks can do more than demarcate a string. | ||||
|       Here we use them in a limited way to spread the template over multiple lines. | ||||
|       Everything between the back-ticks at the beginning and end of the template | ||||
|       is part of a single template string. | ||||
| 
 | ||||
| .l-main-section | ||||
| 
 | ||||
|   :markdown | ||||
|     #  Editing Our Hero | ||||
| 
 | ||||
|     We want to be able to edit the hero name in a textbox. | ||||
| 
 | ||||
|     Replace the hero name `<label>` with an `<input>` element as shown below: | ||||
|   code-example(format="linenums"). | ||||
|     template:` | ||||
|       <h1>{{title}}</h1> | ||||
|       <h2>{{hero.name}} details!</h2> | ||||
|       <div><label>id: </label>{{hero.id}}</div> | ||||
|       <div> | ||||
|         <label>name: </label> | ||||
|         <div><input value="{{hero.name}}" placeholder="name"></input></div> | ||||
|       </div> | ||||
|       ` | ||||
|   :markdown | ||||
|     We see in the browser that the hero’s name does appear in the `<input>` textbox. | ||||
|     But something doesn’t feel right. | ||||
|     When we change the name, we notice that our change | ||||
|     is not reflected in the `<h2>`. We won't get the desired behavior | ||||
|     with a one-way binding to `<input>`. | ||||
| 
 | ||||
|     ## Two-Way Binding | ||||
| 
 | ||||
|     We intend to display the name of the hero in the `<input>`, change it, | ||||
|     and see those changes wherever we bind to the hero’s name. | ||||
|     In short, we want two-way data binding. | ||||
| 
 | ||||
|     Let’s update the template to use the  **`ng-model`** built-in directive for two-way binding. | ||||
| 
 | ||||
|     <!-- TODO style with a class and add a link | ||||
|     *Sidenote: Learn more about ng-model in the [Using Forms chapter]* | ||||
|     --> | ||||
| 
 | ||||
|     Replace the  `<input>` with the following HTML | ||||
| 
 | ||||
|     ``` | ||||
|     <input [(ng-model)]="hero.name" placeholder="name"></input> | ||||
|     ``` | ||||
|     Unfortunately, that change broke our application and we're no longer displaying the hero in the browser. | ||||
|     Let’s fix that next. | ||||
| 
 | ||||
| .l-main-section | ||||
| 
 | ||||
|   :markdown | ||||
|     # Declaring Template Directives | ||||
| 
 | ||||
|     We added the `ng-model` directive but we didn't tell Angular about it. | ||||
|     A component must disclose every directive that appears in its template. | ||||
| 
 | ||||
|     Let’s first gain access to the `NgModel` directive class by importing it from Angular as shown below: | ||||
| 
 | ||||
|     ```` | ||||
|     import {bootstrap, Component, NgModel} from 'angular2/angular2'; | ||||
|     ``` | ||||
| 
 | ||||
|     Now tell the component that we will use the  `ng-model` directive in the template | ||||
|     by adding the `directives` property to the `@Component` decoration | ||||
|     immediately below the `template` string: | ||||
| 
 | ||||
|     ``` | ||||
|     directives: [NgModel] | ||||
|     ``` | ||||
| 
 | ||||
|     The `directives` property is an array holding all directive classes that | ||||
|     are used by the component’s template. | ||||
| 
 | ||||
|     Unfortunately when we view the app in the browser we still have an error: | ||||
| 
 | ||||
|     ``` | ||||
|     *EXCEPTION: No value accessor for ' ' in [null]* | ||||
|     ``` | ||||
| 
 | ||||
|     Apparently declaring the `NgModel` is not quite enough. | ||||
| 
 | ||||
|     ## Declare Multiple Form Directives | ||||
| 
 | ||||
|     We learned from our latest error message that we can’t the import `NgModel` alone. | ||||
|     We need additional directives to enable two-way data binding with `NgModel`. | ||||
| 
 | ||||
|     We could hunt them down and add each of them to the `directives` array one by one. | ||||
|     That's painful. No one wants to remember all of the necessary directives and | ||||
|     type them correctly. Fortunately, there is a shortcut. | ||||
| 
 | ||||
|     The `ng-model` directive is one of many Forms directives which happen to be | ||||
|     bundled in a convenient array called `FORM_DIRECTIVES`. | ||||
| 
 | ||||
|     <!-- TODO *Sidenote: Learn more about Angular Forms in the [Forms chapter]* --> | ||||
| 
 | ||||
|     Let’s forget about importing `NgModel` and import the `FORM_DIRECTIVES` array instead: | ||||
|     ``` | ||||
|     import {bootstrap, Component, FORM_DIRECTIVES} from 'angular2/angular2'; | ||||
|     ``` | ||||
|     Now we tell the component that our template can use `FORM_DIRECTIVES` | ||||
|     by updating the `directives` property of the `@Component` decorator. | ||||
|     ``` | ||||
|     directives: [FORM_DIRECTIVES] | ||||
|     ``` | ||||
|     The browser refreshes. We see our hero again. We can edit the hero’s name and | ||||
|     see the changes reflected immediately in the `<h2>`. | ||||
| 
 | ||||
|     ### Bundled Directives | ||||
|     Angular bundled the Form-related directives together in a convenient `FORM_DIRECTIVES` array. | ||||
|     That's all we need to remember to light up our template. | ||||
| 
 | ||||
|     We may wish to use this trick ourselves someday. | ||||
|     We too can bundle a collection of directives in an array, give it a catchy name, | ||||
|     and plug that array into the `directives` property. | ||||
| 
 | ||||
|     # The Road We’ve Travelled | ||||
|     Let’s take stock of what we’ve built. | ||||
| 
 | ||||
|     * Our Tour of Heroes uses the double curly braces of interpolation (a form of one-way data binding) | ||||
|     to display the application title and properties of a `Hero` object. | ||||
|     * We wrote a multi-line template using ES2015’s template strings to make our template readable. | ||||
|     * We can both display and change the hero’s name after adding a two-way data binding to the `<input>` element | ||||
|     using the built-in `ng-model` directive. | ||||
|     * The `ng-model` directive also propagates changes to every other binding of the `hero.name`. | ||||
|     * We declared our use of `NgModel` and other Form directives | ||||
|     by setting the component's `directives` metadata property to the `FORMS_DIRECTIVES` array. | ||||
| 
 | ||||
|     Here's the complete `AppComponent.ts` as it stands now: | ||||
| 
 | ||||
|   code-example(format="linenums"). | ||||
|     import {bootstrap, Component, FORM_DIRECTIVES} from 'angular2/angular2'; | ||||
| 
 | ||||
|     class Hero { | ||||
|       id: number; | ||||
|       name: string; | ||||
|     } | ||||
| 
 | ||||
|     @Component({ | ||||
|       template:` | ||||
|         <h1>{{title}}</h1> | ||||
|         <h2>{{hero.name}} details!</h2> | ||||
|         <div><label>id: </label>{{hero.id}}</div> | ||||
|         <div> | ||||
|           <label>name: </label> | ||||
|           <div><input value="{{hero.name}}" placeholder="name"></input></div> | ||||
|         </div> | ||||
|         `, | ||||
|       directives: [FORM_DIRECTIVES] | ||||
|     }) | ||||
|     class AppComponent { | ||||
|       public title = 'Tour of Heroes'; | ||||
|       public hero: Hero = { | ||||
|         id: 1, | ||||
|         name: 'Windstorm' | ||||
|       }; | ||||
|     } | ||||
| 
 | ||||
|     bootstrap(AppComponent); | ||||
|     <!-- TODO: Add this in when the next chapter exists | ||||
|     # **The Road Ahead** | ||||
|     Our Tour of Heroes only displays one hero and we really want to display a list of heroes. | ||||
|     We also want to allow the user to select a hero and display their details. | ||||
|     We’ll learn more about how to retrieve lists, bind them to the | ||||
|     template, and allow a user to select it in the next chapter. | ||||
|     --> | ||||
|     [Let's get started!](./toh-pt1). | ||||
|  | ||||
							
								
								
									
										314
									
								
								public/docs/ts/latest/tutorial/toh-pt1.jade
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										314
									
								
								public/docs/ts/latest/tutorial/toh-pt1.jade
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,314 @@ | ||||
| include ../../../../_includes/_util-fns | ||||
| 
 | ||||
| .l-main-section | ||||
| 
 | ||||
|   :markdown | ||||
|     # Once Upon a Time | ||||
| 
 | ||||
|     Every story starts somewhere. Our story starts where the [QuickStart](../quickstart) ends. | ||||
| 
 | ||||
|     Follow the "QuickStart" steps. They provide the prerequisites, the folder structure, | ||||
|     and the core files for our Tour of Heroes. | ||||
| 
 | ||||
|     Copy the "QuickStart" code to a new folder and rename the folder `angular2-tour-of-heroes`. | ||||
|     We should have the following structure: | ||||
| 
 | ||||
|   code-example. | ||||
|       angular2-tour-of-heroes | ||||
|         ├── node_modules | ||||
|         ├── src | ||||
|         |    ├── app | ||||
|         |    |    └── app.ts | ||||
|         |    ├── index.html | ||||
|         |    └── tsconfig.json | ||||
|         └── package.json | ||||
| 
 | ||||
|   :markdown | ||||
|     ## Keep the App Running | ||||
|     Start the TypeScript compiler and have it watch for changes in one terminal window by typing | ||||
| 
 | ||||
|   pre.prettyprint.lang-bash | ||||
|     code npm run tsc | ||||
| 
 | ||||
|   :markdown | ||||
|     Now open another terminal window and start the server by typing | ||||
| 
 | ||||
|   pre.prettyprint.lang-bash | ||||
|     code npm start | ||||
| 
 | ||||
|   :markdown | ||||
|     This command starts the server, launches the app in a browser, | ||||
|     and keeps the app running while we continue to build the Tour of Heroes. | ||||
| 
 | ||||
|   .alert.is-helpful | ||||
|     :markdown | ||||
|       These two steps watch all project files. They recompile TypeScript files and re-run | ||||
|       the app when any file changes. | ||||
|       If the watchers fail to detect renamed or new files, | ||||
|       stop these commands in each terminal by typing `CTRL+C` and then re-run them. | ||||
| 
 | ||||
| .l-main-section | ||||
| 
 | ||||
|   :markdown | ||||
|     # Show our Hero | ||||
|     We want to display Hero data in our app | ||||
| 
 | ||||
|     Let's add two properties to our `AppComponent`, a `title` property for the application name and a `hero` property | ||||
|     for a hero named "Windstorm". | ||||
| 
 | ||||
|     ``` | ||||
|     class AppComponent { | ||||
|       public title = 'Tour of Heroes'; | ||||
|       public hero = 'Windstorm'; | ||||
|     } | ||||
|     ``` | ||||
| 
 | ||||
|   :markdown | ||||
|     Now we update the template in the `@Component` decoration with data bindings to these new properties. | ||||
| 
 | ||||
|   code-example(format="linenums"). | ||||
|     template: '<h1>{{title}}</h1><h2>{{hero}} details!</h2>' | ||||
|   :markdown | ||||
|     The browser should refresh and display our title and hero. | ||||
| 
 | ||||
|     The double curly braces tell our app to read the `title` and `hero` properties from the component and render them. | ||||
|     This is the "interpolation" form of one-way data binding; | ||||
|     we can learn more about interpolation in the [Displaying Data chapter](displaying-data). | ||||
| 
 | ||||
|     ## Hero Object | ||||
| 
 | ||||
|     At the moment, our hero is just a name.  Our hero needs more properties. | ||||
|     Let's convert the `hero` from a literal string to a class. | ||||
| 
 | ||||
|     Create a `Hero` class with `id` and `name` properties. | ||||
|     Keep this near the top of hte `app.ts` file for now. | ||||
| 
 | ||||
|     ``` | ||||
|     class Hero { | ||||
|       id: number; | ||||
|       name: string; | ||||
|     } | ||||
|     ``` | ||||
| 
 | ||||
|     Now that we have a `Hero` class, let’s refactor our component’s `hero` property to be of type `Hero`. | ||||
|     Then initialize it with an id of `1` and the name, "Windstorm". | ||||
| 
 | ||||
|     ``` | ||||
|     public hero: Hero = { | ||||
|       id: 1, | ||||
|       name: 'Windstorm' | ||||
|     }; | ||||
|     ``` | ||||
| 
 | ||||
|     Because we changed the hero from a string to an object, | ||||
|     we update the binding in the template to refer to the hero’s `name` property. | ||||
| 
 | ||||
|   code-example(format="linenums"). | ||||
|     template: '<h1>{{title}}</h1><h2>{{hero.name}} details!</h2>' | ||||
|   :markdown | ||||
|     The browser refreshes and continues to display our hero’s name. | ||||
| 
 | ||||
|     ## **Adding more HTML** | ||||
|     Displaying a name is good, but we want to see all of our hero’s properties. | ||||
|     We’ll add a `<div>` for our hero’s `id` property and another `<div>` for our hero’s `name`. | ||||
| 
 | ||||
|   code-example(format="linenums"). | ||||
|     template: '<h1>{{title}}</h1><h2>{{hero.name}} details!</h2><div><label>id: </label>{{hero.id}}</div><div><label>name: </label>{{hero.name}}</div>' | ||||
|   :markdown | ||||
|     Uh oh, our template string is getting long. We better take care of that to avoid the risk of making a typo in the template. | ||||
| 
 | ||||
|     ### Multi-line Template Strings | ||||
| 
 | ||||
|     We could make a more readable template with string concatenation | ||||
|     but that gets ugly fast, it is harder to read, and | ||||
|     it is easy to make a spelling error. Instead, | ||||
|     let’s take advantage of the template strings feature | ||||
|     in ES2015 and TypeScript to maintain our sanity. | ||||
| 
 | ||||
|     Change the quotes around the template to back-ticks and | ||||
|     put the `<h1>`, `<h2>` and `<div>` elements on their own lines. | ||||
| 
 | ||||
|   code-example(format="linenums"). | ||||
|     template:` | ||||
|       <h1>{{title}}</h1> | ||||
|       <h2>{{hero.name}} details!</h2> | ||||
|       <div><label>id: </label>{{hero.id}}</div> | ||||
|       <div><label>name: </label>{{hero.name}}</div> | ||||
|       ` | ||||
| 
 | ||||
|   .callout.is-important | ||||
|     header A back-tick is not a single quote | ||||
|     :markdown | ||||
|       **Be careful!** A back-tick (`) looks a lot like a single quote ('). | ||||
|       It's actually a completely different character. | ||||
|       Back-ticks can do more than demarcate a string. | ||||
|       Here we use them in a limited way to spread the template over multiple lines. | ||||
|       Everything between the back-ticks at the beginning and end of the template | ||||
|       is part of a single template string. | ||||
| 
 | ||||
| .l-main-section | ||||
| 
 | ||||
|   :markdown | ||||
|     #  Editing Our Hero | ||||
| 
 | ||||
|     We want to be able to edit the hero name in a textbox. | ||||
| 
 | ||||
|     Replace the hero name `<label>` with an `<input>` element as shown below: | ||||
|   code-example(format="linenums"). | ||||
|     template:` | ||||
|       <h1>{{title}}</h1> | ||||
|       <h2>{{hero.name}} details!</h2> | ||||
|       <div><label>id: </label>{{hero.id}}</div> | ||||
|       <div> | ||||
|         <label>name: </label> | ||||
|         <div><input value="{{hero.name}}" placeholder="name"></input></div> | ||||
|       </div> | ||||
|       ` | ||||
|   :markdown | ||||
|     We see in the browser that the hero’s name does appear in the `<input>` textbox. | ||||
|     But something doesn’t feel right. | ||||
|     When we change the name, we notice that our change | ||||
|     is not reflected in the `<h2>`. We won't get the desired behavior | ||||
|     with a one-way binding to `<input>`. | ||||
| 
 | ||||
|     ## Two-Way Binding | ||||
| 
 | ||||
|     We intend to display the name of the hero in the `<input>`, change it, | ||||
|     and see those changes wherever we bind to the hero’s name. | ||||
|     In short, we want two-way data binding. | ||||
| 
 | ||||
|     Let’s update the template to use the  **`ng-model`** built-in directive for two-way binding. | ||||
|   | ||||
|   .alert.is-helpful | ||||
|     :markdown | ||||
|       Learn more about `ng-model` in the [Template Syntax](../guide/template-syntax#ng-model) | ||||
|   :markdown | ||||
|     Replace the  `<input>` with the following HTML | ||||
| 
 | ||||
|     ``` | ||||
|     <input [(ng-model)]="hero.name" placeholder="name"></input> | ||||
|     ``` | ||||
|     Unfortunately, that change broke our application and we're no longer displaying the hero in the browser. | ||||
|     Let’s fix that next. | ||||
| 
 | ||||
| .l-main-section | ||||
| 
 | ||||
|   :markdown | ||||
|     # Declaring Template Directives | ||||
| 
 | ||||
|     We added the `ng-model` directive but we didn't tell Angular about it. | ||||
|     A component must disclose every directive that appears in its template. | ||||
| 
 | ||||
|     Let’s first gain access to the `NgModel` directive class by importing it from Angular as shown below: | ||||
| 
 | ||||
|     ```` | ||||
|     import {bootstrap, Component, NgModel} from 'angular2/angular2'; | ||||
|     ``` | ||||
| 
 | ||||
|     Now tell the component that we will use the  `ng-model` directive in the template | ||||
|     by adding the `directives` property to the `@Component` decoration | ||||
|     immediately below the `template` string: | ||||
| 
 | ||||
|     ``` | ||||
|     directives: [NgModel] | ||||
|     ``` | ||||
| 
 | ||||
|     The `directives` property is an array holding all directive classes that | ||||
|     are used by the component’s template. | ||||
| 
 | ||||
|     Unfortunately when we view the app in the browser we still have an error: | ||||
| 
 | ||||
|     ``` | ||||
|     *EXCEPTION: No value accessor for ' ' in [null]* | ||||
|     ``` | ||||
| 
 | ||||
|     Apparently declaring the `NgModel` is not quite enough. | ||||
| 
 | ||||
|     ## Declare Multiple Form Directives | ||||
| 
 | ||||
|     We learned from our latest error message that we can’t the import `NgModel` alone. | ||||
|     We need additional directives to enable two-way data binding with `NgModel`. | ||||
| 
 | ||||
|     We could hunt them down and add each of them to the `directives` array one by one. | ||||
|     That's painful. No one wants to remember all of the necessary directives and | ||||
|     type them correctly. Fortunately, there is a shortcut. | ||||
| 
 | ||||
|     The `ng-model` directive is one of many Forms directives which happen to be | ||||
|     bundled in a convenient array called `FORM_DIRECTIVES`. | ||||
|     <!-- TODO | ||||
|     .alert.is-helpful | ||||
|       :markdown | ||||
|         Learn more about Angular Forms in the [Forms chapter]() | ||||
|     :markdown | ||||
|     --> | ||||
|     Let’s forget about importing `NgModel` and import the `FORM_DIRECTIVES` array instead: | ||||
|     ``` | ||||
|     import {bootstrap, Component, FORM_DIRECTIVES} from 'angular2/angular2'; | ||||
|     ``` | ||||
|     Now we tell the component that our template can use `FORM_DIRECTIVES` | ||||
|     by updating the `directives` property of the `@Component` decorator. | ||||
|     ``` | ||||
|     directives: [FORM_DIRECTIVES] | ||||
|     ``` | ||||
|     The browser refreshes. We see our hero again. We can edit the hero’s name and | ||||
|     see the changes reflected immediately in the `<h2>`. | ||||
| 
 | ||||
|     ### Bundled Directives | ||||
|     Angular bundled the Form-related directives together in a convenient `FORM_DIRECTIVES` array. | ||||
|     That's all we need to remember to light up our template. | ||||
| 
 | ||||
|     We may wish to use this trick ourselves someday. | ||||
|     We too can bundle a collection of directives in an array, give it a catchy name, | ||||
|     and plug that array into the `directives` property. | ||||
| 
 | ||||
|     # The Road We’ve Travelled | ||||
|     Let’s take stock of what we’ve built. | ||||
| 
 | ||||
|     * Our Tour of Heroes uses the double curly braces of interpolation (a form of one-way data binding) | ||||
|     to display the application title and properties of a `Hero` object. | ||||
|     * We wrote a multi-line template using ES2015’s template strings to make our template readable. | ||||
|     * We can both display and change the hero’s name after adding a two-way data binding to the `<input>` element | ||||
|     using the built-in `ng-model` directive. | ||||
|     * The `ng-model` directive also propagates changes to every other binding of the `hero.name`. | ||||
|     * We declared our use of `NgModel` and other Form directives | ||||
|     by setting the component's `directives` metadata property to the `FORMS_DIRECTIVES` array. | ||||
| 
 | ||||
|     Here's the complete `AppComponent.ts` as it stands now: | ||||
| 
 | ||||
|   code-example(format="linenums"). | ||||
|     import {bootstrap, Component, FORM_DIRECTIVES} from 'angular2/angular2'; | ||||
| 
 | ||||
|     class Hero { | ||||
|       id: number; | ||||
|       name: string; | ||||
|     } | ||||
| 
 | ||||
|     @Component({ | ||||
|       template:` | ||||
|         <h1>{{title}}</h1> | ||||
|         <h2>{{hero.name}} details!</h2> | ||||
|         <div><label>id: </label>{{hero.id}}</div> | ||||
|         <div> | ||||
|           <label>name: </label> | ||||
|           <div><input value="{{hero.name}}" placeholder="name"></input></div> | ||||
|         </div> | ||||
|         `, | ||||
|       directives: [FORM_DIRECTIVES] | ||||
|     }) | ||||
|     class AppComponent { | ||||
|       public title = 'Tour of Heroes'; | ||||
|       public hero: Hero = { | ||||
|         id: 1, | ||||
|         name: 'Windstorm' | ||||
|       }; | ||||
|     } | ||||
| 
 | ||||
|     bootstrap(AppComponent); | ||||
|      | ||||
|   :markdown | ||||
|     # The Road Ahead | ||||
|     Our Tour of Heroes only displays one hero and we really want to display a list of heroes. | ||||
|     We also want to allow the user to select a hero and display their details. | ||||
|     We’ll learn more about how to retrieve lists, bind them to the | ||||
|     template, and allow a user to select it in the  | ||||
|     [next tutorial chapter](./toh-pt2). | ||||
							
								
								
									
										441
									
								
								public/docs/ts/latest/tutorial/toh-pt2.jade
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										441
									
								
								public/docs/ts/latest/tutorial/toh-pt2.jade
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,441 @@ | ||||
| include ../../../../_includes/_util-fns | ||||
| 
 | ||||
| .l-main-section | ||||
|   :markdown | ||||
|     # It Takes Many Heroes     | ||||
|     Our story needs more heroes.  | ||||
|     We’ll expand our Tour of Heroes app to display a list of heroes,  | ||||
|     allow the user to select a hero, and display the hero’s details.  | ||||
|      | ||||
|     Let’s take stock of what we’ll need to display a list of heroes.  | ||||
|     First, we need a list of heroes. We want to display those heroes in the view’s template,  | ||||
|     so we’ll need a way to do that. | ||||
|       | ||||
| .l-main-section | ||||
|   :markdown | ||||
|     # Where We Left Off | ||||
|     Before we continue with Part 2 of the Tour of Heroes,  | ||||
|     let’s verify we have the following structure after [Part 1](./toh-pt1).  | ||||
|     If not, we’ll need to go back to Part 1 and figure out what we missed. | ||||
|      | ||||
|   code-example. | ||||
|       angular2-tour-of-heroes | ||||
|         ├── node_modules | ||||
|         ├── src | ||||
|         |    ├── app | ||||
|         |    |    └── app.ts | ||||
|         |    ├── index.html | ||||
|         |    └── tsconfig.json | ||||
|         └── package.json  | ||||
|   :markdown | ||||
|     ## Keep the App Running | ||||
|     Start the TypeScript compiler and have it watch for changes in one terminal window by typing | ||||
| 
 | ||||
|   pre.prettyprint.lang-bash | ||||
|     code npm run tsc | ||||
| 
 | ||||
|   :markdown | ||||
|     Now open another terminal window and start the server by typing | ||||
| 
 | ||||
|   pre.prettyprint.lang-bash | ||||
|     code npm start | ||||
| 
 | ||||
|   :markdown | ||||
|     This will keep the application running while we continue to build the Tour of Heroes. | ||||
|      | ||||
|     # Displaying Our Heroes | ||||
|     ## Creating Heroes | ||||
|     Let’s create an array of ten heroes at the bottom of `app.ts`. | ||||
|     ``` | ||||
|     var HEROES: Hero[] = [ | ||||
|     { "id": 11, "name": "Mr. Nice" }, | ||||
|     { "id": 12, "name": "Narco" }, | ||||
|     { "id": 13, "name": "Bombasto" }, | ||||
|     { "id": 14, "name": "Celeritas" }, | ||||
|     { "id": 15, "name": "Magneta" }, | ||||
|     { "id": 16, "name": "RubberMan" }, | ||||
|     { "id": 17, "name": "Dynama" }, | ||||
|     { "id": 18, "name": "Dr IQ" }, | ||||
|     { "id": 19, "name": "Magma" }, | ||||
|     { "id": 20, "name": "Tornado" } | ||||
|     ]; | ||||
|     ``` | ||||
|     The `HEROES` array is of type `Hero`.  | ||||
|     We are taking advantage of the `Hero` class we coded previously to create an array of our heroes.   | ||||
|     We aspire to get this list of heroes from a web service, but let’s take small steps  | ||||
|     on this road and start by displaying these mock heroes in the browser. | ||||
|       | ||||
|     ## Exposing Heroes | ||||
|     Let’s create a public property in `AppComponent` that exposes the heroes for binding. | ||||
|     ``` | ||||
|     public heroes = HEROES; | ||||
|     ``` | ||||
|     We did not have to define the `heroes` type.  TypeScript can infer it from the `HEROES` array. | ||||
|   .alert.is-helpful | ||||
|     :markdown | ||||
|       We could have defined the heroes list here in this component class.  | ||||
|       But we know that we’ll get the heroes from a data service.  | ||||
|       Because know where we are heading, it makes sense to separate the hero data  | ||||
|       from the class implementation from the start. | ||||
|   :markdown | ||||
|     ## Displaying Heroes in a Template | ||||
|     Our component has`heroes`. Let’s create an unordered list in our template to display them.  | ||||
|     We’ll insert the following chunk of HTML below the title and above the hero details. | ||||
|      ``` | ||||
|     <h2>My Heroes</h2> | ||||
|     <ul class="heroes"> | ||||
|       <li> | ||||
|         <!-- each hero goes here --> | ||||
|       </li> | ||||
|     </ul> | ||||
|     ``` | ||||
|     Now we have a template that we can fill with our heroes. | ||||
| 
 | ||||
|     ## Iterating Over Our Heroes with `ng-for` | ||||
|      | ||||
|     We want to bind the array of `heroes` in our component to our template, iterate over them,  | ||||
|     and display them individually.  | ||||
|     We’ll need some help from Angular to do this. Let’s do this step by step. | ||||
|      | ||||
|     First modify the `<li>` tag by adding the built-in directive `*ng-for`.  | ||||
|     ``` | ||||
|     <li *ng-for="#hero of heroes"> | ||||
|     ``` | ||||
|   .alert.is-important | ||||
|     :markdown | ||||
|       Do not neglect the leading asterisk (`*`) in front of `ng-for`! | ||||
|        | ||||
|       Although it is not part of the `ng-for` directive name,  | ||||
|       it is a critical part of the syntax in this usage of `ng-for`. | ||||
|   :markdown | ||||
|     The (`*`) prefix to the `ng-for` indicates that the `<li>` element and its children | ||||
|     constitute a master template. | ||||
|      | ||||
|     The `ng-for` directive iterates over the `heroes` array returned by the `AppComponent.heroes` property | ||||
|     and stamps out instances of this template. | ||||
|                   | ||||
|     The quoted text assigned to `ng-for` means  | ||||
|     “*take each hero in the `heroes` array, store it in the local `hero` variable, | ||||
|     and make it available to the corresponding template instance*”. | ||||
| 
 | ||||
|     The `#` prefix before "hero" identifies the `hero` as a local template variable.  | ||||
|     We can reference this variable within the template to access a hero’s properties. | ||||
| 
 | ||||
|   .alert.is-helpful | ||||
|     :markdown | ||||
|       Learn more about `ng-for` and local template variables in the  | ||||
|       [Template Syntax](../guide/template-syntax#ng-for) chapter. | ||||
| 
 | ||||
|   :markdown | ||||
|     With this background in mind, we now insert some content between the `<li>` tags  | ||||
|     that uses the `hero` template variable to display the hero’s properties. | ||||
|   code-example. | ||||
|     <li *ng-for="#hero of heroes"> | ||||
|       <span class="badge">{{hero.id}}</span> {{hero.name}}</a> | ||||
|     </li> | ||||
|   :markdown | ||||
|     ## Declaring NgFor | ||||
|     When we view the running app in the browser we see nothing … no heroes.  | ||||
|     We open the developer tools and see an error in the console. | ||||
|     ``` | ||||
|     EXCEPTION:  | ||||
|     Can't bind to 'ngForOf' since it isn't a known property of the '<template>' element and  | ||||
|     there are no matching directives with a corresponding property | ||||
|     ``` | ||||
|     Thankfully we have a clear error message that indicates where we went wrong.  | ||||
|     We used `ng-for` in the template but we didn’t tell the component about it.  | ||||
|     From Angular's perspective, `ng-for` is a meaningless attribute.  | ||||
|     When it tries to render the view, it doesn’t recognize `ng-for` and gives up. | ||||
|      | ||||
|     We need to say “*hey component, I’m going to use this NgFor directive. OK?*” | ||||
|      | ||||
|     To that end, we first import  the `NgFor` symbol | ||||
|     ``` | ||||
|     import {bootstrap, Component, FORM_DIRECTIVES, NgFor} from 'angular2/angular2'; | ||||
|     ``` | ||||
|     and then declare `NgFor` to be one of the view’s directives in the `@Component` decorator.  | ||||
|     ``` | ||||
|     directives: [FORM_DIRECTIVES, NgFor] | ||||
|     ``` | ||||
|     After the browser refreshes, we see a list of heroes! | ||||
|      | ||||
|     ## Styling Our Heroes | ||||
|     Our list of heroes looks pretty bland. | ||||
|     We want to make it visually obvious to a user which hero we are hovering over and which hero is selected. | ||||
|      | ||||
|     Let’s add some styles to our component by setting the `styles` property on the `@Component` decorator  | ||||
|     to the following CSS classes: | ||||
|     ``` | ||||
|     styles: ` | ||||
|       .heroes {list-style-type: none; margin-left: 1em; padding: 0; width: 10em;} | ||||
|       .heroes li { cursor: pointer; position: relative; left: 0; transition: all 0.2s ease; } | ||||
|       .heroes li:hover {color: #369; background-color: #EEE; left: .2em;} | ||||
|       .heroes .badge { | ||||
|         font-size: small; | ||||
|         color: white; | ||||
|         padding: 0.1em 0.7em; | ||||
|         background-color: #369; | ||||
|         line-height: 1em; | ||||
|         position: relative; | ||||
|         left: -1px; | ||||
|         top: -1px; | ||||
|        }  | ||||
|        .selected { background-color: #EEE; color: #369; } | ||||
|       ` | ||||
|     ``` | ||||
|     Notice that we again use the back-tick notation for multi-line strings. | ||||
|      | ||||
|     When we assign styles to a component they are scoped to that specific component.  | ||||
|     Our styles will only apply to our `AppComponent` and won't "leak" to the outer HTML. | ||||
|      | ||||
|     Our template for displaying the heroes should now look like this: | ||||
|      | ||||
|   code-example(format="linenums").     | ||||
|     <h2>My Heroes</h2> | ||||
|     <ul class="heroes"> | ||||
|       <li *ng-for="#hero of heroes"> | ||||
|         <span class="badge">{{hero.id}}</span> {{hero.name}}</a> | ||||
|       </li> | ||||
|     </ul> | ||||
|   :markdown | ||||
|     Our styled list of heroes should look like this: | ||||
|   | ||||
|   figure.image-display | ||||
|     img(src='/resources/images/devguide/toh/heroes-list-2.png' alt="Output of heroes list app (no selection color)")  | ||||
| 
 | ||||
| .l-main-section | ||||
|   :markdown | ||||
|     # Selecting a Hero | ||||
|     We have a list of heroes and we have a single hero displayed in our app.  | ||||
|     The list and the single hero are not connected in any way.  | ||||
|     We want the user to select a hero from our list, and have the selected hero appear in the details view.  | ||||
|     This UI pattern is widely known as “master-detail”.  | ||||
|     In our case, the master is the heroes list and the detail is the selected hero.  | ||||
|      | ||||
|     Let’s connect the master to the detail through a `selectedHero` component property bound to a click event. | ||||
|      | ||||
|     ## Click Event | ||||
|     We modify the `<li>` by inserting an Angular event binding to its click event. | ||||
|      | ||||
|   code-example(format="linenums").    | ||||
|     <li *ng-for="#hero of heroes" (click)="onSelect(hero)"> | ||||
|       <span class="badge">{{hero.id}}</span> {{hero.name}}</a> | ||||
|     </li> | ||||
|   :markdown | ||||
|     Focus on the event binding | ||||
|   pre.prettyprint.lang-bash | ||||
|     code (click)="onSelect(hero)"> | ||||
|   :markdown | ||||
|     The parenthesis identify the `<li>` element’s  `click` event as the target.  | ||||
|     The expression to the right of the equal sign calls the  `AppComponent` method, `onSelect()`,  | ||||
|     passing the local template variable `hero` as an argument. | ||||
|     That’s the same `hero` variable we defined previously in the `ng-for`. | ||||
|   .alert.is-helpful | ||||
|     :markdown | ||||
|       Learn more about Event Binding in the [Templating Syntax](../guide/template-syntax#event-binding) chapter. | ||||
|   :markdown | ||||
|     ## Add the click handler | ||||
|     Our event binding refers to an `onSelect` method that doesn’t exist yet.  | ||||
|     We’ll add that method to our component now.  | ||||
|      | ||||
|     What should that method do? It should set the component’s selected hero to the hero that the user clicked. | ||||
|      | ||||
|     Our component doesn’t have a “selected hero” yet either. We’ll start there. | ||||
|      | ||||
|     ### Expose the Selected Hero  | ||||
|     We no longer need the static `hero` property of the `AppComponent`. | ||||
|     **Replace** it with this simple `selectedHero` property: | ||||
|     ``` | ||||
|     public selectedHero: Hero; | ||||
|     ``` | ||||
|     We’ve decided that none of the heroes should be selected before the user picks a hero so  | ||||
|     we won’t initialize the `selectedHero` as we were doing with `hero`.  | ||||
|      | ||||
|     Now **add an `onSelect` method** that sets the `selectedHero` property to the `hero` the user clicked. | ||||
|     ``` | ||||
|     onSelect(hero: Hero) { this.selectedHero = hero; } | ||||
|     ``` | ||||
|      | ||||
|     We will be showing the selected hero's details in our template.  | ||||
|     At the moment, it is still referring to the old `hero` property.  | ||||
|     Let’s fix the template to bind to the new `selectedHero` property. | ||||
|      | ||||
|   code-example(format="linenums").     | ||||
|     <h2>{{selectedHero.name}} details!</h2> | ||||
|     <div><label>id: </label>{{selectedHero.id}}</div> | ||||
|     <div> | ||||
|         <label>name: </label> | ||||
|         <input [(ng-model)]="selectedHero.name" placeholder="name"></input> | ||||
|     </div> | ||||
|      | ||||
|   :markdown | ||||
|     ### Hide the empty detail with NgIf | ||||
|      | ||||
|     When our app loads we see a list of heroes, but a hero is not selected.  | ||||
|     The `selectedHero` is `undefined`.  | ||||
|     That’s why we'll' see the following error in the browser’s console: | ||||
|     ``` | ||||
|     EXCEPTION: TypeError: Cannot read property 'name' of undefined in [null] | ||||
|     ```  | ||||
|     Remember that we are displaying `selectedHero.name` in the template.  | ||||
|     This name property does not exist because `selectedHero`itself is undefined.  | ||||
|      | ||||
|     We'll' address this problem by keeping the hero detail out of the DOM until there is a selected hero. | ||||
|      | ||||
|     We wrap the HTML hero detail content of our template with a `<div>`.  | ||||
|     Then we add the `ng-if` built-in directive and set it to the `selectedHero` property of our component. | ||||
|      | ||||
|   code-example(format="linenums").      | ||||
|       <div *ng-if="selectedHero"> | ||||
|         <h2>{{selectedHero.name}} details!</h2> | ||||
|         <div><label>id: </label>{{selectedHero.id}}</div> | ||||
|         <div> | ||||
|           <label>name: </label> | ||||
|           <input [(ng-model)]="selectedHero.name" placeholder="name"></input> | ||||
|         </div> | ||||
|       </div> | ||||
|   .alert.is-important | ||||
|     :markdown | ||||
|       Do not neglect the leading asterisk (`*`) in front of `ng-if`.  | ||||
|       It's a critical part of the syntax in this usage of `ng-if`.       | ||||
|   :markdown | ||||
|     When there is no `selectedHero`, the `ng-if` directive removes the hero detail HTML from the DOM.  | ||||
|     There will no hero detail elements and no bindings to worry about.  | ||||
|      | ||||
|     When the user picks a hero, `selectedHero` becomes "truthy" and | ||||
|     `ng-if` puts the hero detail content into the DOM and evaluates the nested bindings.  | ||||
|      | ||||
|   .alert.is-helpful | ||||
|     :markdown | ||||
|       `ng-if` and `ng-for` are called “structural directives” because they can change the  | ||||
|       structure of portions of the DOM.  | ||||
|       In other words, they give structure to the way Angular displays content in the DOM. | ||||
|         | ||||
|       Learn more about `ng-if`, `ng-for` and other structural directives in the  | ||||
|       [Template Syntax](../guide/template-syntax#directives) chapter | ||||
|      | ||||
|   :markdown | ||||
|     We learned previously with `NgFor` that we must declare every directive we use in the component’s `@Component` decorator.  | ||||
|     Let’s do that again for `NgIf`. | ||||
|      | ||||
|     Add the `NgIf` symbol to our imports at the top of our `app.ts` file, keeping them sorted | ||||
|     alphabetically to make them easier to find: | ||||
|     ``` | ||||
|     import {bootstrap, Component, FORM_DIRECTIVES, NgFor, NgIf} from 'angular2/angular2'; | ||||
|     ``` | ||||
|   :markdown | ||||
|     Now add `NgIf` to the directives array in the `@Component` decorator: | ||||
|     ``` | ||||
|     directives: [FORM_DIRECTIVES, NgFor, NgIf] | ||||
|     ``` | ||||
|     The browser refreshes and we see the list of heroes but not the selected hero detail.  | ||||
|     The `ng-if` keeps it out of the DOM as long as the `selectedHero` is undefined.  | ||||
|     When we click on a hero in the list, the selected hero displays in the hero details.  | ||||
|     Everything is working as we expect. | ||||
|       | ||||
|     ### Styling the Selection | ||||
|      | ||||
|     We see the selected hero in the details area below but we can’t quickly locate that hero in the list above.  | ||||
|     We can fix that by applying the `selected` CSS class to the appropriate `<li>` in the master list.  | ||||
|     For example, when we select Magneta from the heroes list,  | ||||
|     we can make it pop out visually by giving it a subtle background color as shown here. | ||||
|      | ||||
|   figure.image-display | ||||
|     img(src='/resources/images/devguide/toh/heroes-list-selected.png' alt="Selected hero") | ||||
|   :markdown | ||||
|     First we’ll add a `getSelectedClass` method to the component that compares the current `selectedHero` to a hero parameter  | ||||
|     and returns an object with a single key/value pair.  | ||||
|      | ||||
|     The key is the name of the CSS class (`selected`). The value is `true` if the two heroes match and `false` otherwise.  | ||||
|     We’re saying “*apply the `selected` class if the heroes match, remove it if they don’t*”.  | ||||
|     Here is that method. | ||||
|     ``` | ||||
|     getSelectedClass(hero: Hero) { | ||||
|       return { 'selected': hero === this.selectedHero }; | ||||
|     } | ||||
|     ``` | ||||
|     What do we do with this method and its peculiar result? | ||||
|      | ||||
|     ### NgClass | ||||
|     We’ll add the `ng-class`built-in directive to the `<li>` element in our template and bind it to `getSelectedClass`. | ||||
|     It’s no coincidence that the value returned by `getSelectedClass` is exactly what the `ng-class` requires  | ||||
|     to add or remove the `selected` class to each hero’s display. | ||||
|   code-example(format="linenums").      | ||||
|       <li *ng-for="#hero of heroes" | ||||
|         [ng-class]="getSelectedClass(hero)" | ||||
|         (click)="onSelect(hero)"> | ||||
|         <span class="badge">{{hero.id}}</span> {{hero.name}}</a> | ||||
|       </li> | ||||
| 
 | ||||
|   .alert.is-helpful     | ||||
|     :markdown | ||||
|       Learn more about `ng-class` in the  | ||||
|       [Template Syntax](../guide/template-syntax#ng-class) chapter | ||||
|           | ||||
|   :markdown | ||||
|     Notice in the template that the `ng-class` name is surrounded in square brackets (`[]`).   | ||||
|     This is the syntax for a Property Binding, a binding in which data flow one way  | ||||
|     from the data source (the `getSelectedClass`) to a property of the `ng-class` directive.  | ||||
|   | ||||
|   .alert.is-helpful | ||||
|     :markdown | ||||
|       Learn more about about one-way property data binding in the | ||||
|       [Template Syntax](../guide/template-syntax#property-binding) chapter | ||||
|   :markdown | ||||
|     We've added yet another new directive to our template that we have to import and declare  | ||||
|     in the component’s `directives` array as we’ve done twice before. | ||||
|     ``` | ||||
|     import {bootstrap, Component,  | ||||
|             FORM_DIRECTIVES, NgClass, NgFor, NgIf} from 'angular2/angular2'; | ||||
|     ``` | ||||
|      | ||||
|     ```     | ||||
|     directives: [FORM_DIRECTIVES, NgClass, NgFor, NgIf] | ||||
|     ``` | ||||
|     The browser reloads our app.  | ||||
|     We select a hero and the selection is clearly identified by the background color. | ||||
|       | ||||
|   figure.image-display | ||||
|     img(src='/resources/images/devguide/toh/heroes-list-1.png' alt="Output of heroes list app") | ||||
|      | ||||
|   :markdown | ||||
|     We select a different hero and the tell-tale color switches to that hero. | ||||
|         | ||||
|     ## Declaring Built-In Directives | ||||
|      | ||||
|     Every time we used a directive, we imported it and declared it in the component.  | ||||
|     We only used three directives but we can easily envision a component that uses many more.  | ||||
|     The `directives` array grows quickly and the process of importing the directive and adding it to the array is tedious.  | ||||
|     We can make this easier. | ||||
|      | ||||
|     Remember how we imported the `FORM_DIRECTIVES` array to help us apply `ng-model`to our template in the previous chapter? | ||||
|     The `FORM_DIRECTIVES` array held all the directives we needed for `ng-model` (and a few more).  | ||||
|     We didn’t have to list them. We simply added the `FORM_DIRECTIVES` array to the component’s `directives` array. | ||||
|      | ||||
|     The `NgClass`, `NgFor`, and `NgIf` are extremely common directives used by many components in many applications.  | ||||
|     Fortunately they are all exported from Angular as part of the `CORE_DIRECTIVES` array.  | ||||
|      | ||||
|     Let’s replace all of those separate import variables with `CORE_DIRECTIVES`: | ||||
|     ``` | ||||
|     import {bootstrap, Component, CORE_DIRECTIVES, FORM_DIRECTIVES} from 'angular2/angular2'; | ||||
|     ``` | ||||
|     Then replace `NgClass`, `NgFor`, and `NgIf` in the `directives` array with `CORE_DIRECTIVES`: | ||||
|     ``` | ||||
|     directives: [CORE_DIRECTIVES, FORM_DIRECTIVES] | ||||
|     ``` | ||||
|     Everything still works and we have a convenient way to import and declare the most commonly used directives.  | ||||
|     Cleaner code for the win!      | ||||
|    | ||||
| .l-main-section | ||||
|   :markdown | ||||
|     # The Road We’ve Travelled | ||||
|     Here’s what we achieved in this chapter: | ||||
|      | ||||
|     * Our Tour of Heroes now displays a list of selectable heroes  | ||||
|     * We added the ability to select a hero and show the hero’s details | ||||
|     * We learned how to use the built-in directives `ng-if`, `ng-for` and `ng-class` in a component’s template | ||||
|       | ||||
|     ## The Road Ahead | ||||
|     Our Tour of Heroes has grown, but it’s far from complete.  | ||||
|     We want to get data from an asynchronous source using promises, use shared services, and create reusable components.  | ||||
|     We’ll learn more about these tasks in the coming tutorial chapters.          | ||||
							
								
								
									
										
											BIN
										
									
								
								public/resources/images/devguide/toh/heroes-list-2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/resources/images/devguide/toh/heroes-list-2.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 52 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/resources/images/devguide/toh/heroes-list-selected.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/resources/images/devguide/toh/heroes-list-selected.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 8.1 KiB | 
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user