仮想サーファーの波乗り

仮想化エンジニアの日常

プログラミング・SNS分析・仮想通貨・自動化などに関してよく書く雑記ブログ

Angular6でアプリ開発④「仮想通貨詳細ページ作成と一覧ページから遷移」

f:id:virtual-surfer:20180603084150p:plain

Angularでアプリケーションを作ろう企画の4記事目。今回は仮想通貨情報の詳細ページを作成して、一覧ページから遷移できるようにしていきます。今回の実装で各ページの遷移ができるようになります。


コイン詳細ページの実装

まずはコイン詳細ページを作成していきます。コイン詳細ページでは、CoinGecko APIにアクセスをしてコイン詳細情報を取得し、そのレスポンスデータをComponentで加工してページに表示していきます。

遷移方法は、検索・一覧画面からの選択で遷移の2つの方法に対応できるようにもしていきます。


Serviceの作成

APIアクセスを行うServiceを作成します。

$ ng generate service currency

上記コマンドで「currency.service」ファイルを作成したら、以下のように編集します。

currency.service.ts

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

@Injectable()
export class CoinService {

  COIN_API_URL = 'https://api.coingecko.com/api/v3/coins/';
  constructor(private http: HttpClient) {}

  request(coinId: string): Observable<any> {
    return this.http.get(this.COIN_API_URL + coinId);
  }
}

仮想通貨一覧ページと同様に、HttpClientモジュールによってAPIにHTTPリクエストを行い、レスポンスを取得します。


Componentの作成

コインの詳細情報を扱うComponentを用意します。

$ ng generate component currency

上記コマンドで「currency.component」ファイルを作成したら、以下のように編集します。

currency.component.ts

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

import { CurrencyService } from "../currency.service";

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

  coinId = 'bitcoin';
  id: string;
  symbol: string;
  name: string;
  image: string;
  coingecko_score: number;
  current_price_jpy: number;
  market_cap_jpy: number;
  total_volume_jpy: number;
  high_jpy: number;
  low_jpy: number;
  price_change_24h: string;
  price_change_percentage_24h: string;
  price_change_percentage_7d: string;
  price_change_percentage_14d: string;
  price_change_percentage_30d: string;
  price_change_percentage_60d: string;
  price_change_percentage_200d: string;
  price_change_percentage_1y: string;
  market_cap_change_24h: string;
  market_cap_change_percentage_24h: string;
  facebook_likes: number;
  twitter_followers: number;
  reddit_average_posts_48h: number;
  reddit_average_comments_48h: number;
  reddit_subscribers: number;
  reddit_accounts_active_48h: number;
  forks: number;
  stars: number;
  subscribers: number;
  total_issues: number;
  closed_issues: number;
  pull_requests_merged: number;
  pull_request_contributors: number;
  commit_count_4_weeks: number;
  last_updated: string;

  constructor(
    private currencyService: CurrencyService
  ) {}

  ngOnInit() {
    this.onclick();
  }

  onclick() {
    this.currencyService.requestInfo(this.coinId)
      .subscribe(response => {
          this.id = response.id;
          this.symbol = response.symbol;
          this.name = response.name;
          this.image = response.image.small;
          this.coingecko_score = response.coingecko_score;
          this.current_price_jpy = response.market_data.current_price.jpy;
          this.market_cap_jpy = response.market_data.market_cap.jpy;
          this.total_volume_jpy = response.market_data.total_volume.jpy;
          this.high_jpy = response.market_data.high.jpy;
          this.low_jpy = response.market_data.low.jpy;
          this.price_change_24h = response.market_data.price_change_24h;
          this.price_change_percentage_24h = response.market_data.price_change_percentage_24h;
          this.price_change_percentage_7d = response.market_data.price_change_percentage_7d;
          this.price_change_percentage_14d = response.market_data.price_change_percentage_14d;
          this.price_change_percentage_30d = response.market_data.price_change_percentage_30d;
          this.price_change_percentage_60d = response.market_data.price_change_percentage_60d;
          this.price_change_percentage_200d = response.market_data.price_change_percentage_200d;
          this.price_change_percentage_1y = response.market_data.price_change_percentage_1y;
          this.market_cap_change_24h = response.market_data.market_cap_change_24h;
          this.market_cap_change_percentage_24h = response.market_data.market_cap_change_percentage_24h;
          this.facebook_likes = response.community_data.facebook_likes;
          this.twitter_followers = response.community_data.twitter_followers;
          this.reddit_average_posts_48h = response.community_data.reddit_average_posts_48h;
          this.reddit_average_comments_48h = response.community_data.reddit_average_comments_48h;
          this.reddit_subscribers = response.community_data.reddit_subscribers;
          this.reddit_accounts_active_48h = response.community_data.reddit_accounts_active_48h;
          this.forks = response.developer_data.forks;
          this.stars = response.developer_data.stars;
          this.subscribers = response.developer_data.subscribers;
          this.total_issues = response.developer_data.total_issues;
          this.closed_issues = response.developer_data.closed_issues;
          this.pull_requests_merged = response.developer_data.pull_requests_merged;
          this.pull_request_contributors = response.developer_data.pull_request_contributors;
          this.commit_count_4_weeks = response.developer_data.commit_count_4_weeks;
          this.last_updated = response.last_updated;
        },
        error => {
          console.log('CoinGeckoへのアクセスに失敗しました。');
        }
      );
  }
}

コンポーネントでは、Serviceのメソッドを呼び出してhtmlでComponentを元にデータを表示できるようにしています。また、idが指定されずに画面遷移で詳細ページに遷移した場合には、初期値のbitcoinのページを表示するようにしています。


作成したCurrencyServiceを呼び出し、CurrencyComponentの要素をhtmlファイルで呼び出すためにapp.moduleにServiceとComponentを追加しておきます。また、FormsModuleを利用するので、importしておきます。

app.module.ts

...
import { FormsModule } from "@angular/forms";
import { CurrencyComponent } from './currency/currency.component';
import { CurrencyService } from "./currency.service";
...
declarations: [
    ...
    CurrencyComponent
  ],
imports: [
    ...
    FormsModule
  ],
providers: [
    ...
    CurrencyService
  ],
...


ルーティングの設定

また、画面遷移ができるように、Routingにcurrencyページに遷移するパスの設定をしておきます。

app-routing.module.ts

...
import { CurrencyComponent } from "./currency/currency.component";
...
const routes: Routes = [
  ...
  { path: 'currency', component: CurrencyComponent },
  { path: 'currency/:id', component: CurrencyComponent }
...


htmlの編集

画面にコイン情報詳細を表示するため、htmlを記述していきます。

currency.component.ts

<h2>
  仮想通貨詳細情報
</h2>

<table>
  <tbody>
  <tr>
    <td colspan="2" class="coin-name"><img src="{{image}}" width="50" height="50"> {{name}}({{symbol | uppercase}})</td>
  </tr>
  <tr>
    <th class="item">更新時間</th>
    <td>{{last_updated}}</td>
  </tr>
  <tr>
    <th class="item">現在価格</th>
    <td>¥{{current_price_jpy | number}}</td>
  </tr>
  <tr>
    <th class="item">時価総額</th>
    <td>¥{{market_cap_jpy | number}}</td>
  </tr>
  <tr>
    <th class="item">総額</th>
    <td>¥{{total_volume_jpy | number}}</td>
  </tr>
  <tr>
    <th class="item">high</th>
    <td>¥{{high_jpy | number}}</td>
  </tr>
  <tr>
    <th class="item">low</th>
    <td>¥{{low_jpy | number}}</td>
  </tr>
  <tr>
    <th class="item">24時間の値動き</th>
    <td>{{price_change_24h}}({{price_change_percentage_24h}}%)</td>
  </tr>
  <tr>
    <th class="item">7日間の値動き</th>
    <td>{{price_change_percentage_7d}}%</td>
  </tr>
  <tr>
    <th class="item">14日間の値動き</th>
    <td>{{price_change_percentage_14d}}%</td>
  </tr>
  <tr>
    <th class="item">30日間の値動き</th>
    <td>{{price_change_percentage_30d}}%</td>
  </tr>
  <tr>
    <th class="item">60日間の値動き</th>
    <td>{{price_change_percentage_60d}}%</td>
  </tr>
  <tr>
    <th class="item">200日間の値動き</th>
    <td>{{price_change_percentage_200d}}%</td>
  </tr>
  <tr>
    <th class="item">1年間の値動き</th>
    <td>{{price_change_percentage_1y}}%</td>
  </tr>
  <tr>
    <th class="item">24時間の時価総額の変化</th>
    <td>{{market_cap_change_24h}}({{market_cap_change_percentage_24h}}%)</td>
  </tr>
  <tr>
    <th class="item">Facebookいいね数</th>
    <td>{{facebook_likes | number}}いいね</td>
  </tr>
  <tr>
    <th class="item">Twitterフォロワー数</th>
    <td>{{twitter_followers | number}}人</td>
  </tr>
  <tr>
    <th class="item">48時間でのReddit投稿の平均</th>
    <td>{{reddit_average_posts_48h | number}}本</td>
  </tr>
  <tr>
    <th class="item">48時間でのRedditコメントの平均</th>
    <td>{{twitter_followers | number}}個</td>
  </tr>
  <tr>
    <th class="item">Reddit読者数</th>
    <td>{{reddit_subscribers | number}}人</td>
  </tr>
  <tr>
    <th class="item">Reddit48時間以内ログインアカウント</th>
    <td>{{reddit_accounts_active_48h | number}}個</td>
  </tr>
  <tr>
    <th class="item">Githubフォーク数</th>
    <td>{{forks | number}}回</td>
  </tr>
  <tr>
    <th class="item">Githubスター数</th>
    <td>{{stars | number}}個</td>
  </tr>
  <tr>
    <th class="item">Github購読者数</th>
    <td>{{subscribers | number}}人</td>
  </tr>
  <tr>
    <th class="item">GithubのIssue数</th>
    <td>{{total_issues | number}}個</td>
  </tr>
  <tr>
    <th class="item">GithubクローズしたIssue数</th>
    <td>{{closed_issues | number}}人</td>
  </tr>
  <tr>
    <th class="item">Githubマージされたプルリク数</th>
    <td>{{pull_requests_merged | number}}人</td>
  </tr>
  <tr>
    <th class="item">Githubコントリビューター数</th>
    <td>{{pull_request_contributors | number}}人</td>
  </tr>
  <tr>
    <th class="item">Github4週間でのコミット数</th>
    <td>{{commit_count_4_weeks | number}}人</td>
  </tr>
  <tr>
    <th class="item">CoinGeckoスコア</th>
    <td>{{coingecko_score | number}}</td>
  </tr>
  </tbody>
</table>

Componentで用意した値を表示しているだけです。Pipeを利用して数字形式で表示したり、大きすぎる数字はsliceで短くしたり最低限の加工はこのファイルにて行っています。

app.component.html

<h1>{{title}}</h1>
<ul>
  <li><a routerLink="/currencies">仮想通貨一覧</a></li>
  <li><a routerLink="/currency">仮想通貨詳細</a></li>
</ul>
<router-outlet></router-outlet>

アプリで共通のコンポネーントに仮想通貨詳細ページへの遷移リンクを作成しておきます。


ここまでで、仮想通貨詳細ページは表示できるようになったはずなので、確認してみましょう。

https://gyazo.com/c25859e805f6d9de8d875dce9047fb66

仮想通貨詳細ページに遷移でき、Bitcoinの詳細情報が表示できるようになっていますね。


コイン検索フォームの用意

現状だと、特定のコインの検索と表示をすることができないので、特定のコインを検索して画面表示できるようにしていきます。htmlを以下のように編集します。


currencies.component.html

<h2>
  仮想通貨詳細情報
</h2>
<form>
  <label for="coinId">コインのID:</label>
  <input id="coinId" name="coinId" type="text" size="50" [(ngModel)]="coinId">
  <input type="button" (click)="onclick()" value="検索" />
</form>

これでフォームでcoinIdを受け取って、その値でAPIアクセスをし、特定のコイン情報を表示できるようになりました。

https://gyazo.com/74fe4f2b08c6d61864820cfca6c27547


コイン一覧ページから仮想通貨詳細ページに遷移

さらに、コイン一覧ページからどれかをクリックするとそのコインの詳細ページに遷移するようにしていきます。

まずはCurrencyComponentにActivatedRouteモジュールを追加し、ngOnInitメソッドの中でrouteから取得したidをcoinIdに参照させます。

currency.component.ts

...
import { ActivatedRoute } from "@angular/router";
...
constructor(
    private route: ActivatedRoute,
...
ngOnInit() {
    if (this.route.snapshot.paramMap.get('id') != null) {
      this.coinId = this.route.snapshot.paramMap.get('id');
    }
    this.onclick();
  }
...


コイン一覧ページから遷移するので、仮想通貨詳細ページのComponentとhtmlに仮想通貨詳細ページに遷移するための処理を追加していきます。まずはComponentにRouteモジュールを追加し、routeCoinメソッドで

currencies.component.ts

...
import { Router } from "@angular/router";
...
constructor(
    private router: Router,
...
routeCoin(id: string) {
    this.router.navigate(["/currency/" + id]);
  }
...

これによって、htmlで受け取ったidを元に仮想通貨詳細ページに遷移するような処理を用意できました。続いて、htmlで処理を呼び出して、画面遷移できるようにしていきます。

currencies.component.html

...
<tr *ngFor="let currency of currencies" (click)="routeCoin(currency.id)">
...

「(click)="routeCoin(currency.id)"」の箇所でクリックイベントを呼び出し、行のどこかをクリックされたらその行のコインIDを引数に渡してCurrenciesServiceのrouteCoinメソッドが呼ばれ画面遷移します。


ここまでで、一覧ページから特定の仮想通貨詳細ページに遷移できるようになっているはずです。

https://gyazo.com/d01732110411bdb06d6c48b3e32a44fa

コイン一覧ページから仮想通貨詳細ページに遷移していることが確認できますね!


まとめ

今回は仮想通貨詳細ページを実装し、コイン一覧ページからクリックして特定のコインに遷移できるようにしていきましたが、今回の実装によってより自分の知りたい情報をピンポイントで知れるようになりました。

次回は、何を実装するか悩み中。。。そろそろAPIアクセスだけだと飽きてきたので、データベースにアクセスしてデータの加工処理をやっていきたいところ。


では!