Djangoroidの奮闘記

python,django,angularJS1~三十路過ぎたプログラマーの奮闘記

Angular4 + Django1.11 vol.2

参考サイト

https://www.codingforentrepreneurs.com/projects/angular-django/

ng build to Django Static

  • $ ng build --prod で、production versionのbuildを実行する。(結果、bundle.jsが作成される)

  • buildしたファイルの出力先をdjangoのstaciファイル内にする。

$ ng build --prod --output-path /Users/yassy/Desktop/djangular4/backend/src/static/ang --watch --output-hashing none

// --output-path で出力先を指定
// --watchで、変更があったら自動でbuildするモードに切り替えられる
// --output-hashing none で、build時に作成されるhashをなくすことができる。
  • deploy.sh ファイルを作成する。deployのときに実行するコマンドを記載する。
ng build --prod --output-path /Users/yassy/Desktop/djangular4/backend/src/static/ang --watch --output-hashing none

Django render Angular

  • static/ang/index.htmlをang_home.htmlにコピペする。さらに、load staticで、django templateで、staticfileを読み込むようにする。
{% load static %}
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Srvup</title>
  <base href="/">

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
      <!-- index.html -->
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="{% static 'ang/styles.bundle.css' %}" rel="stylesheet"/></head>
<body>
  <app-root>Loading...</app-root>
  <script type="text/javascript" src="{% static 'ang/inline.bundle.js' %}"></script>
  <script type="text/javascript" src="{% static 'ang/polyfills.bundle.js' %}"></script>
  <script type="text/javascript" src="{% static 'ang/vendor.bundle.js' %}"></script>
  <script type="text/javascript" src="{% static 'ang/main.bundle.js' %}"></script>
</body>
</html>
  • 無事表示された!しかし、image ファイルなどが読み込まれていない。。。ang/assets/videos.jsonの中のpathがdjangoと一致してないのが原因ぽい。なので、そのimage のpathを修正する。
    {
     "name": "Welcome",
     "slug": "item-1",
     "embed": "1hyjLD7pk10",
     "image": "/static/ang/assets/images/nature/4.jpg",
     "featured": true
    }
    ...
  • 次に、vides.service.ts内のendpoint(videos.jsonにアクセスするpath)も修正する。
const endpoint = '/static/ang/assets/json/videos.json'
  • http://127.0.0.1:8000/にアクセスしてみる。。。error!というか画像が表示されない。。。原因は、videos.jsonをbuildあとのファイル(angフォルダ以下のファイル)を修正していたため、buildが実行されるたびに、上書きされていたためだった(^^; なので、client/assets/json/videos.jsonの方のpathを修正して再度buildする。

  • 無事表示された(^^)

  • home.component.tsの、defaultImageのpathも修正する。

videoListDefaultImage = '/static/ang/assets/images/videos/1.jpg'

まとめ

  • angularアプリと、djangoアプリは別物と考える。
  • angularアプリ作成→build→deployという流れだが、django backendに使うときは、angularアプリ作成→build output→outputしたファイルを、djangoに静的ファイルとして扱わせる という流れになるのか。
  • build –watch にしておくことで、常にtranscompile + outputされる。そのため、基本的に、django側にoutputされたファイルを修正ではなく、angularアプリの方のファイルを修正する(djangoは、angularアプリからoutputされるファイルを扱うという役割)

Angular4 + Django1.11 vol.1

参考サイト

https://www.codingforentrepreneurs.com/projects/angular-django/

Getting Started Angular4+Django

  • djangular4というフォルダを作成する。さらに、djangular4/client, djangular4/backend というサイトをそれぞれ作成する。

  • clientフォルダに、joincfe/github のtry-angular-v4をgit cloneしてくる。

$ git clone https://github.com/codingforentrepreneurs/Try-Angular-v4.git .
// ここのピリオドがポイント

$ npm install
// package.jsonに記載があるmoduleをinstallする。

$ ng serve
// angularのコードをtranscompileして、localserverを起動する。

$ rm -rf .git
// gitのrepositoryを削除する
  • djangoの環境を作成する。
$ pyenv virtualenv djangular4 // 仮想環境を作成する
$ pip install django djangorestframework // django djangorestframework をpip installする

A Django URL Catch-all

  • django startprojectで、新しいprojectを開始する。$ django-admin.py startproject tryangular .

  • settings.pyにstaticfileの設定を追記する。

STATIC_URL = '/static/'

STATIC_ROOT = os.path.join(os.path.dirname(BASE_DIR), 'static-root')

STATICFILES_DIRS = [
        os.path.join(BASE_DIR, 'static')
]
  • urls.pyにangularで表示するurlをcatchするための設定をする。
from django.conf.urls import url
from django.contrib import admin
from django.views.generic.base import TemplateView #template viewをimportする

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^.*', TemplateView.as_view(template_name="ang_home.html"), name='home'), #
]
  • templatesフォルダを作成して、その中にang_home.htmlを作成する。
<h1>Hello Django!</h1>
  • またsettings.pyにtemplatesのフォルダの場所を指定する。
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
  • http://127.0.0.1:8000/でも、http://127.0.0.1:8000/dddでも、表示されるページが、ang_home.htmlになっていればOK。

  • 今後は、個別に表示したいurlがある場合は、(adminのように)、r'^.*'より上に書く。

まとめ

  • これで一通りの準備は完了ぽい。
  • .(ピリオド)を使うのがポイントだったな。

Angular4入門_Vol.20

参考サイト

https://www.codingforentrepreneurs.com/projects/try-angular-v4/

ngBuild and Deploy to Heroku

  • packageが完成したら、$ ng build --prod で、deploy用に、bundle.jsを作成する。prodオプションは、minファイルにするためか。bundle.jsには、typescriptファイルを役割ごとにまとめて、javascriptにtrans compileしたコードが記載されている。

  • $ ng build --prod --output-path /Users/username/Desktop/try_angular4/ngSrvupTest というコマンドでpathを指定して、ファイルを出力する。

  • さらに、$ ng build --prod --output-path /Users/username/Desktop/try_angular4/ngSrvupTest --watchで、watchもできる。

  • $ ng build --prod --output-path /Users/yassy/Desktop/try_angular4/ngSrvupTest/public/ というように、publicにしておいたほうがデプロイのときに楽ぽい。

  • ngSrvupTest フォルダに、Procfileを作成する。

web: node index.js
  • package.jsonを作成する(requirements.txtみたいなもんかな)
{
  "name": "try-angular-4",
  "version": "1.0.0",
  "main": "index.js",
  "engines": {
    "node": "5.9.1"
  },
  "dependencies": {
    "ejs": "2.4.1",
    "express": "4.13.3",
    "path": "0.12.7",
    "mongoose": "4.9.3",
    "body-parser": "1.17.1",
    "compression": "1.6.2"
  }
}
  • index.jsを作成する。
var express = require('express');
var path = require('path');
var app = express();

const port = process.env.PORT || '5000';

app.set('port', port);
app.use(express.static(__dirname + '/public')); // staticファイルの置き場所?
app.get('/[^\.]+$', function(req, res){
    res.set('Content-Type', 'text/html')
        .sendFile(path.join(__dirname, '/public/index.html'))
});

app.listen(app.get('port'), function(){
    console.log("Node app is running at localhost:" + app.get('port'))
});
  • $ node index.jsを実行してみる。。。error! express確認できないとのこと。

  • $ npm installで、package.jsonから必要なpackageをinstallする。

  • $ node index.js再度実行する。。。localhost:5000にアクセスしたところ無事表示された(^^)

  • .gitignoreファイルを作成する。

node_modules
.DS_Store
  • herokuにデプロイするために、git repositoryをlocalに作成する。
$git init
$git add --all
$git commit -m "initial commit"
  • herokuのrepositoryを作成する。
$ heroku create tryangular4
Creating ⬢ tryangular4yassy... done
https://tryangular4.herokuapp.com/ | https://git.heroku.com/tryangular4.git
  • $ git push heroku master で、deployする。。。デプロイ成功!上記で作成されたurlにアクセスすると、デプロイされているか確認できる(^^)

Angular4入門_Vol.19

参考サイト

https://www.codingforentrepreneurs.com/projects/try-angular-v4/ https://angular.io/docs/ts/latest/api/router/index/RouterLink-directive.html https://angular.io/docs/ts/latest/guide/router.html#!#basics-router-links

Router Link & Improved Navigation

  • Router Linkを使ってみる。video-list.component.htmlに追記してみる。
<p>
  {{ title }}
</p>

<div *ngFor='let item of videoList'>
  <h1><a routerLink="/videos/{{ item.slug }}" routerLinkActive="active" >{{ item.name }}</a></h1>
  <!-- <div [innerHTML]='item.embed'></div>
  <div [innerHTML]='someItem'></div> -->
  <iframe *ngIf='item.embed' width="560" height="315" [src]="getEmbedUrl(item) | safe" frameborder="0" allowfullscreen></iframe>
  <hr>
</div>
  • hrefの代わりに、routerLinkを使うという感じになっているが、hrefとの違いは何なんだろう。。。

  • routerLinkActiveを使うことによって、よってより柔軟にlinkの切り替えができるとかそんな感じなのかな。。。。

  • とりあえず、動画講義では、hrefになっていた箇所を片っ端から、routerLinkに変えていっていた。

Improve Styling

  • interfaceだけを修正するだけなので、割愛

ngClass

  • ngClassを使ってみる。ngClassは、たぶん、classをangularで双方向データバインディングできるようなモジュールだと思われる。
<p>
  {{ title }}
</p>
<div *ngFor='let item of videoList; let i = index' [ngClass]="{'row': (i+1)%3 == 0}">
  <div class="col-sm-4">
    <div class="thumbnail">
        <a routerLink="/videos/{{ item.slug }}" routerLinkActive="active" *ngIf='item.image'><img [src]='item.image' alt="{{ item.name }} image"></a>
        <div class="caption">
          <h3><a routerLink="/videos/{{ item.slug }}" routerLinkActive="active" >{{ item.name }}</a></h3>
          <p>...</p>
          <p><a routerLink="/videos/{{ item.slug }}" routerLinkActive="active" class="btn btn-primary" role="button">View</a></p>
        </div>
      </div>
  </div>
</div>

Angular4入門_Vol.18

参考サイト

https://www.codingforentrepreneurs.com/projects/try-angular-v4/

Video Item Model

  • video classを作成するために、video.tsファイルを作成する。これはdjangoで言うと、modelsを使うようなものか。
export class VideoItem {
  slug: string;
  name: string;
  image: string;
  embed?: string; //optionalな場合は、最後に?をつけるということか?
  featured?: Boolean;
}
  • video-list.component.tsのvideoListの型定義を、VideoItemに設定する。
import { Component, OnInit, OnDestroy } from '@angular/core';
import { VideoItem } from '../videos/video'; // VideoItem class を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: [VideoItem]; // VideoItemのリスト型を定義する。

  constructor(private _video:VideoService) { }

  ngOnInit() {
    // this.todayDate = new Date();
    this.req = this._video.list().subscribe(data=>{
      this.videoList = data as [VideoItem]; // VideoItemのリスト型を定義する。
    }); //
  }

  ngOnDestroy(){
    this.req.unsubscribe()
  }

  getEmbedUrl(item){
    return 'https://www.youtube.com/embed/' + item.embed;
  }

}
  • video-detail.component.tsも同様にVideoItem classを追記する。
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

import { VideoItem } from '../videos/video';
import { VideoService } from '../videos/videos.service';


@Component({
  selector: 'video-detail',
  templateUrl: './video-detail.component.html',
  styleUrls: ['./video-detail.component.css'],
  providers:[VideoService]
})
export class VideoDetailComponent implements OnInit, OnDestroy {
  private routeSub:any;
  private req:any;
  video: VideoItem;
  slug: string;
  constructor(private route: ActivatedRoute, private _video:VideoService) { }

  ngOnInit() {
    this.routeSub = this.route.params.subscribe(params => {
    this.slug = params['slug']
    this.req = this._video.get(this.slug).subscribe(data=>{
    this.video = data as VideoItem
      })
    })
  }
  ngOnDestroy(){
    this.routeSub.unsubscribe()
    this.req.unsubscribe()
  }

}
  • search-detail.component.ts にも追記する。
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

import { VideoItem } from '../videos/video'; // VideoItemをimport する
import { VideoService } from '../videos/videos.service';


@Component({
  selector: 'app-search-detail',
  templateUrl: './search-detail.component.html',
  styleUrls: ['./search-detail.component.css'],
  providers: [VideoService]
})
export class SearchDetailComponent implements OnInit, OnDestroy {
  private routeSub:any;
  private req:any;
  query: string;
  videoList: [VideoItem]; // VideoItem のリスト型を定義する

  constructor(private route: ActivatedRoute, private _video:VideoService) { }

  ngOnInit() {
    this.routeSub = this.route.params.subscribe(params=>{
      console.log(params)
      this.query = params['q']
      this.req = this._video.search(this.query).subscribe(data=>{
        this.videoList = data as [VideoItem]; // VideoItemのリスト型を定義する。
      })
    })
  }

  ngOnDestroy(){
    this.routeSub.unsubscribe()
  }

  getEmbedUrl(item){
    return 'https://www.youtube.com/embed/' + item.embed;
  }

}
  • home.component.tsにも追加する。
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router } from '@angular/router';

import { VideoItem } from '../videos/video'; // VideoItem import
import { VideoService } from '../videos/videos.service'; // VideoService import

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css'],
  providers: [VideoService] //providersに、VideoServiceを定義する
})
export class HomeComponent implements OnInit, OnDestroy {
  private req: any;
  homeImageList: [VideoItem] = [] as [VideoItem] ; //VideoItemのリスト型を定義する。さらに、初期値として、空のリストを渡しておく
  // [] のあとに、as [VideoItem]は必須の模様。代入するときはそのデータのclass定義が必要なのか?よくわからないな。
  //   homeImageList = [] as [VideoItem] ;は通った。
  // あ〜そうか、VideoItemで定義された homeImageListには、何も型定義していない空のリストは、型が不明のため、VideoItem classで定義された空のリストでないと代入できないのか。

  constructor(private http:Http, private router:Router, private _video:VideoService) { } //_video を定義する

  ngOnInit() {
    this.req = this._video.list().subscribe(data=>{ // _video.list()で、呼び出す。
      // console.log(data.json())
      //this.homeImageList = [] as [VideoItem]
      data.filter(item=>{
        if(item.featured){
          this.homeImageList.push(item)
        }
      })
      // this.homeImageList = data.json();
    })
  }

  ngOnDestroy(){
    this.req.unsubscribe()
  }

  preventNormal(event:MouseEvent, image:any){
    if (!image.prevented){
      event.preventDefault()
      // image.link = '/videos'
      // image.prevented = true;
      this.router.navigate(['./videos'])
  }
}

}

まとめ

  • angularのclassは、djangoのmodelのような使い方ができる。
  • 型の定義が厳密になるため、エラーチェックなどに有効

Angular4入門_Vol.17

参考サイト

https://www.codingforentrepreneurs.com/projects/try-angular-v4/

Search Video List

  • search methodを、videos.service.tsに追記する。methodの処理の方法は、get methodと似てるので、中身をコピペして再利用する。
search(query){
  return this.http.get(endpoint) // get methodで、endpointにアクセス
          .map(response=>{ // responseを代入して結果をmappingする
            let data = [] // data に空のリストを代入
            let req = response.json().filter(item=>{ // response.json()のデータをitemのname
                              if (item.name.indexOf(query) >= 0 ){ //indexOf
                                  data.push(item) // itemをpushする。
                              }
                            })
            return data
            })
          .catch(this.handleError)
}
  • indexOfは、indexOf() メソッドは、呼び出す String オブジェクト中で、fromIndex から検索を始め、指定された値が最初に現れたインデックスを返します。値が見つからない場合は -1 を返します。とのことなので、>=0としてある場合は、文字列を少なくとも1つは含む場合でfilterをかけていることになる。

  • 試しに、video-list.component.tsにsearch methodを記載してみる。

  ngOnInit() {
    this.todayDate = new Date();
    this.req = this._video.search("テスト1").subscribe(data=>{
      this.videoList = data as [any];
    }); //search("テスト1") で、引数として文字列を与えてみる。
  }
  • 無事テスト1だけ表示された!テストは成功したので、searchからlist()methodに戻しておく。

  • search-detailに、search methodを追記する。

import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { VideoService } from '../videos/videos.service'; //VideoServiceをimportする


@Component({
  selector: 'app-search-detail',
  templateUrl: './search-detail.component.html',
  styleUrls: ['./search-detail.component.css'],
  providers: [VideoService] //VideoServiceをprovidersとして、定義する
})
export class SearchDetailComponent implements OnInit, OnDestroy {
  private routeSub:any;
  private req:any;
  query: string;
  videoList: [any];

  constructor(private route: ActivatedRoute, private _video:VideoService) { } //_vidoe にVideoServiceを型定義する。

  ngOnInit() {
    this.routeSub = this.route.params.subscribe(params=>{
      console.log(params)
      this.query = params['q']
      this.req = this._video.search(this.query).subscribe(data=>{ //searchメソッドを、引数をqueryとして組み込む
        this.videoList = data as [any];
      })
    })
  }

  ngOnDestroy(){
    this.routeSub.unsubscribe()
  }

}
  • search boxに、キーワードを入れて検索してみる。。。。何も表示されない。。。

  • video-list.component.html の、video listを表示する箇所のhtmlのコードをコピペしてみる。

<p *ngIf='query'>
  Searched for <b>{{ query }}</b>
</p>

<div *ngFor='let item of videoList'>
  <h1><a href="videos/{{ item.slug }}" >{{ item.name }}</a></h1>
  <!-- <div [innerHTML]='item.embed'></div>
  <div [innerHTML]='someItem'></div> -->
  <iframe *ngIf='item.embed' width="560" height="315" [src]="getEmbedUrl(item) | safe" frameborder="0" allowfullscreen></iframe>
  <hr>
</div>
  • Error発生! ERROR TypeError: co.getEmbedUrl is not a functionとのこと。なので、getEmbedUrlを定義する。
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { VideoService } from '../videos/videos.service';


@Component({
  selector: 'app-search-detail',
  templateUrl: './search-detail.component.html',
  styleUrls: ['./search-detail.component.css'],
  providers: [VideoService]
})
export class SearchDetailComponent implements OnInit, OnDestroy {
  private routeSub:any;
  private req:any;
  query: string;
  videoList: [any];

  constructor(private route: ActivatedRoute, private _video:VideoService) { }

  ngOnInit() {
    this.routeSub = this.route.params.subscribe(params=>{
      console.log(params)
      this.query = params['q']
      this.req = this._video.search(this.query).subscribe(data=>{
        this.videoList = data as [any];
      })
    })
  }

  ngOnDestroy(){
    this.routeSub.unsubscribe()
  }

  getEmbedUrl(item){
    return 'https://www.youtube.com/embed/' + item.embed;
  }

}
  • これで再度検索してみる。。。できた〜!

まとめ

  • serviceは、apiからデータを取得>加工>componentに渡す 役割があるぽい。
  • もしかしたら、componentからデータ加工の指示>serviceを通じてapiのデータを変更・削除などもserviceでやるのか?それはまた別なのかな。

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 が基本セットになるのかも