반응형

import { BrowserModule } from '@angular/platform-browser';

import { NgModule } from '@angular/core';

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


import { AppComponent } from './app.component';

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

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

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

import { MessagesComponent } from './messages/messages.component';

import { MessageService } from './message.service';

import { AppRoutingModule } from './/app-routing.module';

import { DashboardComponent } from './dashboard/dashboard.component';



@NgModule({

  declarations: [

    AppComponent,

    HeroesComponent,

    HeroDetailComponent,

    MessagesComponent,

    DashboardComponent

  ],

  imports: [

    BrowserModule,

    FormsModule,

    AppRoutingModule

  ],

  providers: [HeroService, MessageService],

  bootstrap: [AppComponent]

})

export class AppModule { }


모듈은 특정 업무를 위해 각 기능들을 묶어놓은 라이브러리이며 모듈이 여러게일수도 있고 root 모듈은 실행시 필요하므로 반드시  필요하다.

모듈은 사용되는 컴포넌트, 서비스, 디렉티브, 주입되는 서비스들을 정의 하는 것이다. 

@NgModule 애노테이션을 통해 정의하며 declarations, imports, providers, bootstrap을 배열로 정의한다.

declarations은 모듈에서 사용할 콤포넌트를 선언한다.

bootstrap로 지정된 콤포넌트가 root 콤포넌트로 지정된다.

providers은 서비스를 주입할때 사용한다.


모듈 클래스 앞에 export  해야 외부에서 해당 모듈을 사용 할수 있다.

모듈에 exports 배열 속성을 이용해서 해당 다른 모듈에서 해당모듈을 임포트하고 콤포넌트을 사용 할수 있다.


반응형
반응형


영웅 상세 탐색

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를 공유했습니다.


반응형
반응형


라우팅

우들의 영웅 관광 앱에 새로운 자격요건 이다.


  • 대시보드 뷰를 추가한다.
  • Heroes와 Dashboard사이에 길을 찾을수 있게 추가한다.
  • 사용자들이 뷰에 어느것하나의 영웅 이름을 크릭 할때 , 선택된 영웅의 상세 뷰로 길을 찾는다.
  • 사용자들이 이메일 안에 링크를 클릭 할때, 특별한 영웅의 상세 뷰를 연다.

마쳤을때, 다음처럼 앱이 네비게이트가 가능해질겁니다. 



AppRoutingModule 추가

Angular 모범적인 방법은 분리된 것을 라우터로 환경을 설정 한것을 적재하는 것이고, 최고 레벨 모듈은  root AppModule으로부터 라우링하는것과 임포관례상 모듈 클래스 이름은 AppRoutingModule 이고 app-routing.module.ts는 src/app 폴더 자리에 있다.

CLI를 사용해서 자동생성한다.

ng generate module app-routing --flat --module=app

--flat 자신의 폴더 대신에  src/app 안에  파일을 놓습니다.

--module=app CLI이 register에게 AppModule의 imports 배열에 알려준다.


PS D:\workspace\dev\angular02\angular-tour-of-heroes> ng generate module app-routing -flat --module=app

  create src/app/app-routing.module.ts (194 bytes)

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

자동생성된 파일은 다음과 같다.

src/app/app-routing.module.ts (generated)

import { NgModule } from '@angular/core';

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


@NgModule({

  imports: [

    CommonModule

  ],

  declarations: []

})

export class AppRoutingModule { }

대게 라우팅 모듈에서 콤포넌트 선언을 못한고 @NgModule.declarations 배열을 삭제 할수 있고 CommonModule 참조도 역시 삭제 할수 있다. .

@angular/router 라이브러리에서 부터 2개 심볼을 import함으로써 RouterModule안에  Routes로 라우터 환경을 설정한다.

RouterModule에 @NgModule.exports 배열을 추가한다. RouterModule을 Exporting 하는 것은  그들이 필요할 AppModule 컴포넌트 에서 지시 가능한 라우터를 만든다. 

AppRoutingModule 다음 과 같다.

src/app/app-routing.module.ts (v1)

import { NgModule }             from '@angular/core';

import { RouterModule, Routes } from '@angular/router';


@NgModule({

  exports: [ RouterModule ]

})

export class AppRoutingModule {}


라우트 추가

라우트는 라우터가 어떤 뷰를 표시하는것을 사용자가 링크를 클릭했을때나 브라우저에 주소창에 URL을 붙혀 넣었을때 알려준다.

보통 앵귤라 라우터는 두개의 속성을 갖는다.

  1. path : 브라우저의 주소바에 매치되는 문자열이다.
  2. component : 라우터가 이 라우터를 찾아갈때 생성 해야하는 콤포넌트다.

URL이  localhost:4200/heroes 과 약간 비슷할때  HeroesComponent를 찾을 예정이다.

라우터에서 참조할수 있는 HeroesComponent를 임포트 한다. 그리고나서 라우터들의 배열을 그 콤포넌트의 한게의 라우터와 함께  정의한다.

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

const routes: Routes = [

  { path: 'heroes', component: HeroesComponent }

];


셋팅이 완료되었을때, router는 URL 경로 'heroes' 와  표시되는 HeroesComponent를 매치 한다.


RouterModule.forRoot()

첫번째로 라우터를 초기화를 하고 브라우저의 경로 변경 리스닝을 시작 해야만 한다.

RouterModule을 @NgModule.imports 배열에 추가한다. 그리고 임포트한 배열안에서 RouterModule.forRoot()을 부르는 하나의 단계에서 configure을  라우터와 함깨 설정한다.  

imports: [ RouterModule.forRoot(routes) ],

메소드는 forRoot() 라고 불린다. 왜냐면 애플리케이션의 root 레벨에서 그라우터를 설정하는 것이기 때문이다.  forRoot() 메소드는 서비스 프로바이더를  공급하고, 디렉티브는 라우팅을 필요로하고, 현재 부라우저 URL에 기반을둔 항해 초기화를 수행한다.


RouterOutlet 추가


Open the AppComponent template replace the <app-heroes> element with a <router-outlet> element.

AppComponent 템픅릿을 열어  <app-heroes> 요소를 <router-outlet> 요소로 변경한다.

src/app/app.component.html (router-outlet)

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

<router-outlet></router-outlet>

<app-messages></app-messages>

<app-heroes>를 지운다 왜냐하면 HeroesComponent는 사용자가 이것을 찾아가기위해 단지 표시하는것입니다.

<router-outlet>는 라우저가 라우터된 뷰가 어디에 표시되는지 알려준다.

RouterOutlet은 AppComponent가 가능하게된 라우터 디렉티브중의 하나다. AppModule은   RouterModule을 익스포트되는한 AppRoutingModule을 임포트 하기 때문이다.


아래처럼 해보자.

여전히 이 CLI 명령어와 같이  동작  해야한다.

ng serve

브라우저는 앱 제목을 다시 표시하지만 영웅 목록은 아니다.

브라우저의 주소창을 보면 URL마지막에 /가 있다.  HeroesComponent에 라우터 경로는 /heroes 이다.

브라우저 주소창 URL에  /heroes 를 추가한다. 익숙한 영웅들의 주/상세 뷰를  볼수 있다.


링크 네비게이션 추가 (routerLink)

사용자들은  주소창에 URL 라우터를 붙혀넣기를 해야만하는것을 할 필요가없다.  길을 찾기위해서 가능하면  링크를 클릭 해야한다.

<nav> 요소를 추가하고, 그 안에서, anchor 요소 , HeroesComponent에 항해를 유발하는.  수정된 AppComponent 템플릿은 다음과 같다.

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

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

<nav>

  <a routerLink="/heroes">Heroes</a>

</nav>

<router-outlet></router-outlet>

<app-messages></app-messages>


routerLink 속성은 "/heroes"를 설정하며, 그 문자열은 라우터가  라우트에서 HeroesComponent로  매치한다. routerLink는 라우터 네비게이션에서 사용자브라우저가에서  앱 제목과 영웅 목록 링크가  새롭게 다시 보여진다. 그러나 영웅 목록은 아니다.

링크를 클릭한다. 주소창은 /heroes로 갱신 되고 영웅 목록이 보이기 시작한다.

이것을 만들고 링크로 이동하는 기능은 app.component.css에  최종 코드리뷰에 목록 처럼 개인 CSS를 추가하는것 처럼  좋아보인다.


대시보드 뷰 추가

멀티 뷰일때 더 감각적인 라우팅을 만든다. 지금까지 오직 하나의 영웅 뷰 만이었다.

CLI을 이용한 DashboardComponent 추가

ng generate component dashboard

CLI은 DashboardComponent의 파일을 자동생성하고 AppModule에 선언 합니다.

3개의 파일들의 기본 파일의 내용을 다음처럼 교체합니다.


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

    <div class="module hero">

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

    </div>

  </a>

</div>


src/app/dashboard/dashboard.component.ts

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

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

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


@Component({

  selector: 'app-dashboard',

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

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

})

export class DashboardComponent implements OnInit {

  heroes: Hero[] = [];


  constructor(private heroService: HeroService) { }


  ngOnInit() {

    this.getHeroes();

  }


  getHeroes(): void {

    this.heroService.getHeroes()

      .subscribe(heroes => this.heroes = heroes.slice(1, 5));

  }

}

src/app/dashboard/dashboard.component.css

/* DashboardComponent's private CSS styles */
[class*='col-'] {
  float: left;
  padding-right: 20px;
  padding-bottom: 20px;
}
[class*='col-']:last-of-type {
  padding-right: 0;
}
a {
  text-decoration: none;
}
*, *:after, *:before {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
}
h3 {
  text-align: center; margin-bottom: 0;
}
h4 {
  position: relative;
}
.grid {
  margin: 0;
}
.col-1-4 {
  width: 25%;
}
.module {
  padding: 20px;
  text-align: center;
  color: #eee;
  max-height: 120px;
  min-width: 120px;
  background-color: #607D8B;
  border-radius: 2px;
}
.module:hover {
  background-color: #EEE;
  cursor: pointer;
  color: #607d8b;
}
.grid-pad {
  padding: 10px 0;
}
.grid-pad > [class*='col-']:last-of-type {
  padding-right: 20px;
}
@media (max-width: 600px) {
  .module {
    font-size: 10px;
    max-height: 75px; }
}
@media (max-width: 1024px) {
  .grid {
    margin: 0;
  }
  .module {
    min-width: 60px;
  }
}


템플릿에 영웅 이름 링크들의 그리드를 통해 주다.

  • *ngFor 반복자는  컴포넌트의 영웅 배열에 많은 링크를   생성합니다.
  • dashboard.component.css에서 부터 링크들은 스타일이 검은색으로 적용된다.
  • 링크들은 아직 어느 곳으로도 가지 않고 곧 갈것이다.

 클래스는 HeroesComponent 클래스와 비슷하다.

  • heroes 배열 속성을 정의한다.
  • 생성자는 앵귤라가 HeroService가 private heroService 속성으로 주입하는것을 기대한다.
  • ngOnInit()의 라이프사이클 hook은 getHeroes을 호출한다.

표시된 영웅들의 번호를 4개로 getHeroes는 줄이다.

getHeroes(): void {

  this.heroService.getHeroes()

    .subscribe(heroes => this.heroes = heroes.slice(1, 5));

}


대시보드 라우트 추가

대시보드로 길을 찾는것은, 라우터가 적절한 라우터를 필요로한다.

AppRoutingModule에서 DashboardComponent를 임포트한다.

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

import { DashboardComponent }   from './dashboard/dashboard.component';


라우터를 경로가 DashboardComponent로 매치되는것을 AppRoutingModule.routes 배열에 추가한다.

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


기본 라우트 추가

앱이 시작될때, 브라우저 주소창은 웹 사이트의 root를 가리킨다. 저것은  어디든 길을 찾지 못한하는 라우터 처럼 어떤 기존 라우트와 매치가 안된다. <router-outlet>의 아래는 비어 있다.

앱이 대시보드를로 자동적으로 찾아가는것을 만드는것은, 다음 라우터를 AppRoutingModule.Routes 배열에  추가한다.

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

이 경로는 빈경로와 완전히 일치하는 URL을 경로가 '/dashboard'  경로로 다시보낸다. 

브라우저가 재로고침된 후에, 라우터는 DashboardComponent를  적재한고 브라우저 주소창에 /dashboard URL을 보여준다.


쉘에 대시보드 링크 추가

사용자는 페이지 상단 근처의 탐색 영역에있는 링크를 클릭하여 DashboardComponent와 HeroesComponent간에 앞뒤로 이동할 수 있어야합니다.

AppComponent 쉘 템플릿에 대시보드 네비게이션 링크를 반드시 영웅 링크 위에 추가 한다.


src/app/app.component.html

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

<nav>

  <a routerLink="/dashboard">Dashboard</a>

  <a routerLink="/heroes">Heroes</a>

</nav>

<router-outlet></router-outlet>

<app-messages></app-messages>

브라우저가 재생된후에 링크를 클릭함으로써 두개의 뷰 사이의 길을 자유롭게 찾을수 있다.

반응형
반응형

옵저버블 데이터

HeroService.getHeroes() 메소드는 동기 특성을 가진다, 어느 HeroService 동시에 가지고 올수 있는것을  넌지시 나타낸다. HeroesComponent는 영웅들을 동시적으로 가져온는것을 하는 것처럼 getHeroes() 결과를 사용한다.

this.heroes = this.heroService.getHeroes();

실제 앱에선 동작할수 없다. 이제 서비스가 현재 리턴하는 가짜 영웅들을에게 떠난다. 하지만 곧 앱은 영웅들을 원격 서버에서 가져올수 있다. 그것은 선천적으로 동시적인 연산이다.

HeroService는 서버의 응답을 기야려야만 한다.  getHeroes() 영웅 데이터 리턴을 즉각적으로 할수 없다.  브라우저는  그 서비스가 기다리는 동안의 차단되지 않을 것이다.           

HeroService.getHeroes()는 어떤 종류의 비동기특성을가져야 한다.

그것은 응답을 가질수있다. 그것은 리턴을 약속합니다. 그것은 관찰할수 있는것을 리턴할수 잇다.

이 튜토리얼에서  HeroService.getHeroes()는 이 부분에서 관찰가능한것을 리턴할수 있습니다. 왜냐하면 결국 영웅들을 가져오기 위해 앵귤라 HttpClient.get 메소드를 사용 하는것일 것이다. HttpClient.get()은 관찰 가능함을 리턴한다.


옵저버블한 HeroService 

옵저버블은 RxJS라이브러리에서 키 클래스들중의 하나이다.

나중에 HTTP 튜토리얼에서, 앵귤라의 HttpClient 메소드가 RxJS 옵저버블을 리턴하는것을 배울수 있습니다. 이번 튜토리얼은 RxJS의 of() 기능을 통해 서버에서 데이터를 갖는것을 가장할것이다.

HeroService 파일을 열어 Observable 과 RxJS 기호를 Import 합니다.

src/app/hero.service.ts (Observable imports)

import { Observable } from 'rxjs/Observable';

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

getHeroes 메소드를 아래처럼 변경합니다.

getHeroes(): Observable<Hero[]> {

  return of(HEROES);

}

of(HEROES)은  가짜 영웅 배열의 하나의 값을 내는  Observable<Hero[]>  리턴합니다. 

HTTP 튜토리얼에서 HttpClient.get<Hero[]>()을 호출 할것이다.   또한 Observable<Hero[]>은  HTTP 응답 내용에서 영웅 배열에 하나의값을 나타내는 것을 리턴합니다.


HeroesComponent 구독

HeroService.getHeroes 메소드는 Hero[]을 리턴 하곤 했다. 지금은 Observable<Hero[]>을 리턴한다.

HeroesComponent에서의 차이를 적응 해야만 합다.

getHeroes 메소드를 찾아  다음 코드와 함께 변경합니다. ( 이번전번과 비교를 위해 나라히 보면서 )

heroes.component.ts (Observable)

getHeroes(): void {

  this.heroService.getHeroes()

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

}

heroes.component.ts (Original)

getHeroes(): void {

  this.heroes = this.heroService.getHeroes();

}

Observable.subscribe()는 주요변화이다.

이전 버전은 영웅들의 배열을 콤포넌트의 영웅들의 속성으로 배정합니다. 동시에 일어나는 배정은, 서버에 즉시 영웅 정보를 리턴하는것처럼  혹은  브라우저 UI가 서버의 응답을 기다리는동안 멈춰 질수 있다.

HeroService는 원격 서버에서 실제로 요청을 만들 작업하지 않을것이다.

새로운 버전은 옵저버블되어 나타난 영웅들의 배열을 ( 지금 일어날수 있거나 지금에서 부터 몇분간  ) 기다린다.  그리고는 콜백으로 나타난 배열 구독을 통과한다. 컴포넌트의 영웅 속성의 어떤 설정을 합니다.

동시에 다가가것은 HeroService가 서버에서 영웅들을 요청할때 동작 할수 있다.


메세지를 보자

이번섹션에서는 할것들

  • 화면 아래에 앱메세지가 표시될 MessagesComponent를  추가한다.
  • injectable을 생성한다. 앱 전체의 MessageService는 표시되게 메세지를 보낸다.
  • HeroService으로 MessageService를 주입한다.
  • 메세지가 표시될때 HeroService는 영웅들을 성공적으로 가지고 온다.


MessagesComponent 생성

MessagesComponent를 CLI 사용해서 생성합니다.

ng generate component messages

CLI 은 src/app/messages 폴더 안에 컴포넌트 파일을  만들고 AppModule 안에 MessagesComponent를 선언 합니다.

AppComponent 템플릿을 자동생성된 MessagesComponent를 화면에 보이는것을 수정한다.


/src/app/app.component.html

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

<app-heroes></app-heroes>

<app-messages></app-messages>

이페이지 아래에 있는 MessagesComponent에서 기본 단락을  보아야 한다.


MessageService 생성

MessageService를  src/ap 안에 CLI을 사용해서 생성한다. --module=app 옵션은 CLI이 AppModule안에 서비스를 제공하기위함을  알려준다.

ng generate service message --module=app

MessageService 열고 다음처럼 내용을 변경한다.


/src/app/message.service.ts

import { Injectable } from '@angular/core';


@Injectable()

export class MessageService {

  messages: string[] = [];


  add(message: string) {

    this.messages.push(message);

  }


  clear() {

    this.messages = [];

  }

}


서비스 캐쉬된 메세지와 두개위 모듈을 드러낸다.  메세지가 캐시로된 add()와 다른 캐시에 그캐시를 clear()한다.


HeroService에 주입하기.

HeroService 다시열고 MessageService를 Import 한다.

/src/app/hero.service.ts (import MessageService)

import { MessageService } from './message.service';

선언한 private messageService 속성 생성자와 파라메터를 수정한다. HeroService가 생성될때  앵귤라는 싱글톤 MessageService를 그 속성에  주입할수 있다.

constructor(private messageService: MessageService) { }

전형적인 "service-in-service" 시나리오 : MessageService를 HeroService에  주입한는것은  HeroesComponent에 주입된것이다.


HeroService에서 메세지 보내기

getHeroes 메소드로 메세지를 보낼때 영웅을 가져오게  수정한다.

getHeroes(): Observable<Hero[]> {

  // Todo: send the message _after_ fetching the heroes

  this.messageService.add('HeroService: fetched heroes');

  return of(HEROES);

}


HeroService에서 메세지를 표시

MessagesComponent는 모든 메세지를 표시 해야하며, 영웅들을 가져올때 포함된는 그 메세지는 HeroService로 보낸다.

MessagesComponent을 열고 MessageService를 import 한다.

/src/app/messages/messages.component.ts (import MessageService)

import { MessageService } from '../message.service';

생성자를 파라메터와 함깨  선언한 공개  messageService 속성을 수정한다. 앵귤라는 HeroService가 생성될때 싱글톤 MessageService를 그 속성에  주입할수 있다.

constructor(public messageService: MessageService) {}

messageService 속성은 public 으로 해야만 하는 이유는 이 템플릿에서 바인딩하기 때문이다.

앵귤라 단순히 public 된 컴포넌트 속성을 바인드한다.


MessageService 바인드

CLI은 자동생성된  MessagesComponent 템플릿을 다음처럼 변경한다. 

src/app/messages/messages.component.html

<div *ngIf="messageService.messages.length">

  <h2>Messages</h2>

  <button class="clear"

          (click)="messageService.clear()">clear</button>

  <div *ngFor='let message of messageService.messages'> {{message}} </div>

</div>

템플릿은 컴포넌트의 messageService로 직접 바인된다. 

*ngIf은 단지 메세지가 표시될때면 메세지 영역에 표시 하기만 한다. 

*ngFor 는 <div> 요소가 반복속에  메세지의 목록을 부여한다.

Angular는 버튼의 클릭 이벤트를 MessageService.clear()로 이벤트 구속력 있게 바인드한다.

메세지는 사적인 CSS 스타일을  messages.component.css에 추가 했을때 더 좋아 보일수 있다. 

브라우저가 재생되고 영웅들의 목록이 페이지에서 표시된다.  아래로 스크롤릴  하면  메세지 영역안의 HeroService에서 온 메세지를 본다 . "clear"버튼을 크릭하면 메세지 영역을 사라지게 한다.


요약

  • 리팩토리된 HeroService 클래스로 데이터를 접근한다.
  • 제공된어진 HeroService는 root AppModule에서 어디에서나 주입될수 있다.
  • 컴포넌트 안으로 주입하는것을 Angular Dependency Injection을  사용 한다.
  • HeroService get data 메소드에 비동기 특성을 지정 했다.
  •  Observable 과 RxJS Observable library를 발견 했다.
  • 가짜 영웅이 Observable이 리턴되는  RxJS of() 사용했다.
  • 콤포넌트의 라이프사이클 hook는 생성자가 아니고 ngOnInit에서    HeroService 메소드를 호출한다. 
  • 클래스 사이의 약결합 통신인  MessageService 생성했다.
  • HeroService는 컴포넌트에  주입되어진다.다른 주입된 서비스는 MessageService 로 생성된다.


반응형
반응형


서비스

영웅 관광 HeroesComponent는 현재 얻은 가짜 데이터를 표시 한다.

이 튜토리얼에서 다음의 리팩토링은 HeroesComponent은 뷰를 지원함에 있어 선택과 집중 할수 있다. 또한 목 서비스와 같이 단위테스트 쉽게 되게 할수 있다.


왜 서비스냐?

컴포넌트는  데이터를 직접가저오거나 바로 저장 하면 안된고 현재에 틀림없이 가짜 데이터라는것을 알고도 하면 안된다. 데이터 프리젠팅, 데이터 접근을 서비스로 위함하는데 집중해야 한다.

이 튜토리얼에서 모든 애플리케이션 클래스들이 영웅을 얻을 때 사용 할수 있는 HeroService 만들수 있다. 새로운 서비스를 만들는거 대신에,  HeroesComponent 생성자로 주입하는 앵귤라 의존성 주입에 의지 할 수 있다.

서비스는 알수없는 서로 다른 것을 클래스 사이에서 정보를 공유하기 좋은 방법이다. MessageService를 생성할수 있고 그것을 두곳에 주입 합니다.

HeroService에서는 서비스를 사용하여 메세지를 보낸다.

MessagesComponent에서는 메세지를 표시한다.


HeroService 생성

Angular CLI을 사용해 영웅을 호출하는 서비스를 생성합니다.

ng generate service hero

그 명령은 src/app/hero.service.ts 안에 HeroService 클래스의 뼈대를 자동생성합니다. 


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

  create src/app/hero.service.spec.ts (362 bytes)

  create src/app/hero.service.ts (110 bytes)

서비스는 테스트과 기능의 ts 파일 두개만 생성 하네요.


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

import { Injectable } from '@angular/core';

@Injectable()

export class HeroService {

  constructor() { }

}

@Injectable() 서비스

새로운 서비스는 앵귤라 Injectable 기호를 import 하고  @Injectable() 데코레이터를 클래스 옆에 애노테이트한것을 알수 있다.

@Injectable() 데코레이터는 Angular에게 이 서비스는 아마 그 자신은 주입된 의존성을 가진다라고 알려준다.  이것은 지금 의존성을 가지고 있지 않지만 곧 갖게 될것이다.  할수 있는지 혹은 할수 없든 decorator를 유지하는 것이 좋습니다.

앵귤라 스타일 가이드라인을 지키도록 강하게 권고하고 부스러기 린터는 그 룰대로 집행합니다.


영웅 데이터 얻기

HeroService 영웅 데이터는 웹서비스,  local storage, 혹은 가짜 데이터 중 어디서나 얻울수 있다.

콤포넌트에서 데이터 접근을 제거 하는것은 변할수 있는 너의 마음에 대해 언제든지, 어떤 컴포넌트의 간섭 없이 구현하는것을 의미한다. 그들은 서비스가 어떻게 일하는지 알수 없다.

이 튜토리얼 안에서 가짜 영웅들을 제공 하는것을 구현 할수 있다.


Her 와 HEROES Import

import { Hero } from './hero';

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

mock 영웅을 리턴하는 getHeroes 메소드를 추가한다.

getHeroes(): Hero[] {

  return HEROES;

}


HeroService 제공

의존성 주입 시스템에 HeroService를 제공해야  앵귤라가 HeroesComponent에 HeroService를 주입 할수 있다.

AppModule에 AppComponent안의 HeroesComponent에서 HeroService를 제공하는데에 몇몇방법이 있다. 각각 선택은 장단점을 가진다.

이 튜토리얼은 AppModule에서 제공하는것을 원한다.

앞서말한 인기있는 선택은 당신이 CLI에 전할수 있는 선택자 이다.  --module=app 추가하면 자동적으로 

ng generate service hero --module=app


AppModule 클래스를 열고, HeroService를 import 하고 , @NgModule.providers 배열을 추가한다.

src/app/app.module.ts (providers)

providers: [

    HeroService,

/* . . . */

  ],

providers 배열은 HeroService의 인스턴스를 공유하고, 그것을 뭍는 어떤 클래스로 주입하도록 앵귤라가 지시한다.

HeroService는   HeroesComponent에  지금 연결될 준비가 됬습니다.

이것은 잠정적인 코드 샘플이다. HeroService를 제공하고 사용하는것을 허락한다.  이쯤에서,  "최종 코드리뷰"안에  HeroService에서 코드는  다른 것이다.

Providers 가이드에서 제공자에 대하여 더 배웁니다.


HeroesComponent 업데이트

HeroesComponent 클래스 파일을 열어라.

더이상 원하지 않은 HEROES import를 삭제한다. 대신에 HeroService를 포함 합니다.

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

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

영웅 속성의 정의를 간단 선언으로 변경합니다.

heroes: Hero[];


HeroService 주입

생성자에 HeroService 타입의 개인 heroService 파라메터를 추가합니다.

constructor(private heroService: HeroService) { }

개인 heroService 속성을  파라메터는 동시에 정의되고 HeroService 주입 위치를 확인한다.

HeroesComponent를 앵규라가 생성할때, 의존성 주입 시스템은 heroService 파라메터를 HeroService의 싱글톤 인스턴스로 설정된다.


getHeroes() 추가

함수를 서비스에서 영웅들을 되찾아오는 것을 생성합니다.

getHeroes(): void {

  this.heroes = this.heroService.getHeroes();

}


ngOnInit 에서 요청

생성자에서 getHeroes() 실행 할때, 좋은 엽습이 아니다.

속성에 와이어 링 생성자 매개 변수와 같은 간단한 초기화를 위해 생성자를 예약하십시오. 생성자는 아무 것도해서는 안됩니다. 실제 데이터 서비스처럼 원격 서버에 HTTP 요청을하는 함수를 호출해서는 안됩니다.

대신에 getHeroes()를 ngOnInit 라이프사이틀 후크 내에서 호출하고, HeroesComponent 인스턴스를 생성한후 적절한 시점에 앵귤라는 ngOnInit를 호출 합니다.

ngOnInit() {

  this.getHeroes();

}


실행 보기

브라우저가 새로 고침되면 앱이 이전과 같이 실행되어 영웅의 목록과 영웅 이름을 클릭하면 영웅 상세보기가 표시됩니다.

반응형
반응형

주 / 상세 콤포넌트

현재  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를 동작시킨다

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


반응형
반응형


Angular CLI 설치


Angular CLI 설치 , 이미 했으면 안해도 된다.

npm install -g @angular/cli

이미 설치 했는데 진행해 보았다.

먼가 추가되고 업데이트 되고 지워지면서 업데이트 되는것 처럼 보였다.


.......

+ @angular/cli@1.7.4

added 179 packages, removed 272 packages, updated 134 packages and moved 14 packages in 45.039s



새로운 앱 생성


CLI 명령을 통해 새로운 프로젝트를 angular-tour-of-heroes 라는 이름으로 생성한다.

ng new angular-tour-of-heroes

Angular CLI 기본적인 앱과 지원하는 파일들과 같이 새로운 프로젝트가 자동생성된다.


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

  create angular-tour-of-heroes/e2e/app.e2e-spec.ts (304 bytes)

  create angular-tour-of-heroes/e2e/app.po.ts (208 bytes)

  create angular-tour-of-heroes/e2e/tsconfig.e2e.json (235 bytes)

  create angular-tour-of-heroes/karma.conf.js (923 bytes)

  create angular-tour-of-heroes/package.json (1307 bytes)

  create angular-tour-of-heroes/protractor.conf.js (722 bytes)

  create angular-tour-of-heroes/README.md (1035 bytes)

  create angular-tour-of-heroes/tsconfig.json (363 bytes)

  create angular-tour-of-heroes/tslint.json (3012 bytes)

  create angular-tour-of-heroes/.angular-cli.json (1257 bytes)

  create angular-tour-of-heroes/.editorconfig (245 bytes)

  create angular-tour-of-heroes/.gitignore (544 bytes)

  create angular-tour-of-heroes/src/assets/.gitkeep (0 bytes)

  create angular-tour-of-heroes/src/environments/environment.prod.ts (51 bytes)

  create angular-tour-of-heroes/src/environments/environment.ts (387 bytes)

  create angular-tour-of-heroes/src/favicon.ico (5430 bytes)

  create angular-tour-of-heroes/src/index.html (306 bytes)

  create angular-tour-of-heroes/src/main.ts (370 bytes)

  create angular-tour-of-heroes/src/polyfills.ts (3114 bytes)

  create angular-tour-of-heroes/src/styles.css (80 bytes)

  create angular-tour-of-heroes/src/test.ts (642 bytes)

  create angular-tour-of-heroes/src/tsconfig.app.json (211 bytes)

  create angular-tour-of-heroes/src/tsconfig.spec.json (283 bytes)

  create angular-tour-of-heroes/src/typings.d.ts (104 bytes)

  create angular-tour-of-heroes/src/app/app.module.ts (316 bytes)

  create angular-tour-of-heroes/src/app/app.component.html (1141 bytes)

  create angular-tour-of-heroes/src/app/app.component.spec.ts (986 bytes)

  create angular-tour-of-heroes/src/app/app.component.ts (207 bytes)

  create angular-tour-of-heroes/src/app/app.component.css (0 bytes)

npm WARN deprecated nodemailer@2.7.2: All versions below 4.0.1 of Nodemailer are deprecated. See https://nodemailer.com/status/

npm WARN deprecated mailcomposer@4.0.1: This project is unmaintained

npm WARN deprecated socks@1.1.9: If using 2.x branch, please upgrade to at least 2.1.6 to avoid a serious bug with socket data flow and an import issue introduced in 2.1.0

npm WARN deprecated node-uuid@1.4.8: Use uuid module instead

npm WARN deprecated buildmail@4.0.1: This project is unmaintained

npm WARN deprecated socks@1.1.10: If using 2.x branch, please upgrade to at least 2.1.6 to avoid a serious bug with socket data flow and an import issue introduced in 2.1.0


> uws@9.14.0 install D:\workspace\dev\angular02\angular-tour-of-heroes\node_modules\uws

> node-gyp rebuild > build_log.txt 2>&1 || exit 0



> node-sass@4.8.3 install D:\workspace\dev\angular02\angular-tour-of-heroes\node_modules\node-sass

> node scripts/install.js


Cached binary found at C:\Users\oldma\AppData\Roaming\npm-cache\node-sass\4.8.3\win32-x64-57_binding.node


> uglifyjs-webpack-plugin@0.4.6 postinstall D:\workspace\dev\angular02\angular-tour-of-heroes\node_modules\webpack\node_modules\uglifyjs-webpack-plugin

> node lib/post_install.js



> node-sass@4.8.3 postinstall D:\workspace\dev\angular02\angular-tour-of-heroes\node_modules\node-sass

> node scripts/build.js


Binary found at D:\workspace\dev\angular02\angular-tour-of-heroes\node_modules\node-sass\vendor\win32-x64-57\binding.node

Testing binary

Binary is fine

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

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


added 1382 packages in 154.975s

Project 'angular-tour-of-heroes' successfully created.


맨아래 성공적으로 프로젝트가 만들어졌다고 한다.

기본파일처음에 생성하고 deprecated 에 대한 경고 나오는거 보니 라이브러리의 의존성 체크도 하나보다.

노드관련 모듈 머시기 설치하고 빌드하고 하는것 같고 ( 노드도 배우얄 탠데)


애플리케이션 서버

프로젝트 디렉토리로 이동하고 앱을 시작합니다.

cd angular-tour-of-heroes

ng serve --open

ng serve 명령어는 앱을 빌드, 개발서버에서 시작, 소스 파일을 봐주고, 파일을 수정해서 앱을 리빌드한다.

--open 플래그는 브라우저를 http://localhost:4200/로 열어준다.

브라우저에서 앱이 실행되는것이 보인다.


PS D:\workspace\dev\angular02\angular-tour-of-heroes> ng serve --open

** NG Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **

 10% building modules 7/11 modules 4 active ...our-of-heroes\node_modules\url\url.jswebpack: wait until bundle finished: /   Date: 2018-04-06T00:33:28.449ZHash: 4802a752f2c2aa4cfe70

Time: 6533ms

chunk {inline} inline.bundle.js (inline) 3.85 kB [entry] [rendered]

chunk {main} main.bundle.js (main) 18 kB [initial] [rendered]

chunk {polyfills} polyfills.bundle.js (polyfills) 554 kB [initial] [rendered]

chunk {styles} styles.bundle.js (styles) 41.5 kB [initial] [rendered]

chunk {vendor} vendor.bundle.js (vendor) 7.42 MB [initial] [rendered]


webpack: Compiled successfully.


브라우저(IE) 띄어지고 자동으로 http://localhost:4200/ 경로가 열렸다.

브라우저는 윈도우 기본브라우저가 열리는듯 한다. 브라우저 선택도 어딘가에 설정이 있을듯 한데..

자동으로 열림 IE 에서 화면에 아무것도 보여지지 않고, 크롬에서 열어야 기본 앵귤러 화면이 보인다.


Angular components

페이지에서 애플리케이션 shell이 보인다. shell은 AppComponent라는 Angular component에 의해서 제어 된다.

콤포넌트는 앵귤러 애플러케이션의 기본 구성요소다. 화면에 데이터를 표시하고, 사용자의 입력을 감지하고, 그리고 입력에의 기본조치를 한다.


애플리케이션 제목 변경

즐겨쓰는 에디터나 IDE로 프로젝트를 열고 src/app 폴더를 찾는다.

3개의 파일로 배포된  shll AppComponent 의구현된 것을 확인 할수 있다.


  1. app.component.ts - component 클래스 코드, TypeScript로 작성된다.
  2. app.component.html - component template, html 로 작성된다.
  3. app.component.css - 콤퍼넌트 만의 CSS 스타일이다.


component class file(app.component.ts)을 열고 title 속성의 값을 'Tour of Heroes'로 변경  합니다.

title = 'Tour of Heroes';


component template file (app.component.html) 을 열고 Angular CLI로 기본 템플릿으로 만들어진 것을 삭제 합니다. 다음의 HTML 라인안에 것으로 교체 합니다.

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


이중 중괄호 {{ }} 는 엥귤라를 써넣는 바인딩 문법이다.  title 의 속성 값이  html header 태그 내부에 써넣어진 바인딩 된다.

브라우저 재생되고 새로운 앱 제목이 보여진다. 

(터미널에 보니 알아서 번들링 하면 브라우저가 알아서 변경된 내용으로 보여진다. 변경된 코드를 수동 재배포 하거나 브라우저에서 새로고침이 없어도 갱신된 소스로 반영이된다. )


애플리케이션 스타일 추가

대부분의 애플리케이션은 애플리케이션에서 일관된 모양을 유지 하려고 합니다.

CLI은 일관된 모양을 유지하기 위해 빈 styles.css를 자동생성 했다.  앱의 전체 스타일을 여기에 배치 해라.


/* Application-wide Styles */

h1 {

  color: #369;

  font-family: Arial, Helvetica, sans-serif;

  font-size: 250%;

}

h2, h3 {

  color: #444;

  font-family: Arial, Helvetica, sans-serif;

  font-weight: lighter;

}

body {

  margin: 2em;

}

body, input[text], button {

  color: #888;

  font-family: Cambria, Georgia;

}

/* everywhere else */

* {

  font-family: Arial, Helvetica, sans-serif;

}

Summary

Angular CLI을 사용해서 초기 앱 구조를 생성한다.

Angular components 데이터 표시하는것을 배웠다.

앱제목을 표시할때 이중 중괄호를 사용해서 써넣는다

반응형

+ Recent posts