MASTER/DETAIL

Build a master/detail page with a list of heroes.

In this page, you'll expand the Tour of Heroes app to display a list of heroes, and allow users to select a hero and display the hero's details.


영웅들의 리스트와 함께 마스터/디테일 페이지를 만드세요.

이번 페이지에서 여러분은 영웅들의 리스트를 보여주고 사용자들이 영웅을 선택하고 영웅의 디테일을 볼수 있게 앱을 확장할것입니다.


Where you left off

당신이 도착한곳


Before you continue with this page of the Tour of Heroes, verify that you have the following structure after The Hero Editor page. If your structure doesn't match, go back to that page to figure out what you missed.


여러분이 Tour of Heroes의 이 페이지를 시작하기 전에 다음 구조를 가지고 있는지 확인 하세요. 만약 당신의 구조가 다음 구조와 맞지 않다면 뒤로 돌아가서 무엇을 놓쳤는지 살펴보세요.


angular-tour-of-heroes

src

app

app.component.ts

app.module.ts

main.ts

index.html

styles.css

systemjs.config.js

tsconfig.json

node_modules ...

package.json


Keep the app transpiling and running

앱을 계속 transcompile하고 실행된 채로 두세요.


Enter the following command in the terminal window:

npm start

This command runs the TypeScript compiler in "watch mode", recompiling automatically when the code changes. The command simultaneously launches the app in a browser and refreshes the browser when the code changes.


터미널에 다음 명령어를 실행 합시다:

npm start

이 명령어는 Typescript compiler를 “watch mode”에 둡니다. 코드가 바뀌면 자동으로 재컴파일됩니다.


You can keep building the Tour of Heroes without pausing to recompile or refresh the browser.


여러분은 Tour of Heroes를 정지시켜 재컴파일시키거나 브라우저를 새로고침하지 않고 계속해서 만들 수 있게 되었습니다.


Displaying heroes

영웅 보여주기


To display a list of heroes, you'll add heroes to the view's template.


영웅의 리스트를 보여주기 위해서, 여러분은 영웅들을 뷰의 템플릿에 추가할 것입니다.


Create heroes

영웅 만드세요


Create an array of ten heroes.


영웅을 배열로 10명 만드세요.


srcappapp.component.ts (hero array)

const 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, the class defined in the previous page. Eventually this app will fetch the list of heroes from a web service, but for now you can display mock heroes.


HEROES배열은 Hero클래스 타입이 이전 페이지에 정의된 것 을 말한다. 결국 이 앱은 웹서비스로부터 영웅의 리스트를 가져올 것 이지만, 지금 여러분은 가짜의 영웅들을 보여줄 수 있다.


Expose heroes

영웅들을 노출시켜라


Create a public property in AppComponent that exposes the heroes for binding.


AppComponent에 영웅들을 바인딩하기 위해 노출하는 public 속성을 만들어라.


app.component.ts (hero array property)

heroes = HEROES;

The heroes type isn't defined because TypeScript infers it from the HEROES array.


heroes타입은 타입스크립트가 HEROES배열로 부터 추론하기 때문에 정의되지 않았다.


The hero data is separated from the class implementation because ultimately the hero names will come from a data service.


영웅 데이터는 클래스 이식 과정에서 분리되었다. 왜냐하면 결국 영웅의 이름이 데이터 서비스로부터 올것이기 때문이다.


Display hero names in a template

템플릿에 영웅이름 보여주기


To display the hero names in an unordered list, insert the following chunk of HTML below the title and above the hero details.


순서가 없는 이스트에서 영웅의 이름을 보여주기 위해서는, 다음 HTML을 제목 아래, 영웅의 세부사항 위에 삽입하세요.


app.component.ts (heroes template)

<h2>My Heroes</h2>
<ul class="heroes">
  <li>
    <!-- each hero goes here -->
  </li>
</ul>

Now you can fill the template with hero names.


이제 여러분은 영웅의 이름으로 템플릿을 채워 넣을 수 있다.

List heroes with ngFor

영웅들을 ngFor로 나열하기


The goal is to bind the array of heroes in the component to the template, iterate over them, and display them individually.


목표는 component에 있는 영웅의 배열을 템플릿으로 바인드하고, 반복하고, 그들을 각각 보여주는 것이다.


Modify the <li>tag by adding the built-in directive *ngFor.


<li> tag를 *ngFor 지시어를 추가하여 수정하여라. 


app.component.ts (ngFor)

`<li *ngFor="let hero of heroes">`


The (*) prefix to ngFor is a critical part of this syntax. It indicates that the <li>element and its children constitute a master template.


ngFor 앞에 붙는 (*)접두어는 이 문법의 핵심부분이다. 이는 <li>요소와 그 자식들이 마스터 템플릿을 구성한다는것을 보여준다.


The ngFor directive iterates over the component's heroes array and renders an instance of this template for each hero in that array.


ngFor 지시어는 component의 영웅 배열을 반복하고 이 템플릿의 인스턴스를 배열의 각 영웅마다 변환한다.


The let hero part of the expression identifies hero as the template input variable, which holds the current hero item for each iteration. You can reference this variable within the template to access the current hero's properties.


표현식의 let hero 부분은 hero를 템플릿 input변수로 나타낸다. 결국 템플릿 input변수 hero는 각 반복마다 현재 hero아이템을 가지게 된다. 여러분은 현재 영웅의 속성을 알기 위해 이 변수를 템플릿과 함께 참조 할 수 있다.


Read more about ngFor and template input variables in the Showing an array property with *ngFor section of the Displaying Data page and the ngFor section of the Template Syntax page. Within the <li>tags, add content that uses the hero template variable to display the hero's properties. 


ngFor과 템플릿 input변수에 대해 ‘데이터 보여주기’페이지의 ’*ngFor로 배열의 속성보여주기’섹션과 ‘템플릿 문법’페이지의 ‘ngFor’ 섹션에서 더 읽어보아라. app.component.ts (ngFor template)

<li *ngFor="let hero of heroes">
  <span class="badge">{{hero.id}}</span> {{hero.name}}
</li>

When the browser refreshes, a list of heroes appears.


브라우저가 새로고침되면 영웅의 리스트가 나타난다.


Style the heroes


영웅에 스타일 더하기


Users should get a visual cue of which hero they are hovering over and which hero is selected.


사용자들은 그들이 어떤 영웅위에 hover하고 있는지 그리고 어떤영웅이 선택되었는지 시각적인 효과를 필요로한다.


To add styles to your component, set the styles property on the @Component decorator to the following CSS classes:


여러분의 component에 스타일을 더하기 위해 @Component 데코레이터에 스타일 속성을 다음 CSS 클래스들로 놓아라:


srcappapp.component.ts (styles)

styles: [`
  .selected {
    background-color: #CFD8DC !important;
    color: white;
  }
  .heroes {
    margin: 0 0 2em 0;
    list-style-type: none;
    padding: 0;
    width: 15em;
  }
  .heroes li {
    cursor: pointer;
    position: relative;
    left: 0;
    background-color: #EEE;
    margin: .5em;
    padding: .3em 0;
    height: 1.6em;
    border-radius: 4px;
  }
  .heroes li.selected:hover {
    background-color: #BBD8DC !important;
    color: white;
  }
  .heroes li:hover {
    color: #607D8B;
    background-color: #DDD;
    left: .1em;
  }
  .heroes .text {
    position: relative;
    top: -3px;
  }
  .heroes .badge {
    display: inline-block;
    font-size: small;
    color: white;
    padding: 0.8em 0.7em 0 0.7em;
    background-color: #607D8B;
    line-height: 1em;
    position: relative;
    left: -1px;
    top: -4px;
    height: 1.8em;
    margin-right: .8em;
    border-radius: 4px 0 0 4px;
  }
`]

Remember to use the backtick notation for multi-line strings.


여러줄의 문자열을 다룰때 backtick 표기법을 쓰는것을 잊지마라.


Adding these styles makes the file much longer. In a later page you'll move the styles to a separate file.


이런 스타일을 추가하는것은 파일을 더 길게 만든다. 나중에 여러분은 스타일들을 분리된 파일로 옮길것이다.


When you assign styles to a component, they are scoped to that specific component. These styles apply only to the AppComponent and don't affect the outer HTML.


component에 스타일을 지정했다면 그것들은 특정 component에 한정된다. 이러한 스타일은 AppComponent에만 적용되고 밖의 HTML에는 영향을 미치지 못한다.


The template for displaying heroes should look like this:


영웅들을 보여주기 위한 템플릿은 이래야 한다:


srcappapp.component.ts (styled heroes)

<h2>My Heroes</h2>
<ul class="heroes">
  <li *ngFor="let hero of heroes">
    <span class="badge">{{hero.id}}</span> {{hero.name}}
  </li>
</ul>

Selecting a hero

영웅 고르기


The app now displays a list of heroes as well as a single hero in the details view. But the list and the details view are not connected. When users select a hero from the list, the selected hero should appear in the details view. This UI pattern is known as "master/detail." In this case, the master is the heroes list and the detail is the selected hero.


이제 앱은 영웅들의 목록 뿐만아니라 한 영웅의 세부사항 뷰까지도 보여준다. 하지만 리스트와 세부사항뷰는 관계가 없다. 사용자들이 리스트에서 영웅을 골랐을때, 선택된 영웅은 세부사항뷰에 나타나야 한다. 이 UI패턴은 “마스터/디테일”로 알려졌다. 이 경우에 마스터는 영웅의 리스트이고, 디테일은 선택된 영웅이다.


Next you'll connect the master to the detail through a selectedHero component property, which is bound to a click event.


이제 여러분은 selectedHero component속성을 통해 마스터를 디테일에 연결시킬 것이다. 이는 클릭 이벤트를 하게 만든다.


Add a click event

클릭 이벤트 추가하기


Add a click event binding to the <li>like this:

<li>에 다음과 같이 클릭이벤트가 바인드 되도록 다음과 같이 클릭이벤트를 추가한다:


app.component.ts (template excerpt)

<li *ngFor="let hero of heroes" (click)="onSelect(hero)">
  ...
</li>

The parentheses identify the <li> element's click event as the target. The onSelect(hero) expression calls the AppComponent method, onSelect(), passing the template input variable hero, as an argument. That's the same hero variable you defined previously in the ngFor directive.


구문은 <li>element의 클릭 이벤트를 타겟으로 나타낸다. onSelecr(hero) 표현은 AppComponent 메소드를 부르고, onSelect()는 템플릿 input변수 hero를 아규멘트로 전달한다. 이것은 여러분이 전에 ngFor 지시어에서 정의한 같은 hero변수이다.


Learn more about event binding at the User Input page and the Event binding section of the Template Syntax page.


사용자 input페이지와 템플릿 문법페이지의 이벤트 바인딩 섹션에서 이벤트 바인딩에 대해 더 아라보아라.


Add a click handler to expose the selected hero


선택된 영웅을 노출시키기 위해 믈릭 핸들러를 추가하라.


You no longer need the hero property because you're no longer displaying a single hero; you're displaying a list of heroes. But the user will be able to select one of the heroes by clicking on it. So replace the hero property with this simple selectedHero property:


여러분은 한 영웅만을 보여주지 않기에 hero속성이 더이상 필요하지 않다. 여러분은 영웅리스트를 보여준다. 따라서 사용자는 사용자는 영웅을 클릭함으로서 한 영웅을 선택 할 수 있다. 이제 영웅 속성을 이 간단한 선택된 영웅 속성으로 바꾸자:


srcappapp.component.ts (selectedHero)

selectedHero: Hero;


The hero names should all be unselected before the user picks a hero, so you won't initialize the selectedHero as you did with hero.


영웅의 이름들은 사용자가 영웅을 고르기 전까지 모두 선택되지 않아야 한다. 그래서 여러분은 여러분이 영웅에게 했던것 처럼 선택된 영웅을 초기화하지 말아야 한다.


Add an onSelect method that sets the selectedHero property to the hero that the user clicks.


유저가 선택한 영웅을 선택된 영웅 속성으로 하는 onSelect 메소드를 추가하라.


srcappapp.component.ts (onSelect)

onSelect(hero: Hero): void {
  this.selectedHero = hero;
}

The template still refers to the old hero property. Bind to the new selectedHero property instead as follows:


위 템플릿은 아직도 옛 영웅 속성을 가르킨다. 다음과 같이 새로운 selectedHero 속성에 대신 바인드해라:


app.component.ts (template excerpt)

<h2>{{selectedHero.name}} details!</h2>
<div><label>id: </label>{{selectedHero.id}}</div>
<div>
    <label>name: </label>
    <input [(ngModel)]="selectedHero.name" placeholder="name"/>
</div>

Hide the empty detail with ngIf


비어있는 세부사항을 nglf로 숨기기


When the app loads, the selectedHero is undefined and won't be defined until you click a hero's name. Angular can't display properties of the undefined selectedHero and throws the following error, visible in the browser's console:


앱이 로딩될 때, 선택된 영웅은 정의되지 않아있고 여러분이 영웅의 이름을 선택하기 전까지 정의되지 말아야 한다. Angular는 정의되지 않은 선택된 영웅을 보여줄 수 없고 브라우저 콘솔에 다음과 같은 에러를 낸다:


EXCEPTION: TypeError: Cannot read property 'name' of undefined in [null]

Although selectedHero.name is displayed in the template, you must keep the hero detail out of the DOM until there is a selected hero.


Wrap the HTML hero detail content of the template with a

. Then add the ngIf built-in directive and set it to the selectedHero property of the component.


템플릿의 HTML로된 영웅의 세부사항 콘텐츠를

로 감싸라. 그리고 nglf 내장 지시어를 추가하고 그것을 component의 선택된 영웅 속성에 놓아라.


srcappapp.component.ts (ngIf)

<div *ngIf="selectedHero">
  <h2>{{selectedHero.name}} details!</h2>
  <div><label>id: </label>{{selectedHero.id}}</div>
  <div>
    <label>name: </label>
    <input [(ngModel)]="selectedHero.name" placeholder="name"/>
  </div>
</div>

Don't forget the asterisk (*) in front of ngIf.

The app no longer fails and the list of names displays again in the browser.


nglf앞에 에스테릭을 잊지 마라.

앱은 더이상 실패하지 않고 브라우저에는 이름의 목록이 다시 나타난다.


When there is no selectedHero, the ngIf directive removes the hero detail HTML from the DOM. There are no hero detail elements or bindings to worry about.


선택된 영웅이 없을때, nglf지시어는 영웅의 세부사항 HTML을 DOM으로부터 제거한다. 영웅의 세부사항 요소나 걱정할 바인딩은 없다.


When the user picks a hero, selectedHero becomes defined and ngIf puts the hero detail content into the DOM and evaluates the nested bindings.


사용자가 영웅을 선택할때, 선택된 영웅은 정의되고 nglf는 영웅의 세부 사항 콘텐츠를 DOM에 넣고 괄호로 감싼 바인딩을 확인한다.


Read more about ngIf and ngFor in the Structural Directives page and the Built-in directives section of the Template Syntax page.


구조적 지시어 페이지와 템플릿 문법페이지의 내장 지시어 섹션에서 nglf와 ngFor에 대해 더 읽어보라.


Style the selected hero


선택된 영웅의 스타일 적용


While the selected hero details appear below the list, it's difficult to identify the selected hero within the list itself.


선택된 영웅의 세부사항이 리스트. 아래 나타날때, 리스트 자체와 함께 선택된 영웅을 확인하는 것은 어렵다.


In the styles metadata that you added above, there is a custom CSS class named selected. To make the selected hero more visible, you'll apply this selected class to the <li> when the user clicks on a hero name. For example, when the user clicks "Magneta", it should render with a distinctive but subtle background color like this:


여러분이 위에서 더한 스타일 메타데이터에는 selected라는 커스텀 CSS클래스가 있다. 선택된 영웅을 더 눈에 띄이게 하도록 여러분은 사용자가 영웅의 이름을 클릭할때 <li>에 이 selected클래스를 적용할 것이다. 예를 들어, 사용자가 “Magneta”를 클릭할 때, 이것은 구별되지만 미묘한 배경색깔을 이렇게 렌더링한다:


In the template, add the following [class.selected] binding to the <li>:


템플릿에서, 다음 [class.selected] 바인딩을 <li>에 추가하라.


app.component.ts (setting the CSS class)

[class.selected]="hero === selectedHero"


When the expression (hero === selectedHero) is true, Angular adds the selected CSS class. When the expression is false, Angular removes the selected class.


표현식 (hero === selectedHero)이 사실 이라면 Angular는 selected CSS 믈래스를 추가한다. 표현식이 거짓이면 Angular는 selected클래스를 제거한다.


Read more about the [class] binding in the Template Syntax guide.

The final version of the <li> looks like this:


[class]바인딩에 관해 템플릿 문법가이드에서 더 읽어보라.

마지막 <li>의 버전은 다음과 같다:


app.component.ts (styling each hero)

<li *ngFor="let hero of heroes"
  [class.selected]="hero === selectedHero"
  (click)="onSelect(hero)">
  <span class="badge">{{hero.id}}</span> {{hero.name}}
</li>

After clicking "Magneta", the list should look like this:


“Magneta”를 클릭하면 리스트는 이렇게 나타난다:


Here's the complete app.component.ts as of now:


여기에 완벽한 app.component.ts가 있다:


srcappapp.component.ts

import { Component } from '@angular/core';
export class Hero {
  id: number;
  name: string;
}
const 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' }
];
@Component({
  selector: 'my-app',
  template: `
    <h1>{{title}}</h1>
    <h2>My Heroes</h2>
    <ul class="heroes">
      <li *ngFor="let hero of heroes"
        [class.selected]="hero === selectedHero"
        (click)="onSelect(hero)">
        <span class="badge">{{hero.id}}</span> {{hero.name}}
      </li>
    </ul>
    <div *ngIf="selectedHero">
      <h2>{{selectedHero.name}} details!</h2>
      <div><label>id: </label>{{selectedHero.id}}</div>
      <div>
        <label>name: </label>
        <input [(ngModel)]="selectedHero.name" placeholder="name"/>
      </div>
    </div>
  `,
  styles: [`
    .selected {
      background-color: #CFD8DC !important;
      color: white;
    }
    .heroes {
      margin: 0 0 2em 0;
      list-style-type: none;
      padding: 0;
      width: 15em;
    }
    .heroes li {
      cursor: pointer;
      position: relative;
      left: 0;
      background-color: #EEE;
      margin: .5em;
      padding: .3em 0;
      height: 1.6em;
      border-radius: 4px;
    }
    .heroes li.selected:hover {
      background-color: #BBD8DC !important;
      color: white;
    }
    .heroes li:hover {
      color: #607D8B;
      background-color: #DDD;
      left: .1em;
    }
    .heroes .text {
      position: relative;
      top: -3px;
    }
    .heroes .badge {
      display: inline-block;
      font-size: small;
      color: white;
      padding: 0.8em 0.7em 0 0.7em;
      background-color: #607D8B;
      line-height: 1em;
      position: relative;
      left: -1px;
      top: -4px;
      height: 1.8em;
      margin-right: .8em;
      border-radius: 4px 0 0 4px;
    }
  `]
})
export class AppComponent {
  title = 'Tour of Heroes';
  heroes = HEROES;
  selectedHero: Hero;
  onSelect(hero: Hero): void {
    this.selectedHero = hero;
  }
}

The road you've travelled

여러분이 걸어온 길


Here's what you achieved in this page:


여기 여러분이 이 페이지에서 이룬것들이 있다:


The Tour of Heroes app displays a list of selectable heroes.

You added the ability to select a hero and show the hero's details.

You learned how to use the built-in directives ngIf and ngFor in a component's template.

Your app should look like this live example / downloadable example.


The Tour of Heroes 앱은 선택가능한 영웅들의 목록을 보여준다.

여러분은 영웅을 선택할 수 있게 했고, 영웅들의 세부사항을 보여줄 수 있게 ㅁ추가했다.

여러분은 component의 템플릿에서 어떻게 내부 지시어 nglf와 ngFor을 사용하는지 배웠다.

여러분의 앱은 다음 예와 같아야 한다.


The road ahead

앞으로의 길


You've expanded the Tour of Heroes app, but it's far from complete. You can't put the entire app into a single component. In the next page, you'll split the app into sub-components and make them work together.


여러분은 Tour of Heroes앱을 확장했으나 완벽하기엔 이르다. 여러분은 모든 앱을 한 컴포넌트에 넣었다. 다음페이지에서 여러분은 앱을 sub-component에 넣고 이들이 함께 동작할 수 있도록 할 것이다.


'Angular' 카테고리의 다른 글

(번역)Angular tutorials - The Tour of Heroes - THE HERO EDITOR  (0) 2017.04.01
(번역)Angular - User Input  (0) 2017.03.29

+ Recent posts