5 lines
22 KiB
JSON
5 lines
22 KiB
JSON
{
|
|
"id": "tutorial/toh-pt2",
|
|
"title": "Display a selection list",
|
|
"contents": "\n\n\n<div class=\"github-links\">\n <a href=\"https://github.com/angular/angular/edit/master/aio/content/tutorial/toh-pt2.md?message=docs%3A%20describe%20your%20change...\" aria-label=\"Suggest Edits\" title=\"Suggest Edits\"><i class=\"material-icons\" aria-hidden=\"true\" role=\"img\">mode_edit</i></a>\n</div>\n\n\n<div class=\"content\">\n <h1 id=\"display-a-selection-list\">Display a selection list<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt2#display-a-selection-list\"><i class=\"material-icons\">link</i></a></h1>\n<p>In this page, you'll expand the Tour of Heroes application to display a list of heroes, and\nallow users to select a hero and display the hero's details.</p>\n<div class=\"alert is-helpful\">\n<p> For the sample application that this page describes, see the <live-example></live-example>.</p>\n</div>\n<h2 id=\"create-mock-heroes\">Create mock heroes<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt2#create-mock-heroes\"><i class=\"material-icons\">link</i></a></h2>\n<p>You'll need some heroes to display.</p>\n<p>Eventually you'll get them from a remote data server.\nFor now, you'll create some <em>mock heroes</em> and pretend they came from the server.</p>\n<p>Create a file called <code>mock-heroes.ts</code> in the <code>src/app/</code> folder.\nDefine a <code>HEROES</code> constant as an array of ten heroes and export it.\nThe file should look like this.</p>\n<code-example path=\"toh-pt2/src/app/mock-heroes.ts\" header=\"src/app/mock-heroes.ts\">\nimport { Hero } from './hero';\n\nexport const HEROES: Hero[] = [\n { id: 11, name: 'Dr Nice' },\n { id: 12, name: 'Narco' },\n { id: 13, name: 'Bombasto' },\n { id: 14, name: 'Celeritas' },\n { id: 15, name: 'Magneta' },\n { id: 16, name: 'RubberMan' },\n { id: 17, name: 'Dynama' },\n { id: 18, name: 'Dr IQ' },\n { id: 19, name: 'Magma' },\n { id: 20, name: 'Tornado' }\n];\n\n\n</code-example>\n<h2 id=\"displaying-heroes\">Displaying heroes<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt2#displaying-heroes\"><i class=\"material-icons\">link</i></a></h2>\n<p>Open the <code>HeroesComponent</code> class file and import the mock <code>HEROES</code>.</p>\n<code-example path=\"toh-pt2/src/app/heroes/heroes.component.ts\" region=\"import-heroes\" header=\"src/app/heroes/heroes.component.ts (import HEROES)\">\nimport { HEROES } from '../mock-heroes';\n\n</code-example>\n<p>In the same file (<code>HeroesComponent</code> class), define a component property called <code>heroes</code> to expose the <code>HEROES</code> array for binding.</p>\n<code-example path=\"toh-pt2/src/app/heroes/heroes.component.ts\" header=\"src/app/heroes/heroes.component.ts\" region=\"component\">\nexport class HeroesComponent implements <a href=\"api/core/OnInit\" class=\"code-anchor\">OnInit</a> {\n\n heroes = HEROES;\n}\n\n</code-example>\n<h3 id=\"list-heroes-with-ngfor\">List heroes with <code>*<a href=\"api/common/NgForOf\" class=\"code-anchor\">ngFor</a></code><a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt2#list-heroes-with-ngfor\"><i class=\"material-icons\">link</i></a></h3>\n<p>Open the <code>HeroesComponent</code> template file and make the following changes:</p>\n<ul>\n<li>Add an <code><h2></code> at the top,</li>\n<li>Below it add an HTML unordered list (<code><ul></code>)</li>\n<li>Insert an <code><li></code> within the <code><ul></code> that displays properties of a <code>hero</code>.</li>\n<li>Sprinkle some CSS classes for styling (you'll add the CSS styles shortly).</li>\n</ul>\n<p>Make it look like this:</p>\n<code-example path=\"toh-pt2/src/app/heroes/heroes.component.1.html\" region=\"list\" header=\"heroes.component.html (heroes template)\">\n<h2>My Heroes</h2>\n<ul class=\"heroes\">\n <li>\n <span class=\"badge\">{{hero.id}}</span> {{hero.name}}\n </li>\n</ul>\n\n</code-example>\n<p>That shows one hero. To list them all, add an <code>*<a href=\"api/common/NgForOf\" class=\"code-anchor\">ngFor</a></code> to the <code><li></code> to iterate through the list of heroes:</p>\n<code-example path=\"toh-pt2/src/app/heroes/heroes.component.1.html\" region=\"li\">\n<li *<a href=\"api/common/NgForOf\" class=\"code-anchor\">ngFor</a>=\"let hero of heroes\">\n\n</code-example>\n<p>The <a href=\"guide/built-in-directives#ngFor\"><code>*ngFor</code></a> is Angular's <em>repeater</em> directive.\nIt repeats the host element for each element in a list.</p>\n<p>The syntax in this example is as follows:</p>\n<ul>\n<li><code><li></code> is the host element.</li>\n<li><code>heroes</code> holds the mock heroes list from the <code>HeroesComponent</code> class, the mock heroes list.</li>\n<li><code>hero</code> holds the current hero object for each iteration through the list.</li>\n</ul>\n<div class=\"alert is-important\">\n<p>Don't forget the asterisk (*) in front of <code><a href=\"api/common/NgForOf\" class=\"code-anchor\">ngFor</a></code>. It's a critical part of the syntax.</p>\n</div>\n<p>After the browser refreshes, the list of heroes appears.</p>\n<a id=\"styles\"></a>\n<h3 id=\"style-the-heroes\">Style the heroes<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt2#style-the-heroes\"><i class=\"material-icons\">link</i></a></h3>\n<p>The heroes list should be attractive and should respond visually when users\nhover over and select a hero from the list.</p>\n<p>In the <a href=\"tutorial/toh-pt0#app-wide-styles\">first tutorial</a>, you set the basic styles for the entire application in <code>styles.css</code>.\nThat stylesheet didn't include styles for this list of heroes.</p>\n<p>You could add more styles to <code>styles.css</code> and keep growing that stylesheet as you add components.</p>\n<p>You may prefer instead to define private styles for a specific component and keep everything a component needs— the code, the HTML,\nand the CSS —together in one place.</p>\n<p>This approach makes it easier to re-use the component somewhere else\nand deliver the component's intended appearance even if the global styles are different.</p>\n<p>You define private styles either inline in the <code>@<a href=\"api/core/Component#styles\" class=\"code-anchor\">Component.styles</a></code> array or\nas stylesheet file(s) identified in the <code>@<a href=\"api/core/Component#styleUrls\" class=\"code-anchor\">Component.styleUrls</a></code> array.</p>\n<p>When the CLI generated the <code>HeroesComponent</code>, it created an empty <code>heroes.component.css</code> stylesheet for the <code>HeroesComponent</code>\nand pointed to it in <code>@<a href=\"api/core/Component#styleUrls\" class=\"code-anchor\">Component.styleUrls</a></code> like this.</p>\n<code-example path=\"toh-pt2/src/app/heroes/heroes.component.ts\" region=\"metadata\" header=\"src/app/heroes/heroes.component.ts (@Component)\">\n@<a href=\"api/core/Component\" class=\"code-anchor\">Component</a>({\n selector: 'app-heroes',\n templateUrl: './heroes.component.html',\n styleUrls: ['./heroes.component.css']\n})\n\n</code-example>\n<p>Open the <code>heroes.component.css</code> file and paste in the private CSS styles for the <code>HeroesComponent</code>.\nYou'll find them in the <a href=\"tutorial/toh-pt2#final-code-review\">final code review</a> at the bottom of this guide.</p>\n<div class=\"alert is-important\">\n<p>Styles and stylesheets identified in <code>@<a href=\"api/core/Component\" class=\"code-anchor\">Component</a></code> metadata are scoped to that specific component.\nThe <code>heroes.component.css</code> styles apply only to the <code>HeroesComponent</code> and don't affect the outer HTML or the HTML in any other component.</p>\n</div>\n<h2 id=\"viewing-details\">Viewing details<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt2#viewing-details\"><i class=\"material-icons\">link</i></a></h2>\n<p>When the user clicks a hero in the list, the component should display the selected hero's details at the bottom of the page.</p>\n<p>In this section, you'll listen for the hero item click event\nand update the hero detail.</p>\n<h3 id=\"add-a-click-event-binding\">Add a click event binding<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt2#add-a-click-event-binding\"><i class=\"material-icons\">link</i></a></h3>\n<p>Add a click event binding to the <code><li></code> like this:</p>\n<code-example path=\"toh-pt2/src/app/heroes/heroes.component.1.html\" region=\"selectedHero-click\" header=\"heroes.component.html (template excerpt)\">\n<li *<a href=\"api/common/NgForOf\" class=\"code-anchor\">ngFor</a>=\"let hero of heroes\" (click)=\"onSelect(hero)\">\n\n</code-example>\n<p>This is an example of Angular's <a href=\"guide/event-binding\">event binding</a> syntax.</p>\n<p>The parentheses around <code>click</code> tell Angular to listen for the <code><li></code> element's <code>click</code> event.\nWhen the user clicks in the <code><li></code>, Angular executes the <code>onSelect(hero)</code> expression.</p>\n<p>In the next section, define an <code>onSelect()</code> method in <code>HeroesComponent</code> to\ndisplay the hero that was defined in the <code>*<a href=\"api/common/NgForOf\" class=\"code-anchor\">ngFor</a></code> expression.</p>\n<h3 id=\"add-the-click-event-handler\">Add the click event handler<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt2#add-the-click-event-handler\"><i class=\"material-icons\">link</i></a></h3>\n<p>Rename the component's <code>hero</code> property to <code>selectedHero</code> but don't assign it.\nThere is no <em>selected hero</em> when the application starts.</p>\n<p>Add the following <code>onSelect()</code> method, which assigns the clicked hero from the template\nto the component's <code>selectedHero</code>.</p>\n<code-example path=\"toh-pt2/src/app/heroes/heroes.component.ts\" region=\"on-select\" header=\"src/app/heroes/heroes.component.ts (onSelect)\">\nselectedHero?: Hero;\nonSelect(hero: Hero): void {\n this.selectedHero = hero;\n}\n\n</code-example>\n<h3 id=\"add-a-details-section\">Add a details section<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt2#add-a-details-section\"><i class=\"material-icons\">link</i></a></h3>\n<p>Currently, you have a list in the component template. To click on a hero on the list\nand reveal details about that hero, you need a section for the details to render in the\ntemplate. Add the following to <code>heroes.component.html</code> beneath the list section:</p>\n<code-example path=\"toh-pt2/src/app/heroes/heroes.component.html\" region=\"selectedHero-details\" header=\"heroes.component.html (selected hero details)\">\n<h2>{{selectedHero.name | <a href=\"api/common/UpperCasePipe\" class=\"code-anchor\">uppercase</a>}} Details</h2>\n<div><span>id: </span>{{selectedHero.id}}</div>\n<div>\n <label for=\"hero-name\">Hero name: </label>\n <input id=\"hero-name\" [(<a href=\"api/forms/NgModel\" class=\"code-anchor\">ngModel</a>)]=\"selectedHero.name\" placeholder=\"name\">\n</div>\n\n</code-example>\n<p>After the browser refreshes, the application is broken.</p>\n<p>Open the browser developer tools and look in the console for an error message like this:</p>\n<code-example language=\"sh\" class=\"code-shell\">\n HeroesComponent.html:3 ERROR TypeError: Cannot read property 'name' of undefined\n</code-example>\n<h4 id=\"what-happened\">What happened?<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt2#what-happened\"><i class=\"material-icons\">link</i></a></h4>\n<p>When the application starts, the <code>selectedHero</code> is <code>undefined</code> <em>by design</em>.</p>\n<p>Binding expressions in the template that refer to properties of <code>selectedHero</code>—expressions like <code>{{selectedHero.name}}</code>—<em>must fail</em> because there is no selected hero.</p>\n<h4 id=\"the-fix---hide-empty-details-with-ngif\">The fix - hide empty details with <em>*ngIf</em><a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt2#the-fix---hide-empty-details-with-ngif\"><i class=\"material-icons\">link</i></a></h4>\n<p>The component should only display the selected hero details if the <code>selectedHero</code> exists.</p>\n<p>Wrap the hero detail HTML in a <code><div></code>.\nAdd Angular's <code>*<a href=\"api/common/NgIf\" class=\"code-anchor\">ngIf</a></code> directive to the <code><div></code> and set it to <code>selectedHero</code>.</p>\n<div class=\"alert is-important\">\n<p>Don't forget the asterisk (*) in front of <code><a href=\"api/common/NgIf\" class=\"code-anchor\">ngIf</a></code>. It's a critical part of the syntax.</p>\n</div>\n<code-example path=\"toh-pt2/src/app/heroes/heroes.component.html\" region=\"ng-if\" header=\"src/app/heroes/heroes.component.html (*ngIf)\">\n<div *<a href=\"api/common/NgIf\" class=\"code-anchor\">ngIf</a>=\"selectedHero\">\n\n <h2>{{selectedHero.name | <a href=\"api/common/UpperCasePipe\" class=\"code-anchor\">uppercase</a>}} Details</h2>\n <div><span>id: </span>{{selectedHero.id}}</div>\n <div>\n <label for=\"hero-name\">Hero name: </label>\n <input id=\"hero-name\" [(<a href=\"api/forms/NgModel\" class=\"code-anchor\">ngModel</a>)]=\"selectedHero.name\" placeholder=\"name\">\n </div>\n\n</div>\n\n</code-example>\n<p>After the browser refreshes, the list of names reappears.\nThe details area is blank.\nClick a hero in the list of heroes and its details appear.\nThe application seems to be working again.\nThe heroes appear in a list and details about the clicked hero appear at the bottom of the page.</p>\n<h4 id=\"why-it-works\">Why it works<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt2#why-it-works\"><i class=\"material-icons\">link</i></a></h4>\n<p>When <code>selectedHero</code> is undefined, the <code><a href=\"api/common/NgIf\" class=\"code-anchor\">ngIf</a></code> removes the hero detail from the DOM. There are no <code>selectedHero</code> bindings to consider.</p>\n<p>When the user picks a hero, <code>selectedHero</code> has a value and\n<code><a href=\"api/common/NgIf\" class=\"code-anchor\">ngIf</a></code> puts the hero detail into the DOM.</p>\n<h3 id=\"style-the-selected-hero\">Style the selected hero<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt2#style-the-selected-hero\"><i class=\"material-icons\">link</i></a></h3>\n<p>To help identify the selected hero, you can use the <code>.selected</code> CSS class in the <a href=\"tutorial/toh-pt2#styles\">styles you added earlier</a>.\nTo apply the <code>.selected</code> class to the <code><li></code> when the user clicks it, use class binding.</p>\n<div class=\"lightbox\">\n <img src=\"generated/images/guide/toh/heroes-list-selected.png\" alt=\"Selected hero with dark background and light text that differentiates it from unselected list items\" width=\"594\" height=\"290\">\n</div>\n<p>Angular's <a href=\"guide/attribute-binding#class-binding\">class binding</a> can add and remove a CSS class conditionally.\nJust add <code>[class.some-css-class]=\"some-condition\"</code> to the element you want to style.</p>\n<p>Add the following <code>[class.selected]</code> binding to the <code><li></code> in the <code>HeroesComponent</code> template:</p>\n<code-example path=\"toh-pt2/src/app/heroes/heroes.component.1.html\" region=\"class-selected\" header=\"heroes.component.html (toggle the 'selected' CSS class)\">\n[class.selected]=\"hero === selectedHero\"\n\n</code-example>\n<p>When the current row hero is the same as the <code>selectedHero</code>, Angular adds the <code>selected</code> CSS class. When the two heroes are different, Angular removes the class.</p>\n<p>The finished <code><li></code> looks like this:</p>\n<code-example path=\"toh-pt2/src/app/heroes/heroes.component.html\" region=\"li\" header=\"heroes.component.html (list item hero)\">\n<li *<a href=\"api/common/NgForOf\" class=\"code-anchor\">ngFor</a>=\"let hero of heroes\"\n [class.selected]=\"hero === selectedHero\"\n (click)=\"onSelect(hero)\">\n <span class=\"badge\">{{hero.id}}</span> {{hero.name}}\n</li>\n\n</code-example>\n<a id=\"final-code-review\"></a>\n<h2 id=\"final-code-review\">Final code review<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt2#final-code-review\"><i class=\"material-icons\">link</i></a></h2>\n<p>Here are the code files discussed on this page, including the <code>HeroesComponent</code> styles.</p>\n<code-tabs>\n\n <code-pane header=\"src/app/mock-heroes.ts\" path=\"toh-pt2/src/app/mock-heroes.ts\">\nimport { Hero } from './hero';\n\nexport const HEROES: Hero[] = [\n { id: 11, name: 'Dr Nice' },\n { id: 12, name: 'Narco' },\n { id: 13, name: 'Bombasto' },\n { id: 14, name: 'Celeritas' },\n { id: 15, name: 'Magneta' },\n { id: 16, name: 'RubberMan' },\n { id: 17, name: 'Dynama' },\n { id: 18, name: 'Dr IQ' },\n { id: 19, name: 'Magma' },\n { id: 20, name: 'Tornado' }\n];\n\n\n</code-pane>\n\n <code-pane header=\"src/app/heroes/heroes.component.ts\" path=\"toh-pt2/src/app/heroes/heroes.component.ts\">\nimport { <a href=\"api/core/Component\" class=\"code-anchor\">Component</a>, <a href=\"api/core/OnInit\" class=\"code-anchor\">OnInit</a> } from '@angular/core';\nimport { Hero } from '../hero';\nimport { HEROES } from '../mock-heroes';\n\n@<a href=\"api/core/Component\" class=\"code-anchor\">Component</a>({\n selector: 'app-heroes',\n templateUrl: './heroes.component.html',\n styleUrls: ['./heroes.component.css']\n})\n\nexport class HeroesComponent implements <a href=\"api/core/OnInit\" class=\"code-anchor\">OnInit</a> {\n\n heroes = HEROES;\n selectedHero?: Hero;\n\n constructor() { }\n\n ngOnInit() {\n }\n\n onSelect(hero: Hero): void {\n this.selectedHero = hero;\n }\n}\n\n\n</code-pane>\n\n <code-pane header=\"src/app/heroes/heroes.component.html\" path=\"toh-pt2/src/app/heroes/heroes.component.html\">\n<h2>My Heroes</h2>\n<ul class=\"heroes\">\n <li *<a href=\"api/common/NgForOf\" class=\"code-anchor\">ngFor</a>=\"let hero of heroes\"\n [class.selected]=\"hero === selectedHero\"\n (click)=\"onSelect(hero)\">\n <span class=\"badge\">{{hero.id}}</span> {{hero.name}}\n </li>\n</ul>\n\n<div *<a href=\"api/common/NgIf\" class=\"code-anchor\">ngIf</a>=\"selectedHero\">\n\n <h2>{{selectedHero.name | <a href=\"api/common/UpperCasePipe\" class=\"code-anchor\">uppercase</a>}} Details</h2>\n <div><span>id: </span>{{selectedHero.id}}</div>\n <div>\n <label for=\"hero-name\">Hero name: </label>\n <input id=\"hero-name\" [(<a href=\"api/forms/NgModel\" class=\"code-anchor\">ngModel</a>)]=\"selectedHero.name\" placeholder=\"name\">\n </div>\n\n</div>\n\n\n</code-pane>\n\n <code-pane header=\"src/app/heroes/heroes.component.css\" path=\"toh-pt2/src/app/heroes/heroes.component.css\">\n/* HeroesComponent's private CSS styles */\n.heroes {\n margin: 0 0 2em 0;\n list-style-type: none;\n padding: 0;\n width: 15em;\n}\n.heroes li {\n cursor: pointer;\n position: relative;\n left: 0;\n background-color: #EEE;\n margin: .5em;\n padding: .3em 0;\n height: 1.6em;\n border-radius: 4px;\n}\n.heroes li:hover {\n color: #2c3a41;\n background-color: #e6e6e6;\n left: .1em;\n}\n.heroes li.selected {\n background-color: black;\n color: white;\n}\n.heroes li.selected:hover {\n background-color: #505050;\n color: white;\n}\n.heroes li.selected:active {\n background-color: black;\n color: white;\n}\n.heroes .badge {\n display: inline-block;\n font-size: small;\n color: white;\n padding: 0.8em 0.7em 0 0.7em;\n background-color:#405061;\n line-height: 1em;\n position: relative;\n left: -1px;\n top: -4px;\n height: 1.8em;\n margin-right: .8em;\n border-radius: 4px 0 0 4px;\n}\n\ninput {\n padding: .5rem;\n}\n\n\n</code-pane>\n\n</code-tabs>\n<h2 id=\"summary\">Summary<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt2#summary\"><i class=\"material-icons\">link</i></a></h2>\n<ul>\n<li>The Tour of Heroes application displays a list of heroes with a detail view.</li>\n<li>The user can select a hero and see that hero's details.</li>\n<li>You used <code>*<a href=\"api/common/NgForOf\" class=\"code-anchor\">ngFor</a></code> to display a list.</li>\n<li>You used <code>*<a href=\"api/common/NgIf\" class=\"code-anchor\">ngIf</a></code> to conditionally include or exclude a block of HTML.</li>\n<li>You can toggle a CSS style class with a <code>class</code> binding.</li>\n</ul>\n\n \n</div>\n\n<!-- links to this doc:\n - guide/example-apps-list\n - tutorial/toh-pt3\n-->\n<!-- links from this doc:\n - api/common/NgForOf\n - api/common/NgIf\n - api/common/UpperCasePipe\n - api/core/Component\n - api/core/Component#styleUrls\n - api/core/Component#styles\n - api/core/OnInit\n - api/forms/NgModel\n - guide/attribute-binding#class-binding\n - guide/built-in-directives#ngFor\n - guide/event-binding\n - tutorial/toh-pt0#app-wide-styles\n - tutorial/toh-pt2#add-a-click-event-binding\n - tutorial/toh-pt2#add-a-details-section\n - tutorial/toh-pt2#add-the-click-event-handler\n - tutorial/toh-pt2#create-mock-heroes\n - tutorial/toh-pt2#display-a-selection-list\n - tutorial/toh-pt2#displaying-heroes\n - tutorial/toh-pt2#final-code-review\n - tutorial/toh-pt2#list-heroes-with-ngfor\n - tutorial/toh-pt2#style-the-heroes\n - tutorial/toh-pt2#style-the-selected-hero\n - tutorial/toh-pt2#styles\n - tutorial/toh-pt2#summary\n - tutorial/toh-pt2#the-fix---hide-empty-details-with-ngif\n - tutorial/toh-pt2#viewing-details\n - tutorial/toh-pt2#what-happened\n - tutorial/toh-pt2#why-it-works\n - https://github.com/angular/angular/edit/master/aio/content/tutorial/toh-pt2.md?message=docs%3A%20describe%20your%20change...\n-->"
|
|
} |