Help us understand the problem. What is going on with this article?

angularで外部のJavaScriptライブラリを利用する方法

More than 1 year has passed since last update.

justInCaseでフロントエンドを担当している@diostrayです。
12/1に入社したばかりのアラサーおじさんです:innocent: (入社は最近ですが、ジョインはもうちょっと前だったりします)
経歴上サーバサイドがメインだった(はず)なんですが、流れに身を任せた結果、気づいたらフロントエンドをやることになっていました。なぜだろう…フシギダネ

justInCaseのマイページ(契約者専用のWebポータル的なもの)でangularを使っているので、地味にハマった内容をシェアします。

TL;DR

angular-cliプロジェクトはnode-jsを使うことになるので、npmに登録されているライブラリ(資産)がそのまま使えます。
けれども全てのJavaScriptライブラリがnpmに登録されているかと言われればそうではないわけで、外部のJavaScriptライブラリをscriptタグで読み込まないといけない場合があります。
例) facebook-sdk / google-api-sdk

そんな時にどうすればいいの?という疑問に対して、angular-cliプロジェクトにGoogleのJavaScriptライブラリを利用して、GoogleアカウントでのSSOを実装した時の設定内容を元に解説します。

ここの内容に近いんですが、違いは外部のJavaScriptというところと、angularのバージョンが違うというところです。

おことわり

  • Google APIの使い方については触れません。
  • Google APIを利用するために必要なもの(クライアントID etc...)は準備済みとします。

Envirionment(Version info)

envirionment
node-js     8.11.1
angular-cli 6.0.1
angular     6.0.1

angular7出ちゃったよ…

Step1. 外部のJavaScriptライブラリをindex.htmlに追加する

これはそのままなので、index.htmlの差分を貼り付るだけにします。

index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>justincase</title>
    <base href="/">

    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" type="image/x-icon" href="favicon.ico">
+    <script src="https://apis.google.com/js/platform.js?onload=onLoadCallback" async defer></script>
  </head>
  <body>
    <app-root></app-root>
  </body>
</html>

ポイント

  • Googleの参考サイトだと?onload=~~は書いてないですが、ビルドとかの都合上入れる必要があります
  • 理由は後で説明します

Step2. typesを導入する

後でng buildした時とかにエラーで泣く羽目になるので、先に解消するための仕込みをします。
こういう地味なところで躓くのは良いことなのか悪いことなのか未だに判断つきません。

※ typesとはなんぞ?という方はここを見てください。

Step2-1. Google APIの型定義を導入する

https://github.com/DefinitelyTyped/DefinitelyTyped で定義されています。
こういうリポジトリのメンテナにはいつも感謝です。
必要な型定義はgapi.auth2(と依存するgapi)です。

package.json
{
  "name": "test-project",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
  },
  "private": true,
  "devDependencies": {
+    "@types/gapi.auth2": "0.0.47",
  }
}

npm iで終わりと思いきや、大きな罠があります。
npmのライブラリはこれだけで良いんですが、外部ライブラリの場合は個別に読み込む設定を追加しないと型情報を拾ってくれません。(拾ってくれないだけなら良いんですが、ビルドエラーになってしまいます)
そこで、tsconfig.app.jsonに型定義を読み込む設定を追加し、ソース内で型情報を認識させるようにします。

tsconfig.app.json
{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/app",
    "module": "es2015",
-    "types": ["node"]
+    "types": ["node", "gapi", "gapi.auth2"]
  },
  "exclude": [
    "src/test.ts",
    "**/*.spec.ts"
  ]
}

ポイント

  • tsconfig.app.jsonに書くのを忘れない
  • もう一つの解決方法もありますが、そちらはデメリットがあるので省略します

Step2-2. 個別の型定義を導入する

Step1で非同期インクルードの設定をしたのですが、(追加した?onload=~~の部分)この関数を定義しないとビルドで落ちる羽目になります。

そこで、定義ファイルをsrc/typesの下にこんなファイルを追加することで読み込ませます。

window.d.ts
interface Window {
  onLoadCallback: () => void;
}

declare var window: Window;

ポイント

  • 当然ですがindex.htmlとこの中の関数名は一緒にする

Step3. Google APIライブラリをプロジェクトに取り込む

ライブラリを使う時は、大抵の場合初期化処理が必要です。
どこに書くかは好みの問題だと思いますが、app.component.tsにそのまま書いてしまいます。

app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  constructor() {
    // configure google api component.
    window.onLoadCallback = () => {
      gapi.load('client:auth2', () => {
        gapi.auth2.init({
          client_id: "XXXXXXXXXXXXXXXXXXXXXX",
          fetch_basic_profile: true,
          scope: 'profile'
        });
      });
    };
  }
}

ポイント

  • Step1で非同期インクルードの設定をしたのは、ここでの都合上
  • componentの初期化とscriptの読み込みの開始時期を比較した時に、componentの初期化が早いと、未定義エラーになってしまうため、非同期インクルードの設定が必須

Step5. ng buildする

タイトルそのままなので省略…

Step6. ng test用の設定を追加する

ググり力が低かったのか、なかなか見つけられなかったユニットテスト用の設定です。
(あまりやってないのか、それともハマってないのか…)

Step 6-1. type設定を読み込む

Step2-1の設定はビルド向けの設定で、テスト用の設定はtscofig.spec.jsonに用意されています。
tsconfig.app.jsonと同じ設定をこちらにも追加します。

tsconfig.spec.json
{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/spec",
    "module": "commonjs",
    "types": [
      "jasmine",
      "node",
+      "gapi",
+      "gapi.auth2"
    ]
  },
  "files": [
    "test.ts",
    "polyfills.ts"
  ],
  "include": [
    "**/*.spec.ts",
    "**/*.d.ts"
  ]
}

Step7. karmaの設定を変える(外部ライブラリの場合は不要かも?)

karmaはユニットテストのフレームワークです。
ユニットテスト時には当然index.htmlなんてないわけで、index.htmlに書いてあるライブラリをどこかでインクルードしないと未定義エラーが出ます。

そのための設定を管理するのがkarma.conf.jsで、そこにインクルードする設定をfilesで書きます。
ローカルファイルの場合はここの設定をしないとng testでエラーになりました。

※これで相当時間を取られたのはいい思い出…

karma.conf.js
 module.exports = function (config) {
   config.set({
+     files: [
+       // ↓のような感じで、index.htmlで列記しているものを読み込み
+       { pattern: './assets/lib/hogehoge/fugagfuga.js', included: true, watch: false },
+     ],
   });
 );

ポイント

  • filesの位置は先頭が良い(位置は関係ないと思いたいですが、先頭じゃないと動かない時があったような気がします)
  • index.htmlに書いてある順番と同じにする
  • 手抜きして{ pattern: './assets/lib/**/*.js', included: true, watch: false }と書かない

終わりに(テンプレ)

angularやAIを含めて、最新技術で保険業界を変えていきたい!という方をjustInCaseは募集しています!
justInCase では一緒にアプリ・サービスを作り上げていただけるエンジニアを絶賛募集中です。

参考リンク

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした