반응형


HTTP

이 튜토리얼에서, 다음  앵귤라의 HttpClient의 도움을 받아서 데이터 영속성 기능을 추가할수 있다.

  • HeroService는 HTTP 요청을 통해 영웅 데이터를 가져온다.
  • 영웅을 추가, 수정, 삭제할수 있고 이러한 변경 사항을  HTTP를 통해 저장된다.
  • 영웅들의 이름으로 검색할수 있다.

이페이지를 다 마쳤을때 , 앱은 다음 실행 샘플 / 다운로드 예제 처럼 보일 것이다.


HTTP 서비스 활성

HTTP상에서 원격서버와 통신하기위한 HttpClient는 앵귤라의 매카니즘이다.

이 앱 모든곳에서 HttpClient를 사용 가능하게 하려면,

  • root AppModule을 열어,
  • HttpClientModule을 @angular/common/http에서 import 한다.
  • 이것을 @NgModule.imports 배열을 추가한다.


데이터 서버 모의실험

이 튜토리얼 샘플은 In-memory라는 웹 API 모듈을을 사용해서 원격 데이터 서버와 통신하는 것처럼 한다.

모듈을 설치 한 후 앱은 인 메모리 웹 API가 이러한 요청을 가로 채고이를 메모리 내 데이터 저장소에 적용하고 시뮬레이션 된 응답을 반환하지 않고 HttpClient에 요청을 보내고 응답을 수신합니다.

이 기능은 튜토리얼을 위한 휼륭한 편의기능이다.  HttpClient를 배우고 서버에 셋업 않아도 될 것이다.

서버의 웹 api가 불분명하거나 아직 구현되지 않았을때 이것은 아마도 초기단계 에서 당신 자신의 앱 환경에 편의기능일 것이다.

중요 :  In-memory Web API 모듈은 Angular에 HTTP와 관계가 없다.

만약 당신이 HttpClient에 대해 배울려고 단지 이 튜토리얼을 읽기만하면 , 이번 단계를 건너 띌수 있다.  만약 당신은 이 튜토리얼을 따라 코딩한다면, 여기 있어서 In-memory Web API을 지금 추가해라.


npm을 통한 In-memory Web API 패키지 설치한다.

참고 : 이 패키지의 버전은 현재 버전인 @angular/cli과의  호환선을 유지하기 위해 v0.5은 로 잠겨 있다.

npm install angular-in-memory-web-api@0.5 --save


아래는 실행결과이다.

PS D:\workspace\dev\angular02\angular-tour-of-heroes> npm install angular-in-memory-web-api@0.5 --save

npm ERR! path D:\workspace\dev\angular02\angular-tour-of-heroes\node_modules\needle\node_modules

npm ERR! code EPERM

npm ERR! errno -4048

npm ERR! syscall scandir

npm ERR! Error: EPERM: operation not permitted, scandir 'D:\workspace\dev\angular02\angular-tour-of-heroes\node_modules\needle\node_modules'

npm ERR!  { Error: EPERM: operation not permitted, scandir 'D:\workspace\dev\angular02\angular-tour-of-heroes\node_modules\needle\node_modules'

npm ERR!   stack: 'Error: EPERM: operation not permitted, scandir \'D:\\workspace\\dev\\angular02\\angular-tour-of-heroes\\node_modules\\needle\\node_modules\'',

npm ERR!   errno: -4048,

npm ERR!   code: 'EPERM',

npm ERR!   syscall: 'scandir',

npm ERR!   path: 'D:\\workspace\\dev\\angular02\\angular-tour-of-heroes\\node_modules\\needle\\node_modules' }

npm ERR!

npm ERR! Please try running this command again as root/Administrator.

npm ERR! A complete log of this run can be found in:

npm ERR!     C:\Users\oldma\AppData\Roaming\npm-cache\_logs\2018-04-23T04_16_31_730Z-debug.log


npm cache clean 하면 된다는데 npm cache clean 해도 오류를 뱉는다.

npm cache verify 한후 실행하니 경고와 함깨 실행되었다.

PS D:\workspace\dev\angular02\angular-tour-of-heroes> npm install angular-in-memory-web-api@0.5 --save

npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.0 (node_modules\fsevents):

npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.0: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})


+ angular-in-memory-web-api@0.5.4

added 127 packages and updated 1 package in 19.496s



잠시후에 만들 HttpClientInMemoryWebApiModule과 InMemoryDataService 클래스를 임포트 한다.

src/app/app.module.ts (In-memory Web API imports)

import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';

import { InMemoryDataService }  from './in-memory-data.service';

InMemoryDataService로 HttpClient를 가져온 후에 HttpClientInMemoryWebApiModule을 @ NgModule.imports 배열에 추가하십시오.

HttpClientModule,


// The HttpClientInMemoryWebApiModule module intercepts HTTP requests

// and returns simulated server responses.

// Remove it when a real server is ready to receive requests.

HttpClientInMemoryWebApiModule.forRoot(

  InMemoryDataService, { dataEncapsulation: false }

)


forRoot() 설정 메소드는 인메모리 데이터베이스가 주가된 InMemoryDataService 클래스를 가진다.

The Tour of Heroes 샘플은 다음 내용을 가진 src/app/in-memory-data.service.ts 클래스를 생성한다.  

src/app/in-memory-data.service.ts

import { InMemoryDbService } from 'angular-in-memory-web-api';


export class InMemoryDataService implements InMemoryDbService {

  createDb() {

    const heroes = [

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

    ];

    return {heroes};

  }

}

이파일은 mock-heroes.ts 파일을 대체한다. 이제는 삭제해도 안전하다.

서버가 준비 됬을때, In-memory Web API를 떼어서 분리하고  앱의 요청은 서버를 통해 할것이다.

지금 HttpClient 이야기로 돌아갑니다.


영웅과 HTTP

필요한 어떤 HTTP 을 import 한다.

src/app/hero.service.ts (import HTTP symbols)

import { HttpClient, HttpHeaders } from '@angular/common/http';

HttpClient를 http라 불리는 private 속성으로 생성자에 주입한다.

constructor(

  private http: HttpClient,

  private messageService: MessageService) { }

MessageService 주입을 유지한다.  private 로그 메소드안에서 감싸진것을 자주 호출할수 있다.


/** Log a HeroService message with the MessageService */

private log(message: string) {

  this.messageService.add('HeroService: ' + message);

}

heroesUrl을 서버에서 영웅 자원의 주소 같이 정의한다.

private heroesUrl = 'api/heroes';  // URL to web api

HttpClient를 이용한 영웅 얻기

현재의 HeroService.getHeroes()은 RxJS of()의  Observable<Hero[]> 모의 영웅들의 배열을 리턴하는 함수를 사용한다.

src/app/hero.service.ts (getHeroes with RxJs 'of()')

getHeroes(): Observable<Hero[]> {

  return of(HEROES);

}


HttpClient를 사용해서 메소드 변경

/** GET heroes from the server */

getHeroes (): Observable<Hero[]> {

  return this.http.get<Hero[]>(this.heroesUrl)

}


브라우저가 리프레시 한다. 영웅 데이터는 모의 서버에서 부터 성공적으로 로딩된다.

당신은 http.get을 바꿔 버렸고 두 기능 모두 Observable <Hero []>를 반환하기 때문에 다른 변경없이 앱은 계속 작동합니다.


Http 모듈은 하나의 값을 리턴한다.

모든 HttpClient 메소드는 어떤 RxJS Observable을 리턴한다.

HTTP은 용청/응답 프로토콜이다. 요청하면 하나의 응답을 리턴한다.

보통, Observable은 시간이 흐르면 여러 값을 리턴할수 있다.  HttpClient에서의 옵저버블은 항상 하나의 값을 보내고 그리고 완료하고 다시는 보내지 않는다.

특정한 HttpClient.get 리턴된한 Observable<Hero[]>을 호출한다. 그야말로 "an observable of hero arrays". 실제로, 그것은 하나의 영웅 배열을 한번 리턴한다.


HttpClient.get 응답 데이터를 리턴한다.

HttpClient.get은 기본적으로 타입이 지정되지 않은 JSON object 응답 본문을 리턴한다.  선택적 형식 지정자 <Hero []>를 적용하면 입력 된 결과 개체가 제공됩니다.

JSON data의 형태는 서버의 API 에서 결정한다. The Tour of Heroes의 data API는 영웅 데이터를 배열로 리턴한다.

다른 API들은 이마도 데이터를 묻을수 있다. 오브젝트를 원할때 데이터 바깥쪽에 프로세싱 으로 RxJS map 연산자로 Observable 결과를 프로세싱하는 것을 처리하는것 파내야 해야할지 모른다. 

여기서 논의되진 않았지만,  getHeroNo404() 메소드 안에 map은 간단한 소스코드 코드를 포함된 예제이다.


에러 다루기

특별히 데이터를 원격 서버에서 가져올때 문제가 발생된다. HeroService.getHeroes() 메소드 에러들을 캐치하고 어떤 적절한 것을 한다.

에러를 캐치하는것은,  http.get()에서 RxJS catchError() 연산자를 통한 옵저버블한 결과를 "pipe"한다.

나중에 필요하게 될 다른 연산자와 함께 catchError를 rxjs/operators으로 임포트 한다.

import { catchError, map, tap } from 'rxjs/operators';


이제 .pipe() 메소드를 써서 옵저버블한 결과 연장하고 catchError() 연산자를 제공한다.

getHeroes (): Observable<Hero[]> {

  return this.http.get<Hero[]>(this.heroesUrl)

    .pipe(

      catchError(this.handleError('getHeroes', []))

    );

}

catchError() 연산자는 실패된 옵저버블을 인터셉트한다.  그것은 오류와 함께 원하는 것을 할 수있는 오류 처리기를 오류에 전달합니다.

다음 handleError() 메소드는 에러를 보도한고 애플리케이션이 계속 작업되도록 무해한 결과를 리턴한다.


handleError

다음 errorHandler()은 많은 메소드를 공유 해서 이것은 그들의 다른 요구들을 만났을때 일반적이다.

오류를 직접 처리하는 대신 실패한 작업의 이름과 안전한 반환 값으로 구성된 catchError에 오류 처리기 함수를 반환합니다.

/**

 * Handle Http operation that failed.

 * Let the app continue.

 * @param operation - name of the operation that failed

 * @param result - optional value to return as the observable result

 */

private handleError<T> (operation = 'operation', result?: T) {

  return (error: any): Observable<T> => {


    // TODO: send the error to remote logging infrastructure

    console.error(error); // log to console instead


    // TODO: better job of transforming error for user consumption

    this.log(`${operation} failed: ${error.message}`);


    // Let the app keep running by returning an empty result.

    return of(result as T);

  };

}

에러콘솔로 에러를 보도후에 , 핸들러는 사용자 에게 친근한 메세지를 생성하고 안전한 값을 앱이 유지할수 있게 리턴한다.

각각의 서비스 메소드는  다른  옵저버블 결과 다른 종류를 리턴하기 때문에  errorHandler() 는 타입 매개 변수를 취하므로 앱에서 기대하는 타입에 안전한 값을 리턴할수 있다.


Observable에 다가가다.

HeroService 메소드는 관찰 가능한 값의 흐름을 두드리고 (log ()를 통해) 페이지 하단의 메시지 영역으로 메시지를 보냅니다.

그들은 관측 할 수있는 값을보고, 그 값들로 무언가를하고, 그들을 따라가는 RxJS 탭 연산자로 그것을 할 것입니다. 탭 콜백은 값 자체를 건드리지 않습니다.

다음은 작업을 기록하는 탭이있는 getHeroes의 최종 버전입니다.


/** GET heroes from the server */

getHeroes (): Observable<Hero[]> {

  return this.http.get<Hero[]>(this.heroesUrl)

    .pipe(

      tap(heroes => this.log(`fetched heroes`)),

      catchError(this.handleError('getHeroes', []))

    );

}

id로 영웅을 얻기

대부분 웹 API들은 api/hero/:id에서 요청한 id로 얻는것을  제공합니다. 쵸얻을 만드는 HeroService.getHero() 메소드를 추가한다.

src/app/hero.service.ts


/** GET hero by id. Will 404 if id not found */

getHero(id: number): Observable<Hero> {

  const url = `${this.heroesUrl}/${id}`;

  return this.http.get<Hero>(url).pipe(

    tap(_ => this.log(`fetched hero id=${id}`)),

    catchError(this.handleError<Hero>(`getHero id=${id}`))

  );

}

저것들은 getHeroes()의 3개의 중요한 차이점이다.

  • 바라던 영웅의 id로  요청 URL을 생성합니다.
  • 서버 영웅 하나보다는 영웅의 배열로 응답 할 것이다.
  • 그러므로, getHero은 영웅 배열의 옵저버블 보다는 Observable<Hero>을 리턴한다.

Update heroes

영웅 상세 뷰에서 영웅의 이름을 수정한다. 타이핑 할때, 영웅 이름을 페이지의 상단의 헤드를 업데이트한다. 그러나 "go back button"을 클릭 할때, 변경된 내용이 분실된다.

만약 당신이 변경이 유지되기 원하면, 원래 서버에 그것들을 저장해야 한다.

영웅 상세 템플릿의 끝에, 저장 버튼을 클릭 이벤트를 새로운 콤포넌트 메소드 이름은 save() 라고 언급한 바인딩과 함께 추가한다    


src/app/hero-detail/hero-detail.component.html (save)

<button (click)="save()">save</button>

영웅 서비스 updateHero () 메서드를 사용하여 영웅 이름 변경 사항을 유지 한 다음 이전보기로 다시 이동하는 다음 save () 메서드를 추가합니다.

src/app/hero-detail/hero-detail.component.ts (save)

save(): void {

   this.heroService.updateHero(this.hero)

     .subscribe(() => this.goBack());

 }


HeroService.updateHero() 추가

updateHero() 메소드의 종합적인 구조는 getHeroes()와 유사하나, 그것은 http.put()를 사용해서 서버에 변경된 영웅 정보를 유지한다.  

src/app/hero.service.ts (update)


/** PUT: update the hero on the server */

updateHero (hero: Hero): Observable<any> {

  return this.http.put(this.heroesUrl, hero, httpOptions).pipe(

    tap(_ => this.log(`updated hero id=${hero.id}`)),

    catchError(this.handleError<any>('updateHero'))

  );

}


HttpClient.put() 메소드는 3개의 파라메터를 갖는다.

  • URL
  • 데이터 업데이트 ( 클래스 안에 영웅 수정 )
  • options

The URL is unchanged. The heroes web API knows which hero to update by looking at the hero's id.

URL은 변경된다. 영웅 웹 API는 영웅이 영웅의 id로 찾아 업데이트하는것을 알게된다.


The heroes web API expects a special header in HTTP save requests. That header is in the httpOptions constant defined in the HeroService.

영웅 웹 API는 HTTP 저장요청에 특별한 헤더를 예상한다. 헤더는 httpOptions안에  HeroService에 변하지 안게 정의된것 입니다.


const httpOptions = {

  headers: new HttpHeaders({ 'Content-Type': 'application/json' })

};

브라우저가 리플래시 된고, 영웅 이름 변경한다, 변경을 저장한다, "go back"버튼을 클릭한다. 이제 목록에 변경된 이름으로 영웅은 나타납니다.


새 영웅 추가

새로운 영웅을 추가하는것은, 이 앱은 영웅의 이름만 필요로한다. input 요소가와 추가된 버튼과 짝을지어진 것을 사용할수있다.

HeroesComponent  템플릿안에 헤딩 직후에 다음을 추가한다.

src/app/heroes/heroes.component.html (add)


<div>

  <label>Hero name:

    <input #heroName />

  </label>

  <!-- (click) passes input value to add() and then clears the input -->

  <button (click)="add(heroName.value); heroName.value=''">

    add

  </button>

</div>


클릭 이벤트를 응답하는것은 콤포넌트의 클릭 핸들러로 불려지고 입력 필드를 치우서 , 이것은 다른 이름을 위해 준비된다.


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


add(name: string): void {

  name = name.trim();

  if (!name) { return; }

  this.heroService.addHero({ name } as Hero)

    .subscribe(hero => {

      this.heroes.push(hero);

    });

}

이미 정해진 이름은 비어있지 않다.

addHero 성공적으로 저장 했을때, 새로운 영웅웅 답신을 받는것을 구독하고 표시되는 영웅목록란으로 밀어 넣는다.

다음섹션에 HeroService.addHero 를 작성한다.

HeroService.addHero() 추가

다음 addHero() 메소드를 HeroService 클래스에 추가한다.

src/app/hero.service.ts (addHero)

/** POST: add a new hero to the server */

addHero (hero: Hero): Observable<Hero> {

  return this.http.post<Hero>(this.heroesUrl, hero, httpOptions).pipe(

    tap((hero: Hero) => this.log(`added hero w/ id=${hero.id}`)),

    catchError(this.handleError<Hero>('addHero'))

  );

}

HeroService.addHero()는 두가지 방식중에 updateHero과 다르다.

put() 대신에 HttpClient.post()을 호출한다.

그서버가 새로운 영웅의 id로 자동생성되는 기대한다. Observable<Hero>에서 호출자를(caller) 리턴한다.

브라우저가 리프래시되면 어떤 영웅들이 추가된다.


영웅 삭제

목록 영웅들 내의 각각의 영웅은 삭제 버튼을 가질수있다.

다음 버튼 요소를 HeroesComponent 템플릿에 추가하고  <li> 요소 안에 영웅 이름이 반복된다.


<button class="delete" title="delete hero"

(click)="delete(hero)">x</button>

영웅의 목록에 대한 HTML은 다음과 같아야합니다.


src/app/heroes/heroes.component.html (list of heroes)


<ul class="heroes">

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

    <a routerLink="/detail/{{hero.id}}">

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

    </a>

    <button class="delete" title="delete hero"

    (click)="delete(hero)">x</button>

  </li>

</ul>

영웅 항목의 맨 오른쪽에 삭제 버튼을 배치하려면 heroes.component.css에 CSS를 추가하십시오. 아래의 마지막 검토 코드에서 CSS를 찾을 수 있습니다.

구성 요소에 delete () 핸들러를 추가하십시오.

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


delete(hero: Hero): void {

  this.heroes = this.heroes.filter(h => h !== hero);

  this.heroService.deleteHero(hero).subscribe();

}

비록 HeroService에 영웅 삭제를 위임한 컴포넌트 이긴하지만, 영웅들의 목록 자기 자신의 것을 계속 업데이트를 위해 책임지고 이다.  컴포넌트의 delete() 메소드는 목록에서 즉시 HeroService가 서버에서 성공할것으로 예상하고 제거한다.

컴포넌트가 heroService.delete()으로부터  옵저버블한 리턴하는것은  실제 아무것도  아니다. 게다가 구독해야한다. 

subscribe()을 방치 했다면, 서비스는  서버로 삭제요청 보내는것을 할수 없을것이다. 원칙적으로 Observable은 어떤 구독 전까지 아무것도 하지않는다.

일시적으로 subscribe() 제거를 통해 당신자신을 위해 확실하게 하다. "Dashboard"를 클릭하는것, 그리고 "Heroes" 클릭하는것.  영웅들의 목록이 다시 가득차 있는것을 볼수 있을것이다.

HeroService.deleteHero() 추가

deleteHero() 메소드를 HeroService에 다음과 같이 추가 한다.


src/app/hero.service.ts (delete)


/** DELETE: delete the hero from the server */

deleteHero (hero: Hero | number): Observable<Hero> {

  const id = typeof hero === 'number' ? hero : hero.id;

  const url = `${this.heroesUrl}/${id}`;


  return this.http.delete<Hero>(url, httpOptions).pipe(

    tap(_ => this.log(`deleted hero id=${id}`)),

    catchError(this.handleError<Hero>('deleteHero'))

  );

}


HttpClient.delete을 호출한다.

URL은 영웅 자원 URL과 삭제할 영웅의 id가 더해진 URL이다.

put과 post와 함깨 데이터를 보낼수 없다.

아직까진 httpOptions으로 보낸다.

브라우저가 리프래시 되고 새롭게 삭제하는 기능을 시도한다.


이름으로 검색

이번 마지막 예제는, chain Observable operators 연산자와 함깨 HTTP 요청과 닮은 숫자를 최소화 하는것을  배운고, 네트워크 대역을 경제적으로 소모하는것을 배운다.

영웅을 찾는 기능을 대시보드에 추가할 것이다. 검색 박스안에 사용자가 이름을 타이핑할때, 그 이름으로 필터된 영웅들을 위해 HTTP요청을 반복해서 만들 것이다. 목표는 필요한만큼 요청을 하는 것이다.

HeroService.searchHeroes

searchHeroes 메소드를 HeroService에 추가하는것을 시작 한다.


src/app/hero.service.ts

/* GET heroes whose name contains search term */

searchHeroes(term: string): Observable<Hero[]> {

  if (!term.trim()) {

    // if not search term, return empty hero array.

    return of([]);

  }

  return this.http.get<Hero[]>(`api/heroes/?name=${term}`).pipe(

    tap(_ => this.log(`found heroes matching "${term}"`)),

    catchError(this.handleError<Hero[]>('searchHeroes', []))

  );

}

만약 검색어가 없으면 빈 배열을 메소드는 즉각 리턴한다. 나머지는 getHeroes()와 매우 유사하다. 중요한 다른 단하나는  검색어로된 요청문자열을 포함한  URL이다.


대시보드에 검색 추가

DashboardComponent 템플릿을 열고 DashboardComponent 템플릿의 아래에 <app-hero-search>라는 영웅 검색 요소를 한다.


src/app/dashboard/dashboard.component.html


<h3>Top Heroes</h3>

<div class="grid grid-pad">

  <a *ngFor="let hero of heroes" class="col-1-4"

      routerLink="/detail/{{hero.id}}">

    <div class="module hero">

      <h4>{{hero.name}}</h4>

    </div>

  </a>

</div>


<app-hero-search></app-hero-search>

이템플릿 모양은  HeroesComponent template안에 *ngFor 반복자와 많이 비슷하다.

불행하게도, 앱에 고장난 요소를 추가한다. 앵귤라는 <app-hero-search>와 매치되는 섹렉터의 컴포넌트를 찾을수 없다.

HeroSearchComponent 아직 존재 하지않는다. 고쳐야된다.


HeroSearchComponent 생성

Create a HeroSearchComponent with the CLI.

CLI로 HeroSearchComponent 생성

ng generate component hero-search

CLI이 3개의 HeroSearchComponent 자동생성되고 추가된 컴포넌트를 `AppModule'에 선언한다.

자동생성된 HeroSearchComponent 템플릿은 텍스트 박스로 변경하고 검색결과의 매치를 목록화 한다.


src/app/hero-search/hero-search.component.html


<div id="search-component">

  <h4>Hero Search</h4>


  <input #searchBox id="search-box" (keyup)="search(searchBox.value)" />


  <ul class="search-result">

    <li *ngFor="let hero of heroes$ | async" >

      <a routerLink="/detail/{{hero.id}}">

        {{hero.name}}

      </a>

    </li>

  </ul>

</div>

hero-search.component.css에 아래 마지막 코드 검토에 목록화된  전용 CSS 스타일을  추가한다.

사용자가 검색 상자에 입력 할 때 keyup 이벤트 바인딩은 구성 요소의 search () 메서드를 새 검색 상자 값으로 호출합니다.


AsyncPipe

예상대로 * ngFor는 영웅 개체를 반복합니다.

자세히 살펴보면 * ngFor가 영웅이 아닌 영웅이라는 목록을 반복하는 것을 볼 수 있습니다.

<li *ngFor="let hero of heroes$ | async" >

$는 heroes$ 배열이 아닌 Observable임을 나타내는 관례입니다.

* ngFor는 Observable을 사용하여 아무 것도 할 수 없습니다. 그러나 파이프 문자 (|) 뒤에는 async가 있으며 Angular의 AsyncPipe를 식별합니다.

AsyncPipe는 Observable에 자동으로 가입하므로 구성 요소 클래스에서 Observable을 수행 할 필요가 없습니다.


HeroSearchComponent  클래스 수정

생성 된 HeroSearchComponent 클래스와 메타 데이터를 다음과 같이 바꿉니다.

src/app/hero-search/hero-search.component.ts


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


import { Observable } from 'rxjs/Observable';

import { Subject }    from 'rxjs/Subject';

import { of }         from 'rxjs/observable/of';


import {

   debounceTime, distinctUntilChanged, switchMap

 } from 'rxjs/operators';


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

import { HeroService } from '../hero.service';


@Component({

  selector: 'app-hero-search',

  templateUrl: './hero-search.component.html',

  styleUrls: [ './hero-search.component.css' ]

})

export class HeroSearchComponent implements OnInit {

  heroes$: Observable<Hero[]>;

  private searchTerms = new Subject<string>();


  constructor(private heroService: HeroService) {}


  // Push a search term into the observable stream.

  search(term: string): void {

    this.searchTerms.next(term);

  }


  ngOnInit(): void {

    this.heroes$ = this.searchTerms.pipe(

      // wait 300ms after each keystroke before considering the term

      debounceTime(300),


      // ignore new term if same as previous term

      distinctUntilChanged(),


      // switch to new search observable each time the term changes

      switchMap((term: string) => this.heroService.searchHeroes(term)),

    );

  }

}


Observable로서 영웅의 선언을 주목하라.

heroes$: Observable<Hero[]>;

ngOnInit ()에서 설정합니다. 당신이하기 전에, searchTerms의 정의에 집중하십시오.


The searchTerms RxJS subject

searchTerms RxJS 제목 

searchTerms 속성은 RxJS Subject로 선언됩니다.


private searchTerms = new Subject<string>();


// Push a search term into the observable stream.

search(term: string): void {

  this.searchTerms.next(term);

}

제목은 관찰 가능한 가치의 원천이며 Observable 자체입니다. Observable과 같이 Subject를 구독 할 수 있습니다.

search () 메소드가 수행하는 것처럼 next (value) 메소드를 호출하여 Observable로 값을 보낼 수도 있습니다.

search () 메서드는 텍스트 상자의 키 입력 이벤트에 대한 이벤트 바인딩을 통해 호출됩니다.


<input #searchBox id="search-box" (keyup)="search(searchBox.value)" />

사용자가 텍스트 상자에 입력 할 때마다 바인딩은 텍스트 상자 값인 "검색 용어"로 search ()를 호출합니다. searchTerms는 검색 용어의 꾸준한 흐름을 방출하는 Observable이됩니다.


RxJS 연산자 채널링

모든 사용자 키 입력 후 searchHeroes ()에 새로운 검색어를 직접 전달하면 과도한 양의 HTTP 요청이 만들어져 서버 리소스에 부담이되고 셀룰러 네트워크 데이터 계획을 통해 레코딩됩니다.

대신 ngOnInit () 메서드는 searchHermes ()에 대한 호출 수를 줄이는 RxJS 연산자의 시퀀스를 통해 관찰 할 수있는 searchTerms를 파이프 처리하여 궁극적으로 영웅 검색 결과 (각 Hero [])를 관찰 할 수있게합니다.


this.heroes$ = this.searchTerms.pipe(

  // wait 300ms after each keystroke before considering the term

  debounceTime(300),


  // ignore new term if same as previous term

  distinctUntilChanged(),


  // switch to new search observable each time the term changes

  switchMap((term: string) => this.heroService.searchHeroes(term)),

);

debounceTime (300)은 새로운 문자열 이벤트의 흐름이 300 밀리 초 동안 일시 중지 될 때까지 기다린 후 최신 문자열을 전달합니다. 300ms보다 자주 요청을하지 않습니다.

distinctUntilChanged는 필터 텍스트가 변경된 경우에만 요청을 보냅니다.

switchMap ()은 debounce 및 distinctUntilChanged를 통해 검색하는 각 검색어에 대해 검색 서비스를 호출합니다. 이전 검색 관측 값을 취소하고 파기하며 관찰 가능한 최신 검색 서비스 만 반환합니다.

switchMap 연산자를 사용하면 모든 규정 키 이벤트가 HttpClient.get () 메소드 호출을 트리거 할 수 있습니다. 요청간에 300ms의 일시 중지가 있더라도 비행 중에 여러 HTTP 요청을 할 수 있으며 보낸 순서대로 반환하지 않을 수 있습니다.

이전 searchHeroes () Observable을 취소한다고해서 보류중인 HTTP 요청이 실제로 중단되는 것은 아닙니다. 원치 않는 결과는 응용 프로그램 코드에 도달하기 전에 버려집니다.

구성 요소 클래스는 영웅 $ observable을 구독하지 않는다는 것을 기억하십시오. 이것이 템플릿의 AsyncPipe 작업입니다.


시도 해봐

앱을 다시 실행하십시오. 대시 보드에서 검색 상자에 텍스트를 입력하십시오. 기존의 영웅 이름과 일치하는 문자를 입력하면 다음과 같은 메시지가 표시됩니다.


요약

당신은 여행이 끝났을 때 많은 것을 성취했습니다.

  • 앱에서 HTTP를 사용하기 위해 필요한 의존성을 추가했습니다.
  • HeroService를 리팩터링하여 웹 API에서 영웅을로드하십시오.
  • post (), put () 및 delete () 메소드를 지원하도록 HeroService를 확장했습니다.
  • 영웅을 추가, 편집 및 삭제할 수 있도록 구성 요소를 업데이트했습니다.
  • 메모리 내 웹 API를 구성했습니다.
  • Observables 사용법을 배웠습니다.


반응형
반응형


영웅 상세 탐색

HeroDetailsComponent는 선택된 영웅의 상세를 표시한다. 지금 바로 HeroDetailsComponent는 HeroesComponent의 바닥에 단지 보이기만한다.

The user should be able to get to these details in three ways.

  1. 대시보드에 영웅을 클릭 함으로써
  2. 영웅들의 목록중에 영웅을 클릭함으로써
  3. 브라우저에 주소창에  영웅을 찾아서 표시하기위한  "deep link" URL을  붙힘으로써

이번 섹션에서는, 네이게이션을 HeroDetailsComponent로 활성화 할수 있고 그것을 HeroesComponent로부터 해방시키다.


HeroesComponent에서 영웅 상세를 삭제

HeroesComponent에 영웅 아이템을 클릭했을때, 앱은 HeroDetailComponent을 찾을것이며,  영웅들의 목록 뷰와 함깨 영웅 상세 뷰를 제자리로 되돌리것. 그것이 지금하는것처럼 영우 목록 뷰는 더이상 영웅 상세를 보여주지  않을것이다.

HeroesComponent 템플릿을 열고 바닥으로부터 <app-hero-detail> 요소를 삭제한다.

영웅 아이템을 클릭하는하면 지금 아무것도 하지 않는다. HeroDetailComponent로 라우팅을 활성화 한후에 얼마안되어 고칠것이다.


영웅 상세 라우트 추가

URL은 ~/detail/11 와 같은  영웅의 어떤 id 가 11의 영웅 상세 뷰를 네비게이팅하기에 좋은 URL  일것이다.

AppRoutingModule 을 열고 HeroDetailComponent 를 추가한다.


src/app/app-routing.module.ts (import HeroDetailComponent)

import { HeroDetailComponent }  from './hero-detail/hero-detail.component';

 경로 패턴이 영웅 상세뷰와 매치된 AppRoutingModule.routes 배열로 파라메터화된 라우트를 추가한다.

{ path: 'detail/:id', component: HeroDetailComponent },

경로안의 콜론 (:) 은  :id는 특정 영웅  id를 위한  플레이스홀더로  나타낸다.

이쯤에서, 모든 애플리케이션 라우터들은 제자리에 준비가 되었다.

src/app/app-routing.module.ts (all routes)

const routes: Routes = [

  { path: '', redirectTo: '/dashboard', pathMatch: 'full' },

  { path: 'dashboard', component: DashboardComponent },

  { path: 'detail/:id', component: HeroDetailComponent },

  { path: 'heroes', component: HeroesComponent }

];


DashboardComponent 영웅 링크들

DashboardComponent 영웅 링크는 바로지금 아무것도 안한다.

이제 라우터는 HeroDetailComponent에 라우트를 가졌으므로, 대시보드에 영웅 링크 수정하여 매개변수화된 대시보드 라우트를 통해 탐색한다.


src/app/dashboard/dashboard.component.html (hero links)

<a *ngFor="let hero of heroes" class="col-1-4"  routerLink="/detail/{{hero.id}}">

앵귤라가 써넣은 바인딩을 *ngFor 반복자와 함깨 각각의 라우터링트 안에 반복되는 현 hero.id를 사용한다.


HeroesComponent 영웅 링크

HeroesComponent안에 영웅 아이템들은  클릭 이벤트는 꼭 컴포넌트의 onSelect() 메소드를 묶은  <li>요소 이다.

src/app/heroes/heroes.component.html (list with onSelect)

<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>

<li> 뒤에 *ngFor의 옷을 벗는다. (<a>) 앵커 요소 안에 이름과 badge를 감싸고, 그리고 routerLink 속성을 대시보드 템플릿안에 같은 앵커에  추가한다.

src/app/heroes/heroes.component.html (list with links)

<ul class="heroes">

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

    <a routerLink="/detail/{{hero.id}}">

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

    </a>

  </li>

</ul>

프라이베이트한 스타일시트를 고쳐야만 한다.


죽은 코드 제거 (옵션)

HeroesComponent 클래스가 아직도 작업하는 동안에, onSelect() 메소드와 selectedHero 속성은 이미 사용되지 않는다.

이것은 멋지게 치웠고, 나중에 너자신에게 고마워 할 것이다. 죽은 코드를 잘라낸 후에 클래스다.

src/app/heroes/heroes.component.ts (cleaned up)

export class HeroesComponent implements OnInit {

  heroes: Hero[];


  constructor(private heroService: HeroService) { }


  ngOnInit() {

    this.getHeroes();

  }


  getHeroes(): void {

    this.heroService.getHeroes()

    .subscribe(heroes => this.heroes = heroes);

  }

}

Routable HeroDetailComponent

이전에 부모 HeroesComponent는  HeroDetailComponent.hero 속성과 영웅을 표싷는 HeroDetailComponent를 설정 됬다.

HeroesComponent 더이상 하지 못한다. 라우터는   ~/detail/11과 같은 URL에 응답 으로  HeroDetailComponent를 만든다.

HeroDetailComponent는 영웅을 표시하는 새로운 방법을 필요로합니다.

  • 라우터는 생성된 그것을 얻는다.
  • 라우트에서 id를 뽑아낸다.
  • 서버를 통해 HeroService로  id로 영웅을 습득하다.

다음의 import를 추가한다.

src/app/hero-detail/hero-detail.component.ts

import { ActivatedRoute } from '@angular/router';

import { Location } from '@angular/common';


import { HeroService }  from '../hero.service';

ActivatedRoute, HeroService, 그리고 생성자에 들어가는 지역 서비스 주입, 그것들의 값들을 프라이페이트 항목안에 저장한다.

constructor(

  private route: ActivatedRoute,

  private heroService: HeroService,

  private location: Location

) {}

ActivatedRoute 라우터가 HeroDetailComponent의 인스턴스에 대한 정보를 잡고 있다. 이콤포넌트는 라우터의 URL에서 추출한 파라메터를 담은 자루에 흥미를 가지고 있다. "id" 파라메터는 화면에 표시하는 영웅의 id이다.

HeroService는 원격서버로 부터 영웅 데이터를 받고 이 콤포넌트는 영우을 화면에 표시하는것을 얻는것을 사용할 것이다.

경로는 앵귤라 서비스가 브라우저와 함깨 소통하는것이다. 나중에  그 뷰로 뒤로 돌아가는것을 탐색할려면 사용할수 있다.


라우터 파라메터에서  id를 추출

ngOnInit()에서  라이프사이클 훅은 getHero() 호출하고 다음처럼 정의한다.

ngOnInit(): void {

  this.getHero();

}

getHero(): void {

  const id = +this.route.snapshot.paramMap.get('id');

  this.heroService.getHero(id)

    .subscribe(hero => this.hero = hero);

}


route.snapshot은 라우터 정보의 정적인 이미지를 금새 컴포넌트는 생성된다.

paramMap은 URL에 라우트 파라메터 값의 확장된 사전이다. "id" 키는 가지고온 영웅의 id 리턴한다.

라우트 파라메터는 항상 문자열이다. 자바스크립트 (+) 연산자는  영웅의 id가 될것이 문자열을 숫자로 전환킨다.

브라우저가 재생되고 컴파일러 에러와 함께 앱이 기능을 멈춘다. HeroService getHero() 메소드를 가지는것을 하지않는다. 이것을 지금 추가한다.


HeroService.getHero() 추가

HeroService 열고 getHero() 메소드를 추가한다.


src/app/hero.service.ts (getHero)

getHero(id: number): Observable<Hero> {

  // Todo: send the message _after_ fetching the hero

  this.messageService.add(`HeroService: fetched hero id=${id}`);

  return of(HEROES.find(hero => hero.id === id));

}

backticks ( ` ) 기호는   자바스크립트 템플릿이 내장되는 id를 문자그대로를 정의한다.

getHeroes(), getHero() 동기적 특색을 갖는다. RxJS of() 함수를 사용하여 Observable한 가짜 영웅을 리턴 한다.

실제 Http 요청 처럼 HeroDetailComponent를 변경하는것 없이 그것을 부를수 있게  getHero()를 재구현하는게 가능하다.


시도해 보자.

브라우저는 새로고침되고 앱이 다시 동작한다. 대시보드 혹은 영웅 목록에 있는 영웅을 클릭할수 있고 영웅의 상세 뷰를 네비게이트 한다.

브라우저 주소창에 localhost:4200/detail/11를 붙혀넣으면 , 라우터는 id가 11, "Mr. Nice"인 영웅의 상세뷰를 네비게이트 한다.


돌아오는 길 찾기

브라우저의 뒤로가기 버튼을 클릭함으로써 , 영웅 목록이나 대시 보드 뷰로 뒤로 갈수 있다. 당신에게 어떤 상세뷰를 보내느냐에 따라 달려있다.

HeroDetail 뷰에 그렇게 하는 버튼을 갖는것은 멋져질수 있다.

컴포넌트 템플릿에 바닥에 뒤로가기 버튼을 추가하고 컴포넌트의 goBack()메소드를 바인딩 합니다.

src/app/hero-detail/hero-detail.component.html (back button)

<button (click)="goBack()">go back</button>

미리 주입한 지역 서비스를 사용해서 브라우저 히스토리 스텍의 한단계 뒤로 찾아가는   콤포넌트 클래스에 goBack() 메소드를 추가한다.

src/app/hero-detail/hero-detail.component.ts (goBack)

goBack(): void {

  this.location.back();

}

브라우저를 새로 고치고 클릭하십시오. 사용자는 대시 보드에서 영웅 세부 정보로 돌아가 영웅 목록에서 세부 정보, 영웅 세부 정보 및 영웅 정보를 다시 볼 수 있습니다.

이페이지에서 추진하는 모든 자격요건 충족했다.


마지막 코드리뷰

https://angular.io/tutorial/toh-pt5#final-code-review 참조


요약

  • 다른 콤포넌트사이를 찾아가는 Angular 라우터를  추가된다.
  • AppComponent를 <a> 링크와 <router-outlet>이있는 탐색 쉘로 변환했습니다.
  • AppRoutingModule에서 라우터를 구성했습니다.
  • 간단한 경로, 리디렉션 경로 및 매개 변수화 된 경로를 정의했습니다.
  • anchor 요소에서 routerLink 지시문을 사용했습니다.
  • 밀접하게 결합 된 마스터 / 상세 뷰를 라우팅 된 상세 뷰로 리팩토링했습니다.
  • 라우터 링크 매개 변수를 사용하여 사용자가 선택한 영웅의 상세보기로 이동했습니다.
  • 여러 구성 요소간에 HeroService를 공유했습니다.


반응형
반응형

주 / 상세 콤포넌트

현재  HeroesComponent 영웅들의 목록과 선택된 영웅의 상세 정보 둘다 표시된다

하나의 컴포넌트 안에 모든 특징을 보유한는것은  큰 애플리케이션을 유지 할수 없다. 각각 특정 작업 혹은 작업흐름 집중할수 있도록, 큰 컴포넌트를 더 작은 서브 컴포넌트로 분리하길 원한다. 

이페이지에서 재사용가능한  HeroDetailsComponent를 분리하는 방량의  첫번째 단계를 할수 있다.

HeroesComponent는 영웅들 목록에 있는 영웅들을 단지 내놓기만한다. HeroDetailsComponent 선택된 영웅의 상세를 내놓는다.


HeroDetailComponent 제작

Angular CLI를 사용해서 hero-detail 이름으로 새로운 컴포넌트를 자동생성한다.

ng generate component hero-detail

그 명령은 HeroDetailComponent 파일들과 AppModule에서 컴포넌트를 선언하는 것으로 골격을 이룬다.

PS D:\workspace\dev\angular02\angular-tour-of-heroes> ng generate component hero-detail

  create src/app/hero-detail/hero-detail.component.html (33 bytes)

  create src/app/hero-detail/hero-detail.component.spec.ts (660 bytes)

  create src/app/hero-detail/hero-detail.component.ts (297 bytes)

  create src/app/hero-detail/hero-detail.component.css (0 bytes)

  update src/app/app.module.ts (592 bytes)

실행된 내용이다. 관련 컴포넌트 이름으로 파일 4개를 만들고 app.module.ts 파일을 업데이트 했다.


템플릿 작성

영웅 상세 HTML을 HeroesComponent 템플릿 아래에서 자르고  HeroDetailComponent 템플릿의  그 생성된 표준문안 위에 붙혀넣는다. 

붙혀넣은 HTML은 selectedHero을 참조한다. 새로운 HeroDetailComponent   꼭 선택된 영웅 뿐만아니라 현재의 어떤 영웅을 줄 수 있다.  따라서  템플릿 안의 모든 "hero"를 "selectedHero"로  교체한다.

완료되면 HeroDetailComponent 템플릿 다음 처럼 보여야 한다.

src/app/hero-detail/hero-detail.component.html

<div *ngIf="hero">

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

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

  <div>

    <label>name:

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

    </label>

  </div>

</div>


영웅 속성에 @Input() 추가

HeroDetailComponent 템플릿은 콤포넌트에 Hero 유형의 영웅 속성에 바인드 된다.

HeroDetailComponent 클래스 파일을 열고  Hero symbol을 추가한다.

src/app/hero-detail/hero-detail.component.ts (import Hero)

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

영웅 속성은 Input 속성 @Input() 데코레이터로 애토테이트된 것이어야 야한다.  외부 HeroesComponent 다음과 같이 바인드 하기 때문이다.

<app-hero-detail [hero]="selectedHero"></app-hero-detail>

@angular/core import 문장을 Input을 추가하여  개정한다.

src/app/hero-detail/hero-detail.component.ts (import Input)

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

@Input() 데코레이를 영웅 속성 앞에 추가합니다.

@Input() hero: Hero;

저것은 만들고 싶은 HeroDetailComponent 클래스에 대해 변경해야할 유일한 변경사항입니다. 더이상 속성들이 없다. 프리젠테이션 로직이 아니다.

그 컴포넌트는 단순히 영웅 속성을 통해 영웅 개체를 수신하고 표시한다.


HeroDetailComponent를 보여주자.

HeroesComponent는 여진히  주/상세가 아닙니다.

그것의 자신을 영웅 상세를 표시하고, 템플릿의 일부를 자르기 전에것을 사용한다. 지금 이것은 HeroDetailComponent로 위임 할수 있다.

두개의 콤포넌트들은 부모/자식 관계를 가질수 있다.

그 부모 HeroesComponent 는  자식 HeroDetailComponent 를 보여주기 위해  보내진 새로운 영웅 사용자가 목록에서 영웅을 선택할때마다  제어 할것이다. 

HeroesComponent 클래스가 변하는것을 원치 않지만 그것의 템플릿을 변경 할 것이다.


HeroesComponent 템플릿 업데이트

HeroDetailComponent 선택자는 'app-hero-detail' 이다.

<app-hero-detail> 요소를 HeroesComponent 템플릿의 아래 부근에 추가하고 그곳에서 영웅 상세가  예전처럼 보여진다.

HeroesComponent.selectedHero 영우 속성의 요소로 바인드한다.


heroes.component.html (HeroDetail binding)

<app-hero-detail [hero]="selectedHero"></app-hero-detail>

[hero]="selectedHero" 은 앵귤라 속성을 바인딩하는 것이다.

이것은  selectedHero 에서  단방향 데이터 방인딩이다.

이제 사용자가 목록의 영웅을 클릭하면 selectedHero 변한다. 

selectedHero 변할때, 영웅이 속성 바인딩 업데이트 되고 HeroDetailComponent 그 새로운 영웅을 표시한다.

변경한 HeroesComponent 템플릿은 다음처럼 보인다.

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>

<app-hero-detail [hero]="selectedHero"></app-hero-detail>

브라우저가 재생되고 앱 작업이 이전과 같이 재식작  된다 .


뭐가 바뀌었나?

앞에서와 같이, 매번 사용자가 영웅 이름을 클릭 할때마다, 그 영운 상세는 영웅 목록 아래로 나타난다. 지금 HeroDetailComponent는 HeroesComponent 대신에  그것들이 상세에 있는 것이다.

원래 HeroesComponent를 두개의 콤포넌트로 리팩토링하면 현재와 미래 모두 유익한 효과를 얻을수 있다.

HeroesComponent는 책임을 줄임으로서 간소화 했다.

부모 HeroesComponent의 간섭 없이 풍부한 영웅 수정자안에서 HeroDetailComponent를 진화 할수 있다.

영웅 상세 뷰의 간섭 없이  HeroesComponent는 진화 할수 있다.

어떤 미래 콤포넌트의 템플릿에서 HeroDetailComponent를 재사용 할수 있다.


마지막 코드 리뷰

src/app/hero-detail/hero-detail.component.ts

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

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

 

@Component({

  selector: 'app-hero-detail',

  templateUrl: './hero-detail.component.html',

  styleUrls: ['./hero-detail.component.css']

})

export class HeroDetailComponent implements OnInit {

  @Input() hero: Hero;

 

  constructor() { }

 

  ngOnInit() {

  }

}


src/app/hero-detail/hero-detail.component.html

<div *ngIf="hero"> <h2>{{ hero.name | uppercase }} Details</h2> <div><span>id: </span>{{hero.id}}</div> <div> <label>name: <input [(ngModel)]="hero.name" placeholder="name"/> </label> </div> </div>


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> <app-hero-detail [hero]="selectedHero"></app-hero-detail>


요약

별도의 재사용 할수 있는 HeroDetailComponent를  생성된다.

부모 HeroesComponent는 자식 HeroDetailComponent에 대한 통제를 준는것을 속성바인딩을 사용한다.

@Input 데코레이터는 확장 HeroesComponent으로 빌등 가능한 영웅 속성을  만들때 사용한다.



반응형
반응형

영웅 목록 표시

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


모형 영웅 생성

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

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

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스타일을 토글 할수 있다.


반응형
반응형

영웅 수정

사용자는 <input> 텍스트박스 안에 영웅 이름을 수정할 수 있어야 한다.

텍스트박스 영우의 이름이 표시되고 사용자 타입 속성을 업데이트 한다. 

즉 데이터의 흐름은  컴포넌트 클래스에서 화면으로 표시되고 화면에서 클래스로 흐름이다.

데이터의 흐름을 자동화 하는것은, 요소의 <input>과 hero.name 속성을 양방향 데이터 바인딩으로 설정 한다.


양방향 바인딩

HeroesComponent 템플릿에 상세소스

src/app/heroes/heroes.component.html (HeroesComponent's template)

<div>

    <label>name:

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

    </label>

</div>

[(ngModel)]은 앵귤러의 양방향 데이터 바인딩 문법이다.


여기 hero.name 속성은 HTML 텍스트박스에 바인딩 되고 데이터는 양방향으로 흐를수 있다. hero.name 속성 에서 텍스트박스로, 그리고 텍스트 박스는 hero.name 으로 돌아간다.


누락된 FormsModule

(ngModel) 을 추가 하면 앱이 동작하지 않는다.

에러를 보면, 브라우저의 개발툴과 다음과 같은 메세를 콘솔에서 확인한다.


Template parse errors:

Can't bind to 'ngModel' since it isn't a known property of 'input'.


크롬 에서 확인

Uncaught Error: Template parse errors:

Can't bind to 'ngModel' since it isn't a known property of 'input'. ("<div>

    <label>name:

      <input [ERROR ->][(ngModel)]="hero.name" placeholder="name">

    </label>

</div>"): ng:///AppModule/HeroesComponent.html@2:13

    at syntaxError (compiler.js:485)

    at TemplateParser.parse (compiler.js:24668)

    at JitCompiler._parseTemplate (compiler.js:34621)

    at JitCompiler._compileTemplate (compiler.js:34596)

    at eval (compiler.js:34497)

    at Set.forEach (<anonymous>)

    at JitCompiler._compileComponents (compiler.js:34497)

    at eval (compiler.js:34367)

    at Object.then (compiler.js:474)

    at JitCompiler._compileModuleAndComponents (compiler.js:34366)


ngModel은 유효한 앵귤러 명령 이긴하지만, 기본적으로 사용할수 없다.

선택적 FormsModule 이어서, 사용 할려면 옵션을 넣어야 한다.


AppModule

앵귤라는 반드시 애플리케이션 조각들이 어떻게  서로 잘 맞는지, 무슨 다른 파일들과 앱에서 필요로 하는 라이브러리들을 알아야 합니다. 이 정보를 메타데이터라고 불린다.

메타데이터 중의 일부는  컴포넌트 클래스들을 추가할 @Component 데코레이터이다. 메타데이터의 대단의 중요한 것은 @NgModule 데코레이터에 있다.

제일 중요한 @NgModule 데코레이터 애노테이트는 최상위 AppModule 클래스이다.

프로젝트가 생성될때 AppModule 클래스가 src/app/app.module.ts 코드에 Angular CLI이 자동생성된다. 


이것은 FormsModule 에서  옵션을 넣을때.


FormsModule 추가

AppModule(app.module.ts) 열고 @angular/forms 라이브러리에서 FormsModule 심볼을 가져옵니다.


app.module.ts (FormsModule 추가)

import { FormsModule } from '@angular/forms'; // <-- NgModel lives here


FormsModule을 추가 할때  @NgModule 메터데이터의 배열을 추가한다. 이 배열에 포함된것은 앱에 필요한것을 확장 모듈 목록 이다.


app.module.ts ( @NgModule imports)

imports: [

  BrowserModule,

  FormsModule 

],

브라우저가 재생될때, 앱이 다시 작동 한다.  영웅의 이름을 수정할수 있고, textbox 위에 <h2> 변화되는 즉각 반응하는 모습을 볼수 있다.


HeroesComponent 선언

모든 컴포넌트는 정확히 하나의 NgModule로 선언되어야 한다.

HeroesComponent를 선언하지 않았습니다. 그렇다면 애플리캐이션이 왜 작동 할까요?

그게 작동한 이유는 Angular CLI이 AppModule에서 HeroesComponent 콤포넌트가 자동생성될때 이를 선언했기 때문이다.

src/app/app.module.ts 파일을 열어서  HeroesComponent  찾아 위부근에 추가한다.

import { HeroesComponent } from './heroes/heroes.component';


The HeroesComponent is declared in the @NgModule.declarations array.

declarations: [

  AppComponent,

  HeroesComponent

],

애플리케이션 콤포넌트, AppComponent 와 HeroesComponent AppModule 선언한다.


최종 코드 검토


src/app/heroes/heroes.component.ts

src/app/heroes/heroes.component.html

src/app/app.module.ts

src/app/app.component.ts

src/app/app.component.html

src/app/hero.ts

content_copy

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

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

 

@Component({

  selector: 'app-heroes',

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

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

})

export class HeroesComponent implements OnInit {

  hero: Hero = {

    id: 1,

    name: 'Windstorm'

  };

 

  constructor() { }

 

  ngOnInit() {

  }

}


요약

CLI로 사용하여 두번재 HeroesComponent 생성했다.

HeroesComponent에 AppComponent로 추가하여 표시했다.

UppercasePipe 적용하여 이름 서식을 지정 했다.

양방향 데이터 바인딩은 ngModel 디렉티브를 이용했다.

AppModule에 대해서 배웠다.

AppModule에서  FormsModule을 추가 하면 앵귤라는 그것을 알아채고 ngModel 디렉티브를 지원한다.

AppModule에서 컴포넌트선언의 중요성을 배웠고 당신을 위한 CLI 선언 진가를 알아봤다.


반응형
반응형

영웅 수정

애플리케이션은 이제 기본 제목이 있다. 다음으로 영웅정보 표현과 애플리케이션 쉘의 콤포넌트의 배치 할 새로운 콤포넌트를 만든다.


영웅 콤포넌트 만들기

앵귤라 CLI를 이용해서 heroes라는 이름으로 새로운 콤포넌트를  자동생성한다.

ng generate component heroes



ng : 'ng' 용어가 cmdlet, 함수, 스크립트 파일 또는 실행할 수 있는 프로그램 이름으로 인식되지 않습니다. 이름이 정확한지 확인하고 경로가 포함된 경우 경로가 올바른지 검증한 다음 다시 시도하십시오.

위치 줄:1 문자:1

+ ng generate component heroes

+ ~~~

    + CategoryInfo          : ObjectNotFound: (ng:String) [], CommandNotFoundException

    + FullyQualifiedErrorId : CommandNotFoundException


angular/cli 이 설치가 안되었나 해서 angular/cli  설치후 다시실 행 똑 같은 오류 ..

이상하다 싶었다.

ls 명령어 후 다시 컴포넌트 재너레이터 명령 실행하니 5초 정도 걸린후 제너레이터 된 파일이 만들어졌다. 


PS D:\workspace\dev\angular02\angular-tour-of-heroes> ng generate component heroes

  create src/app/heroes/heroes.component.html (25 bytes)

  create src/app/heroes/heroes.component.spec.ts (628 bytes)

  create src/app/heroes/heroes.component.ts (269 bytes)

  create src/app/heroes/heroes.component.css (0 bytes)

  update src/app/app.module.ts (398 bytes)

html과 테스트, ts파일, css 파일이 제너레이터 되어 생성되고, 모듈 파일이 업데이트 되었다.


CLI은 src/app/heroes/ 폴더를 만들고 HeroesComponent의 3개 파일이 자동생성된다.


HeroesComponent 클래스 파일은  다음과 같다.

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


@Component({

  selector: 'app-heroes',

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

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

})

export class HeroesComponent implements OnInit {


  constructor() { }


  ngOnInit() {

  }

}

앵귤러 코어 라이브러리를 항상 import 해야된다네요. 그리고 @Component 애노테이션을 함깨 사용한다고 합니다.

@Component는  콤포넌트를 위한 Angular 메터데이터를  명시하는 decorator 이다.

CLI은 3개 메테데이터 속성을 자동생성한다.

  1. selector - 콤포넌트의 CSS 요소 선택자
  2. templateUrl - 콤포넌트의 템플릿 파일이 위치 
  3. styleUrls - 콤포넌트마느이 CSS 스타일 위치


CSS 요소 선택자, 'app-heroes', 이컴포넌트가 부모 컴포넌트의 템플릿에서 확인  html 요소의 이름이 같은것을 매치한다.

컴포넌트 생성 후 바로 싱행된는  ngOnInit는  초기화 로직을 넣기에 좋은 곳이다.

 AppModule 안에 처럼  다른곳에서 가져올수 있도록 항상 컴포넌트 클래스를 내보낸다.  


영웅 속성 추가


영웅이름을 "Windstorm" 으로 HeroesComponent 에 영웅 속성을 추가한다.

heroes.component.ts (hero property)

hero = 'Windstorm';


영웅 보여주기.

heroes.component.html 템플릿 파일을 연다.  Angular CLI 가 자동으로 만든 기본 글을 삭제하고 새로운 영웅 속성 데이터 바인을 변경한다.


heroes.component.html

{{hero}}


HeroesComponent view 보여주기

HeroesComponent를 보여주는것은  shell AppComponent의 템플릿을 추가 해야한다.

app-heroes는 HeroesComponent의 요소 선택자를  기억해라. AppComponent 템플릿 파일에 <app-heroes> 요소를 정확히 title 아래 추가한다, 


src/app/app.component.html

<h1>{{title}}</h1>

<app-heroes></app-heroes>

ng serve 명령을 이용해서 title 과 영웅 이름을 표시를 확인한다.


영웅 클래스 생성

src/app 폴더 안에 자신의 파일을 id와 name 속성을 줘서 영웅 클래스를 생성한다. .


src/app/hero.ts

export class Hero {

  id: number;

  name: string;

}


HeroesComponent 클래스로 돌아와서 Hero 클래스를 가져온다.

컴포넌트의 영웅 프로퍼티 는 Hero 타입으로  리팩터한다. 초기화설정은 1의 id 와  Windstorm 이름으로한다.

수정된 HeroesComponent클래스 파일은 다음 과 같다.

src/app/heroes/heroes.component.ts

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

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


@Component({

  selector: 'app-heroes',

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

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

})

export class HeroesComponent implements OnInit {

  hero: Hero = {

    id: 1,

    name: 'Windstorm'

  };


  constructor() { }


  ngOnInit() {

  }

}


이페이지는 더이상 적절히 표시되지 않는다. 왜냐면 문자열에서 객체로 변경된 영웅 폼 때문이다.

 [object Object] 로 표시된다.


영웅 객체 표시

템플릿 안에서 상세 레이아웃 안에서 영웅의 이름과 id 그리고 이름을 보여주는것을 알려주는  바인딩 업데이트 한다.

heroes.component.html (HeroesComponent's template)

<h2>{{ hero.name }} Details</h2>

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

<div><span>name: </span>{{hero.name}}</div>

브라우저 리프래쉬되고 영웅의 정보를 보여준다.


대문자 파이프 포멧

이처럼 hero.name 바인딩을 수정한다.

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

브라우저 리플래시되고 지금 바로 영웅의 이름은 대문자로 표시된다.

대문자를 바인딩을 써넣을때 , 파이프 연산자 | 바로뒤, 내장 UppercasePipe를 동작시킨다

파이프는 문자열 포메팅, 통화 가격, 날짜 와 다른 날짜 표시에 함에 좋은 방법이다.    


반응형
반응형

앵귤라 공식사이트에 있는 튜토리얼에 있는 내용을 직접 단계별로 해볼 계획이다.

앵귤라 공식사이트에 튜토리얼 -> https://angular.io/tutorial

튜토리얼은 총 8부분으로 나뉘어져 있다.

  1. Introduction 
  2. The Application Shell
  3. The Hero Editor
  4. Displaying a List
  5. Master/Detail Components
  6. Services
  7. Routing
  8. HTTP


1섹션은 소개, 2섹션은 애플리케이션 쉘, 3섹션은 영웅 수정, 4섹션은 목록 표시

5섹션은 주/상세 컴포넌트 6섹션은 서비스 7은 라우팅 8은 HTTP관련된 정보로 구분되어졌다.


첫번째 섹션은 만들 애플리케이션의 소개 내용이 담겨져 있다.

부족한 영어실력과 구글 번역기 돌리며 번역한 내용을 정리 해봤다.


튜토리얼은 Tour of Heroes 라는 제목으로 갖고 진행 한다.

이 튜토리얼은 앵귤라의 기본사항을 포함 하고 있다.


직원 채용 대행사 관리하는데 도움이 되는 앱을 튜토리얼 안에서 빌드 할수 있다.

이 기본 앱은  데이터 기반 애플리케이션에서 찾길 기대하는 많은 기능이 있다.

영웅 목록을 만들고 표시하고, 선택된 영웅의 세부사항을 수정하고 다르게 보이는 영웅적인 데이터를 다룹니다.


  • 튜토리얼이 끝날 무렵에 다음의 것을을 할수 있을 것이다.
  • 내장된 앵귤라 디랙티브들을 사용하여 요소를  보이거나  숨기고 영웅데이터를 목록으로 표시한다.
  • 앵귤라 콤포넌트를 만드는것은 영웅의 상세를 표시하고 영웅들의 배열을 표시한다.
  • 읽기전용 데이터는 한방햔 데이터 바인딩을 사용합니다.
  • 키보드를 누르는 거나 클릭하는거와 같은 사용자 이벤트에 컴포넌트 메소드를 바인드한다.
  • 사용자가 주 리스트에서 하나의 영웅을 선택하는것과 상세 보기에서 수정 할수 있다.
  • Format data with pipes.
  • 영웅들을 모으는 공유 서비스를 만듭니다.
  • 다른 뷰들 사이들과 과 그들의 콤포넌트들 사이에서 찾을때 라우팅을 사용합니다.


https://stackblitz.com/angular/ybbpegbnnlq

무엇을 만들지.

대시보드 라는 것으로 시작하고 더 영웅적인 영웅들을 볼수 있고

대시보드 에 두개 링크 ("Dashboard" and "Heroes" 로 구성 하여 화면이동

영웅을 클릭 하면 라우터는 "Hero Details" 을 열어 보여준후 그곳에서 영웅의 이름을 바꿀수 있다.

"Back"버튼을 클릭하는것은 대시보드로 돌아간다.상단위 링크들은 가진다. 메인뷰 중의 하나를. "Heroes"를 클릭하면  그 앱은 "Herose" 주목록이 표시된다

다른 영웅 이름을 클릭했을때, 읽기전용 작은 상세 아래에 그새로 선택된 것이 목록에 비친다.

상세보기 화면에서 선택된 영웅을 수정가능한 상세로 로 주입 한다네요;


만들려고 하는 애플리케이션의 설명을 이미지와 함께 설명하고 있다.

목록, 상세정보, 수정 등을 만든다는 내용인걸로 보인다. 



반응형

+ Recent posts