반응형

영웅 목록 표시

이페이지에서는 영웅 여행 앱이 영웅 목록을 표시하는것을 확장할수 있고,  사용자가 선택한 그영웅의 정보를 표시하는것을 허용한다.


모형 영웅 생성

우리는 표시하고 싶은 영웅들이 필요합니다.

결국 원격 데이터 서버에서 영웅정보를 얻을수 있습니다. 현재로는(당분간은) 어떤 모형 영웅들을 만들고 서버에서 온것처럼 할것이다.

mock-heroes.ts 파일을 src/app/ 폴더속에 만들자. HEROES 상수를 열개의 영웅과 export 한 배열로 정의한다. 

src/app/mock-heroes.ts

import { Hero } from './hero';

export 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' }

];


영웅을 표시

이제  HeroesComponent에 영웅목록에서 제일 위에 것에 표시 할때이다.

HeroesComponent 클래스 파일을 열고 mock HEROES을 import 한다.


src/app/heroes/heroes.component.ts (import HEROES)

import { HEROES } from '../mock-heroes';


영웅들의 속성을 영웅들이 드러나게 빌딩된 것을 클래스에 추가한다.

heroes = HEROES;


*ngFor로 영웅 목록

HeroesComponent 템플릿 파일을 열고 다음과 같이 수정한다.


<h2>를 위에 추가합니다.

그 밑에 HTML 순서가 없는 목록인 <ul> 코드를 추가.

<li>를  영웅의 표시되는 속성을 <ul> 사이에 삽입한다. 

스타일을 위해 CSS 클래스를 간간히 썩어라.

아래처럼 만든다.

heroes.component.html (heroes template)

<h2>My Heroes</h2>

<ul class="heroes">

  <li>

    <span class="badge">{{hero.id}}</span> {{hero.name}}

  </li>

</ul>


지금 <li>를 아래 처럼 바꾼다.

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

*ngFor  앵귤라의 반복 디렉티브다. 리스트의 요소를 반복해서 합니다.

  • <li>은 주 요소입니다.
  • heroes 은 HeroesComponent 클래스에서 정의된 목록이다.
  • 영웅은 그 각의 목록의 반복을 통한 각각의 현재 영웅 객체를 잡고 있다.


ngFor 앞에 *은 잊지말고 쓰자. 문법이니까..

브라우저가 재생된 후에 영웅들의 목록이 나타난다.


영웅의 스타일

영웅 목록은 매력적이고 이어야하고, 사용자가 위에 맴돌때와 목록에서 선택된 영웅이 반응형이어야 한다.

첫번재 튜토리얼에서  styles.css으로 온 애플리케이션을 위한 기본 스타일을 해놓았다. 그 스타일쉬트는 영웅들 목록을 위한 스타일을 포함하지 않는다.

스타일들을 styles.css에  추가할수 있고 스타일시트가 커지지 않케 유지할수 있다.

 HTML, CSS  한곳에서 유지 하는것을 선호 할수 도 있습니다.

이방법을 사용하면 재상용이 가능이 쉽다.

개인 스타일  @Component.styles에 인라인 혹은 @Component.styleUrls 배열로 스타일시트 파일로 정의 합니다.

HeroesComponent가 CLI 자동생성 될때, HeroesComponent를 위한 heroes.component.css 스타일 시트가 생성되고 @Component.styleUrls 다음처럼 가리킨다.


src/app/heroes/heroes.component.ts (@Component)

@Component({

  selector: 'app-heroes',

  templateUrl: './heroes.component.html',

  styleUrls: ['./heroes.component.css']

})

heroes.component.css 파일을 열고  HeroesComponent 만을 위한 CSS 스타일에 붙혀넣는다.  마지막코드 리뷰의 마지막 안내에 찾을수 있다.

스타일과 스타일시트는 @Component 메타데이터는 특정 컴포넌트에 속한 영역에 선언한다.  heroes.component.css 스타일은 HeroesComponent  에만 적용된다..


Master/Detail

주 목록에서 영웅을 사용자가 클릭 할때, 콤포넌트는 선택된 영웅의 상세를 페이지 아래에 표시 해야한다.

이번 섹션에서는 영웅 항목 클릭 이벤트에 와 영웅 상세정보를 업데이트하는 것에 대해 들을수 있다.


클릭 이벤트 바인딩을 추가한다.

아래처럼 <li> 클릭 이벤트 바인딩을 추가한다.

heroes.component.html (template excerpt)

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


앵귤라의 이벤트 바딩인 문법 예제입니다.

괄호 주위 click을 <li>요소들이 클릭이벤트를  엥귤라에게 알린다. 사용자가 <li>테그를 클릭 할때, 앵귤라는  onSelect(hero) 표현이 실행된다.

onSelect()는 HeroesComponent에 작성한 대한 메소드이다. 앵귤라는 <li>클릭시에 표시된 영웅 객체를 부른다, 그 같은 영웅은 *ngFor 표현식으로 이전에 정의된다.


클릭 이벤트 핸들러 추가

컴포넌트의 영웅 속성 selectedHero 으로 이름을 바꾼다.  그러나 이것을 배정 하지 않는다. 저것은 애플리케이션이 시작될때 선택되지 않은 영웅이다.

다음으로 onSelect() 메소드를 추가, 어떤 컴포넌으의 selectedHero 템플릿으로부터 그 선택된 영웅을 배정한다.


src/app/heroes/heroes.component.ts (onSelect)

selectedHero: Hero;

onSelect(hero: Hero): void {

  this.selectedHero = hero;

}


상세 템플릿을 업데이트한다.


The template still refers to the component's old hero property which no longer exists. Rename hero to selectedHero.

템플릿은 컴포넌트의 이미 존재하지 않은 오래된 속성을 여전히 참조하고 있다. hero 에서  selectedHero으로 이름을 바꾼다.


heroes.component.html (selected hero details)

<h2>{{ selectedHero.name | uppercase }} Details</h2>

<div><span>id: </span>{{selectedHero.id}}</div>

<div>

  <label>name:

    <input [(ngModel)]="selectedHero.name" placeholder="name">

  </label>

</div>


빈 상세정보는 *ngIf 로 숨겨진다.

브라우저 재생 된후 애플리케이션은 깨진다.

브라우저 개발툴을 열고 에러메세지가 다음과 같은 콜솔을 본다.

HeroesComponent.html:3 ERROR TypeError: Cannot read property 'name' of undefined

이제 목록 항목중 하나를 클릭합니다. 앱은 다시 동작되는것처럼 보인다.  영웅이 목록과 선택된 영우에 대한 상세 나타난다 페이지 하단에 보이기 사작한다.


무슨일일까?

앱이시작 될때, selectedHero은 고의로 정의되지 않았다.

선택된지 않은 영웅이기 때문에   {{selectedHero.name}}의 표현은 바인딩에 실패한다.


해결책

컴포넌트는 selectedHero가 존재하면 선택된 영웅 상세를 단지 보여주기 해야한다 

<div> 안에 영웅 상세 HTML  싸여져 있다. 앵귤라의 *ngIf 디렉티브를  <div> 에 추가하고 이것을 selectedHero에 설정한다.

ngIf 앞에 *쓰는것을  잊지말아. 중요한 문법중의 하나다.


src/app/heroes/heroes.component.html (*ngIf)

<div *ngIf="selectedHero">

  <h2>{{ selectedHero.name | uppercase }} Details</h2>

  <div><span>id: </span>{{selectedHero.id}}</div>

  <div>

    <label>name:

      <input [(ngModel)]="selectedHero.name" placeholder="name">

    </label>

  </div>

</div>


브라우저가 재생된 후에 이름 목록이 다시 나타난다.   상세 영역은 비어있다. 영웅을 클릭하면 상세정가 나타난다.


어떻게 돌아가나.

selectedHero가 정의되지 않앗을때, ngIf는 DOM에서 영웅 상세정보를 제거한다. 그것들은 selectedHero 바인딩이 아닌 것에 대해서 걱정한다.

사용자가 영웅을 골랐을때, selectedHero 값을 가지고 ngIf는 DOM안에 영웅 상세를 넣는다.


선택된 영웅 스타일

모든 <li>요소가 보이는 목록에서  선택된 영웅을 확인하기가 어렵다.

사용자가 "Magneta"를 클릭 했을때, 영웅은   독특하게 민들 어야만 하나 미묘한 백그라운드 칼라는 다음과 같다.

선택된 영웅 색은 먼저 추가된 스타일 에 .selected css 클래스에 일한다. 사용자가 이것을 클릭 했을대 .selected 클래스를 <li>에 반드시 적용해야한다.

앵귤라 클래스 바인딩은  조건부 CSS 클래스를 추가 / 제거를 쉽게 만든다. [class.some-css-class]="some-condition"를 원하는 스나일 요소에 반드시 추가 하기만 하면 된다.

Add the following [class.selected] binding to the <li> in the HeroesComponent template:

다음의 [class.selected]를  HeroesComponent 템플릿의 <li>으로 바인딩을 추가한다.


heroes.component.html (toggle the 'selected' CSS class)

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


현재 영웅 줄은 selectedHero와 같다. 앵귤라 선택된 CSS 클래스를 추가한다. 두 영웅은 다를 때, 앵귤라는 그 클래스를 제거한다.

완성된 <li> 는 이렇게 보인다.


heroes.component.html (list item hero)

<li *ngFor="let hero of heroes"

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

  (click)="onSelect(hero)">

  <span class="badge">{{hero.id}}</span> {{hero.name}}

</li>



최종 코드 리뷰

src/app/heroes/heroes.component.ts

import { Component, OnInit } from '@angular/core';

import { Hero } from '../hero';

import { HEROES } from '../mock-heroes';

 

@Component({

  selector: 'app-heroes',

  templateUrl: './heroes.component.html',

  styleUrls: ['./heroes.component.css']

})

export class HeroesComponent implements OnInit {

 

  heroes = HEROES;

 

  selectedHero: Hero;

 

 

  constructor() { }

 

  ngOnInit() {

  }

 

  onSelect(hero: Hero): void {

    this.selectedHero = hero;

  }

}

src/app/heroes/heroes.component.html

<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 | uppercase }} Details</h2>

  <div><span>id: </span>{{selectedHero.id}}</div>

  <div>

    <label>name:

      <input [(ngModel)]="selectedHero.name" placeholder="name">

    </label>

  </div>

 </div>

src/app/heroes/heroes.component.css

/* HeroesComponent's private CSS 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;

}

요약

영웅의 여행 앱은 영웅 목록을 주 / 상세 뷰에서 표시된다.

사용자는 영웅을 선택할수 있고 영웅의 상세정보를 볼수 있다.

목록을 표시할때는 *ngFor 이용한다.

*ngIf 는 HTML 블럭을 선택적으로 포함 혹은 제외  한다.

클래스 바인딩을 통해 CSS스타일을 토글 할수 있다.


반응형

+ Recent posts