(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", |   "_listtype": "ordered", | ||||||
| 
 | 
 | ||||||
|   "index": { |   "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." |     "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 | include ../../../../_includes/_util-fns | ||||||
| 
 | 
 | ||||||
| .l-main-section | .l-main-section | ||||||
| 
 |  | ||||||
|   :markdown |   :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. |     Even heroes need to find work. | ||||||
| 
 | 
 | ||||||
|     Of course we'll only make a little progress in this tutorial. What we do build will |     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 |     ## 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: |     view and its list of heroes: | ||||||
| 
 | 
 | ||||||
|   figure.image-display |   figure.image-display | ||||||
| @ -56,312 +55,5 @@ include ../../../../_includes/_util-fns | |||||||
|     met in countless applications. Everything has a reason. |     met in countless applications. Everything has a reason. | ||||||
| 
 | 
 | ||||||
|     And we’ll meet many of the core fundamentals of Angular along the way. |     And we’ll meet many of the core fundamentals of Angular along the way. | ||||||
| 
 |   | ||||||
| .l-main-section |     [Let's get started!](./toh-pt1). | ||||||
| 
 |  | ||||||
|   :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. |  | ||||||
|     --> |  | ||||||
|  | |||||||
							
								
								
									
										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