Angular.io 에서 제공하는 튜토리얼을 정리한 것입니다.
사진 클릭시 튜토리얼로 이동
!! RC4기준 !!
7. Http
1. Providing HTTP Services
이번 장에서는 원격 서버의 웹 API에 대응하는 HTTP 호출을 하는 응용 프로그램을 만들것이다.
HTTP는 core Angular module이 아니다. npm package를 통해서 @angular/http 라는 add-on module로 제공된다. 튜토리얼을 따라왔다면 우리는 systemjs.config에서 필요한 라이브러리들을 이미 다운 받았다.
HTTP를 어디에서나 엑세스하기 위하여 main.ts 의 bootstrap에 등록한다. HTTP_PROVIDERS를 bootstrap의 2번째 파라미터로 등록하는 것은 @Component의 providers 배열과 같은 효과를 나타낸다.
현재 요청을 처리할 수 있는 web server가 없다. 그래서 우리는 가짜 서버를 사용할 것이다. mock service에서 가져오고 저장하는 것을 in-memory web API를 이용하여 HTTP 클라이언트를 속일 것이다. mock-heroes.ts 를 삭제하고 임의의 데이터를 가질 in-memory-data.service.ts 생성하자.
< main.ts >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // Import for loading & comfiguring the in-memory web api import {XHRBackend} from '@angular/http'; import {InMemoryBackendService, SEED_DATA} from 'angular2-in-memory-web-api'; import {InMemoryDataService} from './in-memory-data.service'; // The usual bootstrapping imports import { bootstrap } from '@angular/platform-browser-dynamic'; import {HTTP_PROVIDERS} from '@angular/http'; import { AppComponent } from './app.component'; import { appRouterProviders } from './app.routes'; bootstrap(AppComponent,[ appRouterProviders, HTTP_PROVIDERS, {provide :XHRBackend, useClass:InMemoryBackendService}, {provide : SEED_DATA, useClass:InMemoryDataService} ]).catch(err => console.error(err)); | cs |
< in-memory-data.service.ts >
1 2 3 4 5 6 7 8 9 10 11 12 | 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'} ]; | cs |
< hero.service.ts >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | import { Injectable } from '@angular/core'; import {Http, Headers} from "@angular/http"; import 'rxjs/add/operator/toPromise'; import { Hero } from './hero'; @Injectable() export class HeroService { private heroesUrl='app/heroes';// URL to web api constructor(private http: Http){ } getHeroes() { return this.http.get(this.heroesUrl).toPromise() .then(response=>response.json().data as Hero[]) .catch(this.handleError); } getHero(id: number){ return this.getHeroes() .then(heroes=>heroes.find(hero=>hero.id===id)); } private handleError(error:any){ console.error('An error occurred', error); return Promise.reject(error.message||error); } } | cs |
[line 15 - 20] 비동기 작업을하기 위하여 hero.service.ts의 getHeroes()가 HTTP를 사용하도록 변경한다. 우리는 여전히 Promise를 반환하지만 getHeroes()를 호출하였을 때 어떠한 컴포넌트도 업데이트 할 필요가 없다.
앵귤러의 http.get은 RxJS의 Observable을 반환한다. Observable은 비동기 데이터 흐름을 관리 할 수 있는 강력한 방법이다.
[출처] $scope.$watch에 RxJS 적용하기|작성자 쫌조
[출처] $scope.$watch에 RxJS 적용하기|작성자 쫌조
2. hero.service.ts
HeroService에 히어로를 post,put 그리고 delete하는 메소드 추가.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | import 'rxjs/add/operator/toPromise'; import { Hero } from './hero'; @Injectable() export class HeroService { private heroesUrl='app/heroes';// URL to web api constructor(private http: Http){ } getHeroes() { return this.http.get(this.heroesUrl). toPromise(). then(response=> response.json().data as Hero[]) .catch(this.handleError); } getHero(id: number){ return this.getHeroes() .then(heroes=>heroes.find(hero=>hero.id===id)); } save(hero: Hero): Promise<Hero>{ if(hero.id){ return this.put(hero); } return this.post(hero); } delete(hero:Hero){ let headers=new Headers(); headers.append('Content-Type','application/json'); let url=`${this.heroesUrl}/${hero.id}`; return this.http .delete(url,{headers:headers}) .toPromise().catch(this.handleError); } // Add new Hero private post(hero: Hero): Promise<Hero>{ let headers = new Headers({ 'Content-Type' : 'application/json' }); return this.http.post(this.heroesUrl,JSON.stringify(hero),{headers:headers}) .toPromise().then(res=> res.json().data).catch(this.handleError); } // Update existing Hero private put(hero: Hero){ let headers = new Headers(); headers.append('Content-Type','application/json'); let url=`${this.heroesUrl}/${hero.id}`; return this.http.put(url,JSON.stringify(hero),{headers:headers}) .toPromise().then(()=>hero).catch(this.handleError); } private handleError(error:any){ console.error('An error occurred', error); return Promise.reject(error.message||error); } } | cs |
Post
새로운 히어로를 추가하기 위해 post 사용. header 생성, content type을 application/json으로 셋팅한다. 히어로 객체를 String으로 변환하여 Post하기 전에 JSON.stringify를 호출한다.
Put
hero 각각을 수정하기 위해 사용. Post request와 구조 비슷하다.
유일한 차이점은 우리가 업데이트 할 히어로의 ID를 추가하여 URL을 살짝 변경해야 할 것이다.
Delete
put과 이름만 다른뿐 형식은 같다.
Save
Post, put을 합칠 것이다. public API를 통해서 쉽게 합칠 수 있다.
HeroService는 hero의 상태를 통해 호출할 메소드를 결정한다. 히어로가 이미 ID를 가지고 있다면 수정을 하고 그렇지 않다면 히어로를 추가한다.
!!주의!!
delete,sava의 url 작성할때 엔터옆의 ' 가 아니라 탭 위의 ` 임!
Apostrophe (어퍼스트로피) Grave (그레이브), back quator(백쿼터)
3. hero-detail.component.ts
히어로를 더하고 삭제하는 메소드를 사용하기 위해 컴포넌트를 수정
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | import {Component, OnInit, OnDestroy, Input, Output, EventEmitter} from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Hero } from './hero'; import { HeroService } from './hero.service'; @Component({ selector: 'my-hero-detail', templateUrl: 'app/hero-detail.component.html', styleUrls:['app/hero-detail.component.css'] }) export class HeroDetailComponent implements OnInit, OnDestroy { @Input() hero: Hero; @Output() close= new EventEmitter(); error : any; sub: any; navigated = false;// true if navigated here constructor( private heroService: HeroService, private route: ActivatedRoute) { } ngOnInit() { this.sub = this.route.params.subscribe(params => { if(params['id']!==undefined){ let id = +params['id']; this.navigated=true; this.heroService.getHero(id) .then(hero => this.hero = hero); }else{ this.navigated=false; this.hero=new Hero(); } }); } save(){ this.heroService.save(this.hero).then(hero=>{ this.hero=hero; this.goBack(hero); }).catch(error=> this.error=error); } ngOnDestroy() { this.sub.unsubscribe(); } goBack(saveHero:Hero=null) { this.close.emit(saveHero); if(this.navigated){ window.history.back(); } } } | cs |
[line 26 - 34] Add, Delete를 구별하기 위해 URL에 ID가 있는지를 체크하는 부분을 추가하였다. ID가 없으면 빈 히어로 객체에 HeroDetailComponent를 바인딩한다. UI를 통해 이루어진 수정 사항은 동일한 hero property에 다시 바운딩된다.
[line 60 - 65] goBack()은 히어로를 저장한 후 다시 이전 페이지로 redirect 한다.
[line 61] emit : HeroDetailComponent 와 HeroesComponent 사이의 handshake
2. HTML 수정
- hero-detail.component.html의 Back 버튼 옆에 Sava 버튼 만들기
<button (click)="save()">Save</button>
- heroes.component.html의 히어로(</li> 태그 앞에) Delete 버튼 만들기
<button class="delete-button" (click)="deleteHero(hero, $event)">Delete</button>
- heroes.component.html의 히어로 리스트(</ul>태그 뒤에)에 추가하기
- 첫번째 라인은 에러가 있을 경우 에러 메세지 표시, 나머지 부분은 히어로 추가
<div class="error" *ngIf="error">{{error}}</div>
<button (click)="addHero()">Add New Hero</button>
<div *ngIf="addingHero">
<my-hero-detail (close)="close($event)"></my-hero-detail>
</div>
!!주의!!
*ngIf 할때 *ngif로 작성했는데 오류를 잡아주지 않았다. 어디에도 오류가 없는데 브라우저에 띄워지지 않아서 열심히 해맴(사용하던 툴의 문제였는지 / WebStorm 사용중)
[ Angular 2.0에서 $http를 대체하는 것 ]
http://stackoverflow.com/questions/29361027/angularjs-2-alternative-http
Browser에서 Angular를 Loading 하는 과정
'ANgularJS 2.0+' 카테고리의 다른 글
[ANgular 2.0] Tutorial rc.5 migration (0) | 2016.09.29 |
---|---|
[angularJS 2.0] @ViewChild,Dependency Injection (0) | 2016.09.21 |
[AngularJS 2.0] AngularJS 2.0 Tutorial3_routing (0) | 2016.09.19 |
[AngularJS 2.0] AngularJS 2.0 Tutorial2_service (0) | 2016.09.19 |
[AngularJS 2.0] AngularJS 2.0 Tutorial1 (0) | 2016.09.19 |