LoginSignup
0
0

More than 3 years have passed since last update.

第8回:ニュースフィード取得サーバIFをWebアプリから呼び出す

Last updated at Posted at 2020-09-22

目次:Webアプリ開発ロードマップ

第8回:ニュースフィード取得サーバIFをWebアプリから呼び出す

今回は前回作成したニュースフィード一覧取得IFをフロントエンドから呼び出してみます。

REST API操作用のサービスを作成する

空のサービスを作成する

フロントエンドでREST APIを取り扱うためのサービスを作成します。
下記のコマンドを実行します。

$ ng g service services/rest/rest

rest.service.tsとテストコードのrest.service.spec.tsが作成されます。
image.png

モジュールで作成したサービスを読み込む

src/app/app.module.tsに下記の修正を行い、アプリにserviceを読み込ませます。

・・・
import { RestService } from './services/rest/rest.service';
・・・
  providers: [
    RestService
  ],
・・・

REST APIのレスポンスデータを受け取るためのデータクラスを作成する

ニュースフィード用のデータクラスを作成する

src/app/services/rest/data/newsfeeds.tsを下記の内容で新規に作成します。
ニュースフィードのデータを持たせるためのデータクラスです。

export class Newsfeed {
  message: string;
  createdAt: Date;

  decode(json: any) {
    if (json) {
      this.message = json.message;
      this.createdAt = json.createdAt ? new Date(json.createdAt) : undefined;
    }
  }
}

export class Newsfeeds {
  private sortedByDefault: Newsfeed[] = [];
  newsfeeds: Newsfeed[];

  decode(json: any) {
    if (json) {
      for (const jsonNewsfeed of json) {
        const newsfeed = new Newsfeed();
        newsfeed.decode(jsonNewsfeed);
        this.sortedByDefault.push(newsfeed);
      }
      this.sortByCreatedAt();
    }
  }

  sortByCreatedAt() {
    this.newsfeeds = this.sortedByDefault.slice();
    this.newsfeeds.sort((a, b) => {
      return b.createdAt.getTime() - a.createdAt.getTime();
    });
  }
}

JSONオブジェクトからデータを取り出し、内部に保持させるdecodeメソッドや、
日時で初期ソートを行うsortByCreatedAt()メソッドを持たせています。

レスポンスデータ共通のデータクラスを作成する

src/app/services/rest/data/response.tsを下記の内容で新規に作成します。
全てのレスポンスデータが共通で持つ部分を作成します。

export class Response<T> {
  code: number;
  data: T;

  constructor(data: T, code?: number) {
    this.code = code ? code : 200;
    this.data = data;
  }
}

RestServiceにニュースフィード一覧取得用の処理を実装する

HttpClientModuleをモジュールに登録する

RestClientではHttpClientを利用したいので、HttpClientModuleをapp.module.tsに登録します。

・・・
import { HttpClientModule } from '@angular/common/http';
・・・
  imports: [
・・・
    HttpClientModule,
  ],
・・・

RestServiceにニュースフィード一覧取得の処理を実装する

src/app/services/rest/rest.service.tsにニュースフィード一覧取得の処理を実装します。

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

import { Response } from './data/response';
import { Newsfeeds } from './data/newsfeeds';

@Injectable({
  providedIn: 'root'
})
export class RestService {

  constructor(
    private httpClient: HttpClient
  ) { }

  async searchNewsFeeds(): Promise<Response<Newsfeeds>> {
    return new Promise((resolve, reject) => {
      const url = 'http://127.0.0.1:4300/newsfeeds';
      this.httpClient.get(url).toPromise().then(response => {
        const newsfeeds = new Newsfeeds();
        newsfeeds.decode(response);
        resolve(new Response(newsfeeds));
      }).catch(error => {
        console.error(error);
        reject(error);
      });
    });
  }
}

async、Promise、resolve、reject、then、catchは非同期処理を行うためのものです。
下記を参照してください。

REST APIはthis.httpClient.get(url)の部分で呼び出されます。

コンポーネントから作成したRestServiceを利用する

TypeScriptファイルを修正する。

src/app/components/newsfeeds/newsfeeds.component.tsを下記のように修正します。
後述のhtmlの修正まで行わないとエラーになります。

import { Component, OnInit } from '@angular/core';
import { RestService } from '../../services/rest/rest.service';
import { Newsfeeds } from '../../services/rest/data/newsfeeds';

@Component({
  selector: 'app-newsfeeds',
  templateUrl: './newsfeeds.component.html',
  styleUrls: ['./newsfeeds.component.scss']
})
export class NewsfeedsComponent implements OnInit {

  newsfeeds: Newsfeeds;

  constructor(
    private restService: RestService
  ) { }

  async ngOnInit() {
    try {
      const response = await this.restService.searchNewsFeeds();
      this.newsfeeds = response.data;
    } catch (error) {
      // TODO: display alert dialog.
    }
  }
}

Serviceのオブジェクトはアプリ内で1つだけ生成されます。
constructor()の引数にServiceを記載することで、コンポーネントが生成される時に引数に渡されます。

  constructor(
    private restService: RestService
  ) { }

aync、awaitは非同期のためのものです。
非同期処理async,awaitについてを参照してください。

HTMLファイルを修正する。

src/app/components/newsfeeds/newsfeeds.component.htmlを下記のように修正します。
ここまで実装すれば、画面にバックエンドから取得したデータが表示されます。

<div id="newsfeeds">
  <ng-container *ngIf="newsfeeds && newsfeeds.newsfeeds">
    <article *ngFor="let newsfeed of newsfeeds.newsfeeds" class="newsfeed">
      <p class="message">{{newsfeed.message}}</p>
      <p class="createdAt">{{newsfeed.createdAt | dateToString}}</p>
    </article>
  </ng-container>
</div>

元々はnewsfeedsに配列を持たせていましたが、newsfeeds.newsfeedsに配列を持たせるよう修正しています。
そのため、REST APIからデータをもらう前に画面を作ろうとするとnewsfeeds.newsfeedsに参照エラーが起きてしまいます。
そのためif文を追加し、データがあるときだけ表示するよう修正しています。
また、*ngIf*ngForと同じタグ内に書けないため、<ng-container>を追加し*ngIfを行っています。
<ng-container>は画面には表示されない制御用のタグです。

環境変数を利用する

environmentsファイルを修正

src/app/services/rest/rest.service.tsにサーバアドレスhttp://127.0.0.1:4300を直書きしていたため、
環境変数に置き換えます。
src/environments/environment.prod.tssrc/environments/environment.tsに下記のrestの部分を追加します。

export const environment = {
  production: true,
  rest: {
    domain: 'http://127.0.0.1:4300'
  }
};

environment.prod.tsはプロダクション(商用)ビルド、environment.tsは開発用ビルドで利用される環境変数です。
今まで利用してきたビルドコマンド$ ng serve$ ng buildは開発用のビルドを行います。
--prodオプションをつけることでプロダクションビルドになります。

環境変数を埋め込む

src/app/services/rest/rest.service.tsに下記の修正を加えます。

・・・
import { environment } from '../../../environments/environment';
・・・
  async searchNewsFeeds(): Promise<Response<Newsfeeds>> {
    return new Promise((resolve, reject) => {
      const url = environment.rest.domain + '/newsfeeds';
・・・

最後に

今回はフロントエンドからREST APIを呼んでみました。
次回は、ニュースフィード投稿用のIFを作成していきます。
今回開発したソースコードはGitHubに入っています。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0