Angular4入門_Vol.16
参考サイト
https://www.codingforentrepreneurs.com/projects/try-angular-v4/
Video Service
app/videos フォルダを作成する
$ ng g service videos
で、serviceの雛形を作成する。videos.service.tsと、videos.service.spec.tsを、app/videosフォルダに移動させる。
videos.service.tsにコードを追記する。
import { Injectable } from '@angular/core'; import { Http } from '@angular/http'; // Httpをimportする import 'rxjs/add/operator/map'; // mapをimportする。 import 'rxjs/add/operator/catch'; //catchをimportする。 const endpoint = 'assets/json/videos.json' // yourdomain.com/api/videos // constは変更不可な変数 @Injectable() export class VideoService { constructor(private http: Http) { } // httpに、Httpclassを定義する。 list(){ //list methodを使う(getでも可と言っていた) return this.http.get(endpoint) //httpのgetメソッドで、endpointにアクセスする。 .map(response=>response.json()) //responseをjsonデータに変換して、mappingする。 .catch(this.handleError) // handleErrorをcatchする。 } private handleError(error:any, caught:any): any{ //handleErrorを定義しておく。 console.log(error, caught) } }
- video-list.component.ts に、VideoServiceをimportして使ってみる。
import { Component, OnInit, OnDestroy } from '@angular/core'; import { Http } from '@angular/http'; import { VideoService } from '../videos/videos.service'; //importする @Component({ selector: 'video-list', templateUrl: './video-list.component.html', styleUrls: ['./video-list.component.css'], providers: [VideoService] // providersとして、VideoServiceを定義しておく。 }) export class VideoListComponent implements OnInit { private req:any; title = "Video List!"; someItem = "<h1>HiHi</h1>"; todayDate; videoList: [any]; constructor(private http:Http, private _video:VideoService) { } // _videoを、VideoService classとして定義する。 ngOnInit() { this.todayDate = new Date(); this.req = this._video.list().subscribe(data=>{ // console.log(data) // data.json() は、すでにservice側で処理済みのため外す // this.req = this.http.get('assets/json/videos.json').subscribe(data=>{ // console.log(data.json()) this.videoList = data as [any]; // data.json() は、すでにservice側で処理済みのため外す }); // } ngOnDestroy(){ this.req.unsubscribe() } getEmbedUrl(item){ return 'https://www.youtube.com/embed/' + item.embed; } }
4200/vidoesにアクセスする。。。無事表示された!
これで、video-listのhttpメソッドは不要になったので、削除しておく。
import { Component, OnInit, OnDestroy } from '@angular/core'; // OnDestroyをimportする // httpのimportを削除 import { VideoService } from '../videos/videos.service'; @Component({ selector: 'video-list', templateUrl: './video-list.component.html', styleUrls: ['./video-list.component.css'], providers: [VideoService] }) export class VideoListComponent implements OnInit { private req:any; // req のアノテーション title = "Video List!"; someItem = "<h1>HiHi</h1>"; todayDate; videoList: [any]; constructor(private _video:VideoService) { } //Httpを削除 ngOnInit() { this.todayDate = new Date(); this.req = this._video.list().subscribe(data=>{ console.log(data) this.videoList = data as [any]; }); // } ngOnDestroy(){ this.req.unsubscribe() } getEmbedUrl(item){ return 'https://www.youtube.com/embed/' + item.embed; } }
- video-detailにも同様に、VideoServiceをimportする。
import { Component, OnInit, OnDestroy } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Http } from '@angular/http'; import { VideoService } from '../videos/videos.service'; // VideoServiceをimportする @Component({ selector: 'video-detail', templateUrl: './video-detail.component.html', styleUrls: ['./video-detail.component.css'], providers:[VideoService] // VideoServiceをprovidersとして定義する }) export class VideoDetailComponent implements OnInit, OnDestroy { private routeSub:any; private req:any; // video = { // name: "Default", // slug: "item-1", // embed: "kzjMI00A1f8" // }; video:any; slug: string; // _vidoeをVideoServiceとして定義する constructor(private route: ActivatedRoute, private http: Http, private _video:VideoService) { } ngOnInit() { this.routeSub = this.route.params.subscribe(params => { console.log(params) this.slug = params['slug'] this.req = this._video.list().subscribe(data=>{ //http.getmethodの箇所を_video.list()に修正する data.filter(item=>{ // data.json()のjson()変換は不要なので、削除する。 // console.log(item) if (item.slug == this.slug){ this.video = item } }) }) }) } ngOnDestroy(){ this.routeSub.unsubscribe() this.req.unsubscribe() } }
- videos.service.tsに、get メソッドを追加する。
import { Injectable } from '@angular/core'; import { Http } from '@angular/http'; import 'rxjs/add/operator/map'; import 'rxjs/add/operator/catch'; const endpoint = 'assets/json/videos.json' // yourdomain.com/api/videos @Injectable() export class VideoService { constructor(private http: Http) { } list(){ return this.http.get(endpoint) .map(response=>response.json()) .catch(this.handleError) } // get methodを定義する get(slug){ // slugを引数に取る return this.http.get(endpoint) // endpointにアクセスする .map(response=>{ let data = response.json().filter(item=>{ //responseをjsonデータに変換、item.slug = 引数のslugが一致した場合に、itemを返すfilterを作成する if (item.slug == slug ){ return item } }) console.log(data) // dataをlogに表示する return data // get(slug)の結果としてdataを戻り値として返す }) .catch(this.handleError) } private handleError(error:any, caught:any): any{ console.log(error, caught) } }
- video-detailに, get methodを追記する。
... ngOnInit() { this.routeSub = this.route.params.subscribe(params => { // console.log(params) this.slug = params['slug'] this.req = this._video.get(this.slug).subscribe(data=>{ // getメソッドに引数のslugを渡す this.video = data // video変数に、dataを渡す (ここのdataは、videos.serviceのlet dataで定義したdata) // data.filter(item=>{ // // console.log(item) // if (item.slug == this.slug){ // this.video = item // } // }) }) }) } ...
- video-detailページにアクセスしてみる。。。errorは発生してないが、何も表示されない。これは、data がarrayで渡されており、単体のobjectとして渡されていないことが原因の模様。なので, videos.service.tsのget methodを修正する。
get(slug){ return this.http.get(endpoint) .map(response=>{ let data = response.json().filter(item=>{ if (item.slug == slug ){ return item } }) console.log(data) if (data.length == 1){ return data[0] } return {} }) .catch(this.handleError) }
video-detailのページにアクセスしてみる。。。無事表示された!(^^)そしてdataに無いitemは表示されないことも確認。
最後に、video-detail.component.ts からHttpを削除しておく。
まとめ
- angularのserviceは、apiなどにアクセスして、データを取得 と そのデータをcomponentに渡すという役割がある模様
- componentで、serviceからデータを受け取る場合は、providers:[service名]で指定する。
- detailpageのようなcomponentにも、serviceで取得してfileterをかけてデータをcomponentに渡す方がよさそう。
- mapとcatch が基本セットになるのかも