Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

Angular4アプリをAngular6にバージョンアップする際の嵌りどころ

こんにちは、阪本です。

f:id:acro-engineer:20180509102654p:plain:w200

最近、画面系を触ることが多くて、Angularを使ったシステムを3つほど作ったのですが、
LTSであるAngular6がつい最近リリースされたので、
作った中でシンプルなAngular4アプリケーションをAngular6にバージョンアップさせてみました。

前提

バージョンアップ前のAngularのバージョンは4.4.6でした。
Angular Material 2.0.0-beta.12を使っています。

ちなみに対象のアプリケーションは2KL強(クライアントサイド)の申請管理システムで、申請→承認のフローを実行するものです。

バージョンアップ開始

いざ、出陣!(何

バージョン変更

まずは、ncuを使って、package.jsonに書かれた各パッケージのバージョンを上げます。

C:\App> ncu
Using C:\App\package.json
[..................] | :
 @angular/animations                         4.4.6  →   6.0.0
 @angular/cdk                        2.0.0-beta.12  →   6.0.0
 @angular/common                             4.4.6  →   6.0.0
 @angular/compiler                           4.4.6  →   6.0.0
 @angular/compiler-cli                       4.3.6  →   6.0.0
 @angular/core                               4.4.6  →   6.0.0
 @angular/forms                              4.4.6  →   6.0.0
 @angular/http                               4.4.6  →   6.0.0
 @angular/material                   2.0.0-beta.12  →   6.0.0
 @angular/platform-browser                   4.4.6  →   6.0.0
 @angular/platform-browser-dynamic           4.4.6  →   6.0.0
 @angular/platform-server                    4.4.6  →   6.0.0
 @angular/router                             4.4.6  →   6.0.0
 core-js                                     2.4.1  →   2.5.6
 rxjs                                        5.4.3  →   6.1.0
 ts-helpers                                  1.1.1  →   1.1.2
 typescript                                  2.0.3  →   2.8.3
 zone.js                                    0.8.17  →  0.8.26
 @angular/cli                                1.3.2  →   6.0.0
 @types/node                                6.0.42  →  10.0.4
 codelyzer                                   3.1.2  →   4.3.0
 jasmine-core                                2.4.1  →   3.1.0
 jasmine-spec-reporter                       2.5.0  →   4.2.1
 karma                                       1.2.0  →   2.0.2
 karma-chrome-launcher                       2.0.0  →   2.2.0
 karma-jasmine                               1.0.2  →   1.1.2
 karma-remap-istanbul                        0.2.1  →   0.6.0
 protractor                                  4.0.9  →   5.3.1
 ts-node                                     1.2.1  →   6.0.3
 tslint                                      5.6.0  →  5.10.0
 webdriver-manager                          10.2.5  →  12.0.6

The following dependency is satisfied by its declared version range, but the installed version is behind. You can install the latest version without modifying your package file by using npm update. If you want to update the dependency in your package file anyway, run ncu -a.

 moment   2.19.2  →   2.22.1

Run ncu with -u to upgrade package.json


C:\App>

何やらたくさんバージョンアップされます。恐ろしや。。
思い切って、 ncu -u でバージョンを反映させます。

C:\App> ncu -u
Using C:\App\package.json
[..................] - :
 @angular/animations                         4.4.6  →   6.0.0
(中略)
 moment   2.19.2  →   2.22.1
Upgraded C:\App>

package.jsonが書き換わりました。
なお、TypeScriptは2.8.3にバージョンアップしていますが、
Angular6ではTypeScriptは2.7.xまでしか対応していないため、2.7.2に手で変更する必要があります

package.jsonのTypeScriptのバージョンを変更したら、新しいバージョンのパッケージをインストールします。

C:\App> npm update
npm WARN deprecated nodemailer@2.7.2: All versions below 4.0.1 of Nodemailer are deprecated. See https://nodemailer.com/status/
(中略)
================================================================================
The Angular CLI configuration format has been changed, and your existing configuration can
be updated automatically by running the following command:

  ng update @angular/cli
================================================================================
(中略)
C:\App>

途中、メッセージが出てくる通り、
Angular6では、設定ファイルの記述の仕方が変わっているため、
ng update @angular/cli で設定ファイルの更新が必要です。

C:\App> ng update @angular/cli
            Updating karma configuration
            Updating configuration
            Removing old config file (angular-cli.json)
            Writing config file (angular.json)
            Some configuration options have been changed, please make sure to update any npm scripts which you may have modified.
DELETE angular-cli.json
CREATE angular.json (3523 bytes)
UPDATE karma.conf.js (1074 bytes)
UPDATE src/tsconfig.spec.json (324 bytes)
UPDATE package.json (1466 bytes)
UPDATE tslint.json (2971 bytes)
(中略)
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.0.0 (node_modules\karma\node_modules\chokidar\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.3: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

angular-cli.jsonが削除され、代わりにangular.jsonになりました。
package.jsonには@angular-devkit/build-angularが追加されています。
追加されたパッケージは自動でインストールされます。

とりあえず、パッケージ自体のバージョンアップは完了です。

RxJSの破壊的変更対応

今回のバージョンアップの一番面倒なところが、これです。(Angular本体とちゃうんかい!)
Angularのバージョンアップに伴い、RxJSも5.4から6にバージョンアップ。
importを軒並み書き換え&コードも書き換えが必要です😖

ちなみに、rxjs-compatパッケージをインストールすれば、書き換えずに動作するらしいですが、
ここでは頑張って最新の書き方にしていきます💪

Observable

Angularとは切っても切れないObservable。
Angular4で使われていたRxJS 5.4では

import { Observable } from 'rxjs/Observable';

だったのが、RxJS 6では

import { Observable } from 'rxjs';

に。
rxjsから直接importするように変更されています。

Observable以外にも、BehaviorSubject等がrxjsからのimportに変わっています。

of / merge / throw

Observable.of のように、Observableのstaticメソッドで呼び出していたのが、RxJS 6では独立しました。

■RxJS 5.4

import 'rxjs/add/observable/of';
import 'rxjs/add/observable/merge';
import 'rxjs/add/observable/throw';

Observable.of(o);
Observable.merge(o1, o2);
Observable.throw(e);

■RxJS 6

import { of, merge, throwError } from 'rxjs';

of(o);
merge(o1, o2);
throwError(e);

例外発生は、throwErrorに名称変更されています。

map / catch

RxJS 5.4では observable.map().catch().subscribe() のような書き方をしていたのが、
RxJS 6では observable.pipe(map(), catch()).subscribe() のように、 pipe で挟むようになりました。
convertprocess はオリジナル関数。

■RxJS 5.4

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

this.http.get('url')
    .map(response => convert(response))
    .catch(error => Observable.of(error))
    .subscribe(response => process(response));

■RxJS 6

import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map, catchError } from 'rxjs/operators';

this.http.get('url')
    .pipe(
      map(response => convert(response)),
      catchError(error => of(error)))
    .subscribe(response => process(response));

pipe の中に、 mapcathError を複数書けるようになっています。

tap

余談ですが、RxJS 5.4では、戻り値のない関数も map で実行できましたが、
RxJS 6では tap が用意されており、こちらを使用するようになっています。

HTTPレスポンスの内容をログ出力する例を示します。

import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map, tap, catchError } from 'rxjs/operators';

this.http.get('url')
    .pipe(
      tap(response => console.log(response)),
      map(response => convert(response)),
      catchError(error => of(error)))
    .subscribe(response => process(response));

combineLatest

RxJS 5.4では、最初のObservableの combineLatest を呼んでいましたが、
RxJS 6では独立しました。こちらの方が自然ですね。
ただ、結合した結果はObservableの配列になるので、 map で変換します。
combine はオリジナル関数。

■RxJS 5.4

import 'rxjs/add/operator/combineLatest';

observable1.combineLatest(observable2, (o1, o2) => combine(o1, o2));

■RxJS 6

import { combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';

combineLatest(observable1, observable2)
  .pipe(map(values => combine(values[0], values[1]));

Angular

RxJSの対応が一通り完了したら、あとは大したことはありません。

今回のバージョンアップでは、Angularの内容は全く修正が要りませんでした。
Deprecatedなものを使っていた場合は、削除されている可能性があるため、直す必要があります。

Angular Material

こちらも、今回は何も変更せずに動作しました。
使っているのは matInputmat-selectmat-table くらいですが。

まとめ

RxJSの変更はつらいものがありましたが、それさえ乗り越えれば、最新のAngularを適用するのは楽チンです👍
Angular6はLTSでサポートも2019年10月まで続くので、
古いAngularを使っている場合は、アップデートを検討してみてください。

Acroquest Technologyでは、キャリア採用を行っています。


  • ビッグデータHadoop/Spark、NoSQL)、データ分析(Elasticsearch、Python関連)、Web開発(SpringCloud/SpringBoot、Angular)といった最新のOSSを利用する開発プロジェクトに関わりたい。
  • マイクロサービスDevOpsなどの技術を使ったり、データ分析機械学習などのスキルを活かしたい。
  • 社会貢献性の高いプロジェクトや、顧客の価値を創造するようなプロジェクトで、提案からリリースまで携わりたい。
  • 書籍・雑誌等の執筆や、対外的な勉強会の開催・参加を通した技術の発信、社内勉強会での技術情報共有により、エンジニアとして成長したい。

少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

www.wantedly.com