-
2-9 영웅 내역으로 조회앵귤러/01 퀵스타트 & 튜토리얼 2017. 8. 6. 10:21
영웅 내역으로 조회
Parameterized route(파라미터를 이용한 경로)를 이용하여 경로 설정에 영웅편집기에 대한 경로를 추가한다.
src/app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroesComponent } from './heroes.component';
import { HeroService } from './hero.service';
import { DashboardComponent } from './dashboard.component';
@NgModule({
imports: [
BrowserModule,
FormsModule,
RouterModule.forRoot([
{path:'heroes', component:HeroesComponent},
{path: 'dashboard', component: DashboardComponent},
{path: '', redirectTo: '/dashboard', pathMatch: 'full'},
{path: 'detail/:id', component:HeroDetailComponent}
])
],
declarations: [
AppComponent,
HeroDetailComponent,
HeroesComponent,
DashboardComponent
],
providers: [
HeroService
],
bootstrap: [ AppComponent ]
})
export class AppModule {
}
Path에 콜론(:)표시를 하여 :id라고 표기된 부분에 영웅의 ID를 지정하게되고, HerodetailComponent를 호출하면서 URL로 영웅의 ID가 나타난다.
URL은 /detail/11 와 같이 표시된다.
이제 영웅편집기 HerodetailComponent를 다시 수정해 보자.
src/app/hero-detail.component.ts
import { Component, Input, OnInit } from '@angular/core';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { Location } from '@angular/common';
import 'rxjs/add/operator/switchMap';
import { Hero } from './hero';
import { HeroService } from './hero.service';
@Component({
selector: 'hero-detail',
template: `
<div *ngIf="hero">
<h2>{{hero.name}} details!</h2>
<div>
<label>id: </label>{{hero.id}}
</div>
<div>
<label>name: </label>
<input [(ngModel)]="hero.name" placeholder="name"/>
</div>
</div>
<button (click)="goBack()">뒤로가기</button>
`
})
export class HeroDetailComponent implements OnInit {
hero: Hero;
constructor(
private heroService:HeroService,
private route:ActivatedRoute,
private location:Location
){}
ngOnInit(): void{
this.route.paramMap
.switchMap((params: ParamMap) =>
this.heroService.getHero(+params.get('id')))
.subscribe(hero => this.hero = hero);
}
goBack():void{
this.location.back();
}
}
ActivatedRoute, HeroService, Location 서비스를 임포트한다.
// Keep the Input import for now, you'll remove it later:
import { Component, Input, OnInit } from '@angular/core';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { Location } from '@angular/common';
import { HeroService } from './hero.service';
임포트한 3개의 서비스를 생성시 주입한다.
constructor(
private heroService: HeroService,
private route: ActivatedRoute,
private location: Location
) {}
경로파라미터 타입이 Observable이므로 switchMap오퍼레이터를 사용하기 위해 switchMap을 임포트한다.
import 'rxjs/add/operator/switchMap';
OnInit인터페이스를 구현하기 위해 다음과 같이 수정한다.
export class HeroDetailComponent implements OnInit {
ngOnInit()메소드에서 경로파라미터를 추출한다.
ngOnInit(): void {
this.route.paramMap
.switchMap((params: ParamMap) => this.heroService.getHero(+params.get('id')))
.subscribe(hero => this.hero = hero);
}
switchMap 오퍼레이터는 ParamMap Observable에서 id를 찾아서 새로운 params Observable을 만든다.
+params.get(‘id’)에서 +연산자는 모든 경로 파라미터는 string타입이므로 number타입으로 변환하기 위해 사용한다.
let 변수선언자
자바스크립트에서 변수선언시에 주로 var를 사용한다. var변수는 전역변수로 사용되기 때문에 지역변수로 사용하기 위해 let을 사용한다.
브라우저에서 뒤로가기 버튼을 눌렀을 경우 반응하는지 알아보기 위해 템플릿에 뒤로가기 버튼을 추가한다.
<button (click)="goBack()">뒤로가기</button>
뒤로가기 버튼을 클릭할 때 처리될 메소드를 생성한다.
goBack(){
window.history.back();
}
다시 작성된 영웅편집기 컴포넌트에서 사용된 HeroService에는 getHero메소드가 없으므로 추가한다.
src/app/hero.service.ts
import { Injectable } from '@angular/core';
import { Hero } from './hero';
import { HEROES } from './mock-heroes';
@Injectable()
export class HeroService {
getHeroes(): Promise<Hero[]> {
return Promise.resolve(HEROES);
}
getHero(id:number):Promise<Hero>{
return this.getHeroes()
.then(heroes => heroes.find(hero => hero.id === id));
}
}
HerodetailComponent의 템플릿을 템플릿URL으로 변경해본다.
src/app/hero-detail.component.html
<div *ngIf="hero">
<h2>{{hero.name}} 내역!</h2>
<div><label>아이디 : </label>{{hero.id}}</div>
<div>
<label>이름 : </label>
<div><input [(ngModel)]="hero.name" placeholder="영웅이름"></div>
</div>
<button (click)="goBack()">뒤로</button>
</div>
src/app/hero-detail.component.ts
import { Component, Input, OnInit } from '@angular/core';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { Location } from '@angular/common';
import 'rxjs/add/operator/switchMap';
import { Hero } from './hero';
import { HeroService } from './hero.service';
@Component({
selector: 'hero-detail',
templateUrl: './hero-detail.component.html'
})
export class HeroDetailComponent implements OnInit {
hero: Hero;
constructor(
private heroService:HeroService,
private route:ActivatedRoute,
private location:Location
){}
ngOnInit(): void{
this.route.paramMap
.switchMap((params: ParamMap) =>
this.heroService.getHero(+params.get('id')))
.subscribe(hero => this.hero = hero);
}
goBack():void{
this.location.back();
}
}
대시보드에서 영웅을 클릭할 경우 세부내역으로 이동할 수 있도록 대시보드 컴포넌트를 수정해 보자.
src/app/dashboard.component.html
<h3>최고 영웅들</h3>
<div class="grid grid-pad">
<a *ngFor="let hero of heroes" [routerLink]="['/detail', hero.id]" class="col-1-4">
<div class="module hero">
<h4>{{hero.name}}</h4>
</div>
</a>
</div>
대시보드에서 선택된 영웅에 대해 영웅편집기로 이동하게 하고 해당 영웅의 id값을 url에 표시하면서 파라미터로 전송하도록 한다.
Router.navigate(link)
gotoDetail(hero:Hero){
let link = ['HeroDetail', {id:hero.id}];
this._router.navigate(link);
}
경로 링크 파라미터 배열을 생성하여 라우터의 navigate메소드에 전송한다.
link배열의 첫번째 값은 대상 경로의 이름이고, 두 번째 값은 경로 파라미터 오브젝트를 설정한다. 여기에서는 id와 id의 값은 hero.id값을 등록하였다.
라우팅 모률 리팩토링
경로지정을 위해 AppModule에 20라인 이상을 사용하므로 경로지정을 위한 모듈을 분리한다.
ng g module app-routing--flat
src/app/app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { DashboardComponent } from './dashboard.component';
import { HeroesComponent } from './heroes.component';
import { HeroDetailComponent } from './hero-detail.component';
const routes:Routes = [
{path: 'heroes', component: HeroesComponent},
{path: 'dashboard', component: DashboardComponent},
{path: '', redirectTo: '/dashboard', pathMatch: 'full'},
{path: 'detail/:id', component: HeroDetailComponent}
];
@NgModule({
imports: [
RouterModule.forRoot(routes)
],
exports: [ RouterModule ]
})
export class AppRoutingModule { }
AppModule에서 경로 설정부분을 제거하고 ApproutingModule을 임포트한다.
src/app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { DashboardComponent } from './dashboard.component';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroesComponent } from './heroes.component';
import { HeroService } from './hero.service';
import { AppRoutingModule } from './app-routing.module';
@NgModule({
imports: [
BrowserModule,
FormsModule,
AppRoutingModule
],
declarations: [
AppComponent,
DashboardComponent,
HeroDetailComponent,
HeroesComponent
],
providers: [ HeroService ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
여기까지 만들었으면 실행해 본다. 대시보드에서 영웅을 클릭하면 영웅편집기로 이동하면서 url이 변경된다. 영웅편집기에서 뒤로가기 버튼을 누르거나 브라우저의 뒤로가기를 누르면 이전화면으로 이동되면서 브라우저의 url도 같이 이전으로 변경된다.
영웅목록 컴포넌트도 HeroDetailComponent로 경로배정할 수 있도록 수정해 보자.
영웅목록 편집기의 템플릿의 내용이 많으므로 별도 html파일로 분리하자.
src/app/heroes.component.html
<h2>나의 영웅들</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}}은(는) 나의 영웅입니다.
</h2>
<button (click)="gotoDetail()">세부내역 보기</button>
</div>
{{|uppercase}} (UpperCasePipe)
표현식 내에 파이프연산자(|)를 사용하였다. 파이프는 문자열을 포맷에 넣기 위해 사용하거나, 금액을 표기하거나, 날짜를 표현하기 위해 사용한다.
{{selectedHero.name | uppercase}}은(는) 나의 영웅이에요.
기존에 <my-hero-detail>지시자가 있던 자리를 아래 내용으로 수정하였다.
<div *ngIf="selectedHero">
<h2>
{{selectedHero.name | uppercase}}은(는) 나의 영웅이에요.
</h2>
<button (click)="gotoDetail()">내역보기</button>
</div>
내역보기 버튼을 클릭하는 경우 세부내역으로 이동하는 gotoDetail()메소드를 구현하여야 한다.
스타일의 내용도 많으므로 별도 css파일로 분리하자.
src/app/heroes.component.css
.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 0em;
height: 1.6em;
border-radius: 4px;
}
.heroes li:hover{
color: #607D8B;
background-color: #EEE;
left: .1em;
}
.heroes .text{
position: relative;
top: -3px;
}
.heroes .badge{
display: inline-block;
font-size: small;
color: white;
padding: 0.8em 0.7em 0em 0.7em;
background-color: #607D8B;
line-height:1em;
position: relative;
left: -1px;
top: -4px;
margin-right: .8em;
border-radius: 4px 0px 0px 4px;
}
button:hover {
background-color: #CFD8DC;
}
이제 영웅목록 컴포넌트가 깔끔해 졌다.
src/app/heroes.component.t
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Hero } from './hero';
import { HeroService } from './hero.service';
@Component({
selector: 'my-heroes',
templateUrl: './heroes.component.html',
styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
title = 'Tour of Heroes';
heroes: Hero[];
selectedHero: Hero;
constructor(
private router: Router,
private heroService: HeroService
) { }
getHeroes(): void {
this.heroService.getHeroes()
.then(heroes => this.heroes = heroes);
}
ngOnInit(): void {
this.getHeroes();
}
onSelect(hero: Hero): void {
this.selectedHero = hero;
}
gotoDetail():void{
this.router.navigate(['/detail', this.selectedHero.id])
}
}
버튼 클릭시 처리하는 이밴트 메소드인 gotoDetail()를 구현한다
앵귤러 라이브러리에서 Router를 임포트한다.
생성자 메소드에서 Router를 주입한다.
Router의 navigate()메소드를 호출하여 경로를 지정한다.
gotoDetail(): void {
this.router.navigate(['/detail', this.selectedHero.id]);
}
@Component styleUrls 메타데이타
스타일을 별도의 파일로 분리하게 되면 style에서 styleUrls로 변경한다.
styleUrls:['app/heroes.component.css'],
'앵귤러 > 01 퀵스타트 & 튜토리얼' 카테고리의 다른 글
2-11 json-server 설치 (0) 2017.08.08 2-10 스타일 적용하기 (0) 2017.08.08 2-8 대시보드에 주요 영웅목록 만들기 (0) 2017.08.06 2-7 경로배정에 대시보드 추가 (0) 2017.08.06 2-6 경로배정(Routing) 추가 (0) 2017.08.06