気ままなタンス*プログラミングなどのノートブック

プログラミングやRPGツクール、DTM等について、学んだことや備忘録をアウトプットとして残し、情報を必要としている誰かにとって「かゆいところに手が届く」ブログとなることを願いながら記事を書いています。

【Angular】JSONPの取得方法がわかったのでサンプルを作った話(HttpClient jsonp)

スポンサーリンク

こんばんは。今回はAngularの話です。

今日は休日ということもあり、以前購入した「Angularアプリケーションプログラミング」の本を読みすすめていました。

Angularアプリケーションプログラミング

Angularアプリケーションプログラミング

その中で、はてなブックマークのAPIを使って、 ブックマークエントリー情報をJSONP形式で取得するという内容があったのですが・・・書籍の発売当時のAngularバージョンを元にして書かれているため、 うまく動きません。

そこで、JSONP形式による通信方法について調査し、現行バージョンでの取得サンプルを作成しました。

目次

サンプルプログラムのページ

はてなブックマークエントリ情報APIで、指定したURLのブックマーク情報を取得します。 初期URLは僕が作成した、ツクールMVのプラグインURLが指定されています。

https://rinne-grid.github.io/index.html

(スクリーンショット)

  • 初期画面 f:id:rinne_grid2_1:20180527212431p:plain

  • ブックマークエントリー取得 f:id:rinne_grid2_1:20180527212442p:plain

サンプルプログラムの置き場所

Githubに配置しました。

github.com

  • 今回、@angular/materialを使って画面を作成していますが、この記事の本筋ではないので、コードの記載は省略します。
  • 全ソースが上記にありますので、git cloneして動作確認等にご利用ください。

Angularアプリの作成

Angularアプリケーションの作成については、公式ドキュメントをご参照ください。

ざっくりと下記のような感じです

  • npmの場合
$ npm i -g @angular/cli
$ ng new appname
  • yarnの場合
$ yarn global add @angular/cli
$ ng new appname

app.modules.tsの記述

  • ポイント
    • @angular/common/HttpClientを利用するため、HttpClientModuleをインポートする
    • HttpClientのjsonpを利用するため、HttpClientJsonpModuleをインポートする
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { HttpClientModule, HttpClientJsonpModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    // @angular/core
    BrowserModule,
    HttpClientModule,
    HttpClientJsonpModule,
    BrowserAnimationsModule,
    FormsModule,

  ],
  providers: [
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.htmlの記述

  • ポイント
    • はてなブックマークエントリーAPIを呼び出す契機となるボタンを配置する
    • 取得するブックマークエントリの対象となるURLを入力するテキストボックスを配置する(コンポーネントのtargetUrlと双方向バインド)
<input name="targetUrl" [(ngModel)]="targetUrl">
<button name="getBookmarks"  (click)="getBookmark()">取得</button>

app.component.tsの記述

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  targetUrl = 'https://github.com/rinne-grid/tkoolmv_plugin_RecollectionMode'
  
  constructor(){}
  getBookmark() {
    console.log(this.targetUrl);
  }
}

サービスクラス(hatena-bookmark.service.ts)の作成

ng generateの利用

  • コンソール、ターミナルでcliを実行し、サービスクラスのテンプレートを作成
$ ng generate service hatena-bookmark

サービスクラスの記述

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

import { Observable } from 'rxjs';

@Injectable()
export class HatenaBookmarkService {
    constructor(private http: HttpClient){}
    // はてなブックマークエントリ情報取得用のAPIエンドポイント
    private apiEndPoint: string = "https://b.hatena.ne.jp/entry/json/";

    getBookmarks(targetUrl:string): Observable<any> {
        console.log(targetUrl);
        // API仕様によると、urlとcallbackを指定するとjsonpを戻すとのこと
        // callbackはjsonpの第2引数で指定する
        let httpParams = new HttpParams()
            .set("url", targetUrl);
        
        // httpParamsにパラメータを設定した状態でtoStringを実行すると、key=value形式で返してくれる
        // https://angular.jp/api/common/http/HttpParams#toString
        console.log(`${this.apiEndPoint}?${httpParams.toString()}`)
        return this.http.jsonp<any>(`${this.apiEndPoint}?${httpParams.toString()}`, "callback");
    }
}

サービスクラスをapp.module.tsのprovidersに追加

import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { HttpClientModule, HttpClientJsonpModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';

//-------------------------------------------------------
// 追加
//-------------------------------------------------------
import { HatenaBookmarkService } from './hatena-bookmark.service';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    // @angular/core
    BrowserModule,
    HttpClientModule,
    HttpClientJsonpModule,
    BrowserAnimationsModule,
    FormsModule,

  ],
  providers: [
//-------------------------------------------------------
// 追加
//-------------------------------------------------------
    HatenaBookmarkService
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.tsの修正

  • ポイント
    • HatenaBookmarkServiceをDIする
    • getBookmarkメソッドから、HatenaBookmarkServiceのgetBookmarksメソッドを呼び出す
    • サービスクラスのgetBookmarksの戻り値(Observable)をsubscribeし内容を取得する
import { Component } from '@angular/core';
import { HatenaBookmarkService } from './hatena-bookmark.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  targetEntry: any = null;
  targetUrl = 'https://github.com/rinne-grid/tkoolmv_plugin_RecollectionMode'
  
  constructor(private hatenaService: HatenaBookmarkService){}
  getBookmark() {
    console.log(this.targetUrl);
    this.hatenaService.getBookmarks(this.targetUrl).subscribe(
      (entries) => {
        this.targetEntry = entries;
        console.log(entries);
      }
    );
  }
}

アプリを実行

$ ng serve

http://localhost:4200 に接続する

この記事内のソースコードは@angular/materialに関する記述を除いているため、 リンク先のようなサンプルにはならないのですが、HttpClient.jsonpを利用して、 JSONP形式のデータを取得できたことがわかるかと思います。

StackBlitzに本記事のソースコードのみで作ったサンプルを配置しましたので、こちらもご利用ください

(StackBlitz)本記事内のソースコードのみのバージョン

stackblitz.com

ハマったところ(あまりJSONP関係ない)

  • Github Pagesにサンプルを上げたとき、はてなブックマークAPIのURLがhttpで、GithubPagesがhttpsだったのでMixedコンテンツになってしまってうまく動かなかった
    • APIがhttpsも対応していたため、httpsに変更
    • 同様に、StackBlitzからのリクエストもhttpsに変更したら動いた
  • HttpParamsにパラメータをセットする際にインスタンスを作成した後にappendやsetを実行してもうまくセットされなかった
// ダメだった
let httpParams = new HttpParams(); 
httpParams.set("hoge", "foo");
// うまくいった
let httpParams = new HttpParams().set("hoge", "foo");

AngularによるJSONP取得ですが、だいぶ簡単に書くことができて、非常に便利だと思いました。 HttpClient.get, post, put等だとCORS対策等でハマりがちなのですが、 JSONPの場合は(開発の時点では)あまりハマることなくすんなりいけたように思います。

それでは、皆さんも楽しいAngularプログラミングを。