Angular6で”global is not defined”が出た時の回避策

2018.05.28

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

概要

どうも!クラスメソッドの岡です。

Angular6とAmplifyのcognitoパッケージでログイン画面を作成しようとしたら以下のエラーが発生して、原因が分からずかなり時間を消費しました。

Uncaught ReferenceError: global is not defined

調べて見ると、Amplifyに限らずAngular6とglobalオブジェクトを参照する外部ライブラリを利用している環境で発生してるようです。 Angular6のアップデートでNode.jsと外部ライブラリ間のshimを削除したのが原因のようです。 Angularのエラーではないので、Amplifyのアップデートまでの回避策として以下を実行しました。

詳細

環境

Angular

$ ng -v
Angular CLI: 6.0.0
Node: 10.0.0
OS: darwin x64
Angular: 6.0.2
... animations, cdk, common, compiler, compiler-cli, core, forms
... http, language-service, material, platform-browser
... platform-browser-dynamic, router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.6.3
@angular-devkit/build-angular     0.6.3
@angular-devkit/build-optimizer   0.6.3
@angular-devkit/core              0.6.3
@angular-devkit/schematics        0.6.3
@angular/cli                      6.0.0
@ngtools/webpack                  6.0.3
@schematics/angular               0.6.3
@schematics/update                0.6.3
rxjs                              6.1.0
typescript                        2.7.2
webpack                           4.8.3

Amplify

$ npm outdated aws-amplify
Package      Current  Wanted  Latest  Location
aws-amplify    0.4.0   0.4.1   0.4.1  management-console-test

期待する動作

実装した画面はこんな感じです。

実装コード(抜粋)

environment.ts

export const environment = {
  production: false,
  amplify: {
  Auth: {
      region: 'ap-northeast-1',
      userPoolId: 'ap-northeast-xxxxxxx' // ユーザープールのIDを入力,
      userPoolWebClientId: 'xxxxxxxxxxxxxx' // 追加したアプリクライアントのIDを入力}
  }
};

auth.service.ts

import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Observable, of, BehaviorSubject, from } from 'rxjs';
import { map, tap, catchError } from 'rxjs/operators';
import Amplify, { Auth } from 'aws-amplify';
import { environment } from './../../environments/environment';

@Injectable()
export class AuthService {

  public loggedIn: BehaviorSubject<boolean>;

  constructor(
    private router: Router
  ) {
    Amplify.configure(environment.amplify);
    this.loggedIn = new BehaviorSubject<boolean>(false);
  }

  // サインアップ
  public signUp(userid, password): Observable<any> {
    return from(Auth.signUp(userid, password));
  }

  // サインアップの検証
  public confirmSignUp(userid, code): Observable<any> {
    return from(Auth.confirmSignUp(userid, code));
  }

  // ログイン
  public signIn(userid, password): Observable<any> {
    return from(Auth.signIn(userid, password))
      .pipe(
        tap(() => this.loggedIn.next(true))
      );
  }

  // ログイン状態の取得
  public isAuthenticated(): Observable<boolean> {
    return from(Auth.currentAuthenticatedUser())
      .pipe(
        map(result => {
          this.loggedIn.next(true);
          return true;
        }),
        catchError(error => {
          this.loggedIn.next(false);
          return of(false);
        })
      );
  }

  // ログアウト
  public signOut() {
    from(Auth.signOut())
      .subscribe(
        result => {
          this.loggedIn.next(false);
          this.router.navigate(['/login']);
        },
        error => console.log(error)
      );
  }
}

login.conponent.ts

import { Component, OnInit } from '@angular/core';
import { AuthService } from './../services/auth.service';
import { Router } from '@angular/router';
import { FormControl, FormGroup, Validators, FormBuilder } from '@angular/forms';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
  title = '管理コンソール ログインページ';
  public loginForm: FormGroup;


  constructor(
    private formbuilder: FormBuilder,
    private auth: AuthService,
    private router: Router
  ) { }

  ngOnInit() {
    this.initForm();
  }

  initForm() {
    this.loginForm = this.formbuilder.group({
      'userid': ['', Validators.required],
      'password': ['', Validators.required]
    });
  }

  onSubmitLogin(value: any) {
    const userid = value.userid, password = value.password;
    this.auth.signIn(userid, password)
      .subscribe(
        result => {
          this.router.navigate(['/dashboard']);
        },
        error => {
          console.log(error);
          alert('ログインに失敗しました。');
        });
  }
}

エラー内容

上記の実装でコンパイルは成功しますが画面が表示されません。(!)

chromeの検証ページからコンソールを開くと以下のエラーが表示されていました。

Uncaught ReferenceError: global is not defined

回避策

polyfills.tsの末尾に以下を追記します。

// "global is not defined"の対応
(window as any).global = window;

もう一度ng serveで開いてみます。

またエラーが出ました。

Uncaught ReferenceError: Buffer is not defined

今度はBufferが見つからないようです。

再度、polyfills.tsの末尾に以下を追記します。

// "Buffer is not defined"の対応
global.Buffer = global.Buffer || require('buffer').Buffer;

すると、無事開けるようになりました。

まとめ

エラー解説(github)

Angular6へのアップデートでNode.jsと外部ライブラリ間のshimを削除したと書かれています。
Node.jsのドキュメントを見るとBufferも削除されていました。→Node.js8
Angularのコンパイル環境がNode.jsに適応した形なので、外部ライブラリが適応するまでは上記のように手動でシミングしましょう。

2018/06/06追記

Amplifyの公式ドキュメントにも追記されていました。

↓↓↓

AWS-amplify [angular-6-support]