<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:series="http://organizeseries.com/"
	>

<channel>
	<title>林 優一 &#8211; HTML5Experts.jp</title>
	<atom:link href="/frontainer/feed/" rel="self" type="application/rss+xml" />
	<link>https://html5experts.jp</link>
	<description>日本に、もっとエキスパートを。</description>
	<lastBuildDate>Sat, 07 Jul 2018 03:14:05 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.7.19</generator>
	<item>
		<title>FirebaseとAngular2を使ってリアルタイムでデータのやり取り【導入編】</title>
		<link>/frontainer/19689/</link>
		<pubDate>Mon, 27 Jun 2016 00:00:18 +0000</pubDate>
		<dc:creator><![CDATA[林 優一]]></dc:creator>
				<category><![CDATA[最新動向]]></category>
		<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[システム開発]]></category>
		<category><![CDATA[Angular2]]></category>
		<category><![CDATA[Firebase]]></category>

		<guid isPermaLink="false">/?p=19689</guid>
		<description><![CDATA[連載： Angular2で学ぶFirebase入門 (1)Firebaseはリアルタイム同期なデータベースを中心に、Auth認証やPushNotificationやユーザーの行動分析といったアプリケーションに最適な機能を...]]></description>
				<content:encoded><![CDATA[<div class="seriesmeta">連載： <a href="https://html5experts.jp/series/firebase/" class="series-379" title="Angular2で学ぶFirebase入門" data-wpel-link="internal">Angular2で学ぶFirebase入門</a> (1)</div><p>Firebaseはリアルタイム同期なデータベースを中心に、Auth認証やPushNotificationやユーザーの行動分析といったアプリケーションに最適な機能を提供するBasSです。先月行われたGoogle IOにて、PushNotificationや行動分析機能が追加されるなど話題となりました。</p>

<p>今回はFirebaseのリアルタイムベースとAngular2を用いて、リアルタイムにデータをやり取りする方法について解説します。</p>

<h2>Firebaseのリアルタイムデータベース</h2>

<p>Firebaseのリアルタイムデータベースはクラウドホスト型のNoSQLデータベースです。JSONの形式でデータは保存されます。
APIはもちろん、iOS/Android/Web用のSDKが提供されているので、非常に簡単に導入することができます。</p>

<p>また、ネットワークが途中で切れた場合は自動的に再接続を行い、復帰後に改めてデータが同期されます。同時100コネクション、1GBのデータ、10GBの転送量までは無料で使うことができるので、まず試してみるといったことができます。</p>

<p>今回はこの無料プランとAngular2を使って進めていきます。</p>

<h2>Angular CLIで環境構築</h2>

<p>Angular2の環境構築が初めての方でも簡単に導入できるように、Angular2ではCLIが公開されています。これを利用して環境を構築していきます。（※別途、Node.jsのインストールが必要です）</p>

<p><a href="https://github.com/angular/angular-cli" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">angular-cli</a></p>

<p>まずはnpmを使って、angular-cliをインストールします。</p>

<p></p><pre class="crayon-plain-tag">npm install -g angular-cli</pre><p></p>

<p>インストールが成功していれば、<code>ng version</code>でバージョンが表示されます。</p>

<p></p><pre class="crayon-plain-tag">angular-cli: 1.0.0-beta.5
node: 5.6.0
os: darwin x64</pre><p></p>

<p>続いて、<code>ng new</code>コマンドを用いてプロジェクトを作成します。newの後には任意のプロジェクト名を入力してください。（angularやfirebaseという名称を用いるとパッケージ名が重複してしまうため、関連ライブラリのインストールに失敗するのでご注意ください）</p>

<p></p><pre class="crayon-plain-tag">ng new sample
cd sample</pre><p></p>

<p>今回は<code>ng new sample</code>でプロジェクトを作成します。</p>

<p>sampleというフォルダができるので<code>cd sample</code>で移動し、この状態で <code>ng serve</code> コマンドを実行するとサンプルのアプリケーションがビルドされ、<code>http://localhost:4200/</code>にブラウザでアクセスすると<code>sample works!</code>と表示されます。</p>

<p>これで開発環境の準備が整いました。</p>

<h2>AngularFire導入</h2>

<p>本題のAngularFireを導入していきます。</p>

<p><a href="https://github.com/angular/angularfire2" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">angularfire2</a></p>

<p></p><pre class="crayon-plain-tag">npm install angularfire2 firebase@2.4.2 --save</pre><p></p>

<p>※ 6月14日現在の最新版は、firebase 2.4.2に対応しています。</p>

<p>続いて、firebase用の型定義ファイルをインストールします。
<code>typings</code>がインストールされていない場合は <code>npm i -g typings</code>を実行してインストールをしてください。</p>

<p></p><pre class="crayon-plain-tag">typings install dt~firebase --save --global</pre><p></p>

<p>※ 注意</p>

<p>typings 1系と0系では、コマンドやインストールされるファイルの構成が異なります。現在のバージョンのangular-cliでは0系でインストールされますが、npmでインストールされる最新版は1系になります。本稿では1系がインストールされている状態で解説を行います。0系の場合は以下のtypingsに追記する作業は必要ありません。</p>

<p><code>src/typings.d.ts</code>にインストールされたfirebaseの型定義ファイル群を追記します。</p>

<p></p><pre class="crayon-plain-tag">/// &lt;reference path="../typings/browser.d.ts" /&gt;
/// &lt;reference path="../typings/index.d.ts" /&gt;
declare var module: { id: string };</pre><p></p>

<p>angular-cli-build.jsを開き、<code>vendorNpmFiles</code>に<code>'angularfire2/**/*.js'</code> <code>'firebase/lib/*.js'</code> を追記します。</p>

<p></p><pre class="crayon-plain-tag">var Angular2App = require('angular-cli/lib/broccoli/angular2-app');

module.exports = function(defaults) {
  return new Angular2App(defaults, {
    vendorNpmFiles: [
      'systemjs/dist/system-polyfills.js',
      'systemjs/dist/system.src.js',
      'zone.js/dist/**/*.+(js|js.map)',
      'es6-shim/es6-shim.js',
      'reflect-metadata/**/*.+(js|js.map)',
      'rxjs/**/*.+(js|js.map)',
      '@angular/**/*.+(js|js.map)',
      'angularfire2/**/*.js', // &lt;- 追加
      'firebase/lib/*.js'      // &lt;- 追加
    ]
  });
};</pre><p></p>

<p>続いて、/src/system-config.tsに以下のようにfirebase,angularfire2の記述を追記します。</p>

<p></p><pre class="crayon-plain-tag">/** Map relative paths to URLs. */
const map: any = {
  'firebase': 'vendor/firebase/lib/firebase-web.js',
  'angularfire2': 'vendor/angularfire2'
};

/** User packages configuration. */
const packages: any = {
  angularfire2: {
    defaultExtension: 'js',
    main: 'angularfire2.js'
  }
}</pre><p></p>

<p>これでAngular2でFirebaseを使う準備が整いました。</p>

<h2>Firebaseの登録</h2>

<p>続いて、Firebaseの登録と設定を行っていきましょう。</p>

<p><a href="https://firebase.google.com/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">firebase</a></p>

<p><img src="/wp-content/uploads/2016/06/Screen-Shot-0028-06-14-at-22.46.07-640x194.png" alt="Screen Shot 0028-06-14 at 22.46.07" width="640" height="194" class="alignnone size-large wp-image-19695" srcset="/wp-content/uploads/2016/06/Screen-Shot-0028-06-14-at-22.46.07.png 640w, /wp-content/uploads/2016/06/Screen-Shot-0028-06-14-at-22.46.07-300x91.png 300w, /wp-content/uploads/2016/06/Screen-Shot-0028-06-14-at-22.46.07-207x63.png 207w" sizes="(max-width: 640px) 100vw, 640px" /></p>

<p>FirebaseはGoogleアカウントで登録することができます。登録が完了すると、プロジェクトを作るためのボタンが表示されます。</p>

<p><img src="/wp-content/uploads/2016/06/Screen-Shot-0028-06-14-at-22.47.30-640x235.png" alt="Screen Shot 0028-06-14 at 22.47.30" width="640" height="235" class="alignnone size-large wp-image-19696" srcset="/wp-content/uploads/2016/06/Screen-Shot-0028-06-14-at-22.47.30.png 640w, /wp-content/uploads/2016/06/Screen-Shot-0028-06-14-at-22.47.30-300x110.png 300w, /wp-content/uploads/2016/06/Screen-Shot-0028-06-14-at-22.47.30-207x76.png 207w" sizes="(max-width: 640px) 100vw, 640px" /></p>

<p>「新規プロジェクトを作成」ボタンをクリックして、アプリケーションを作成しましょう。</p>

<p><img src="/wp-content/uploads/2016/06/Screen-Shot-0028-06-14-at-22.48.17.png" alt="Screen Shot 0028-06-14 at 22.48.17" width="449" height="445" class="alignnone size-full wp-image-19697" srcset="/wp-content/uploads/2016/06/Screen-Shot-0028-06-14-at-22.48.17.png 449w, /wp-content/uploads/2016/06/Screen-Shot-0028-06-14-at-22.48.17-150x150.png 150w, /wp-content/uploads/2016/06/Screen-Shot-0028-06-14-at-22.48.17-300x297.png 300w, /wp-content/uploads/2016/06/Screen-Shot-0028-06-14-at-22.48.17-207x205.png 207w" sizes="(max-width: 449px) 100vw, 449px" /></p>

<p>アプリケーションが作成できたら「Database」ページを開きます。
以下のように、まだデータベースには何も入っていません。まずはこの画面から初期データを登録してみましょう。</p>

<p><img src="/wp-content/uploads/2016/06/Screen-Shot-0028-06-14-at-22.55.35.png" alt="Screen Shot 0028-06-14 at 22.55.35" width="294" height="57" class="alignnone size-full wp-image-19698" srcset="/wp-content/uploads/2016/06/Screen-Shot-0028-06-14-at-22.55.35.png 294w, /wp-content/uploads/2016/06/Screen-Shot-0028-06-14-at-22.55.35-207x40.png 207w" sizes="(max-width: 294px) 100vw, 294px" /></p>

<p>このようにデータを作成しました。</p>

<p></p><pre class="crayon-plain-tag">{
  items: {
    0: {
      value: &#039;item0001&#039;
    },
    2: {
      value: &#039;item0002&#039;
    },
    2: {
      value: &#039;item0003&#039;
    }
  }
}</pre><p></p>

<p>Firebase上では、すべてオブジェクトの形式でデータが構成されるようにしておきます。</p>

<p>続いて、「ルール」のタブをクリックしてルールを設定します。
ルールはデータベースへの値に対してのアクセス権の設定を行うことができます。JSONの形式でデータを定義していきます。</p>

<p><img src="/wp-content/uploads/2016/06/Screen-Shot-0028-06-14-at-22.58.07-640x333.png" alt="Screen Shot 0028-06-14 at 22.58.07" width="640" height="333" class="alignnone size-large wp-image-19699" srcset="/wp-content/uploads/2016/06/Screen-Shot-0028-06-14-at-22.58.07.png 640w, /wp-content/uploads/2016/06/Screen-Shot-0028-06-14-at-22.58.07-300x156.png 300w, /wp-content/uploads/2016/06/Screen-Shot-0028-06-14-at-22.58.07-207x108.png 207w" sizes="(max-width: 640px) 100vw, 640px" /></p>

<p>詳しいルールの記載は、以下のページに説明があります。
<a href="https://html5exp-sample.firebaseio.com/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">https://html5exp-sample.firebaseio.com/</a></p>

<p>ここではサンプルということで、読み書きともに誰からでもできるように設定しておきましょう。</p>

<p></p><pre class="crayon-plain-tag">{
  "rules": {
    "items": {
      ".read": "true",
      ".write": "true"
    }
  }
}</pre><p></p>

<p>これによってデータベースの<code>items</code>以下は、読み書きともに誰でも許可されるようになりました。では、これで登録したデータが読み取れるか試してみましょう。/src/main.tsを開きます。</p>

<p></p><pre class="crayon-plain-tag">import { bootstrap } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { SampleAppComponent, environment } from './app/';
import { FIREBASE_PROVIDERS, defaultFirebase } from 'angularfire2'; // &lt;- add

if (environment.production) {
  enableProdMode();
}

bootstrap(SampleAppComponent,[
  FIREBASE_PROVIDERS, // &lt;- add
  defaultFirebase('https://&lt;your-firebase-app&gt;.firebaseio.com/') // &lt;- add
]);</pre><p></p>

<p><code>main.ts</code> にangularfire2のファイルをimportし、bootstrap関数の第２引数に<code>FIREBASE_PROVIDERS</code>と <code>defaultFirebase('https://.firebaseio.com/')</code> を配列で渡しておきます。</p>

<p>こうすることによって、今後Angular2のコンポーネント内でFirebaseに必要な設定とプロバイダが提供されるようになります。</p>

<p>続いて、画面にデータを表示するためのコンポーネントに変更を加えます。</p>

<p></p><pre class="crayon-plain-tag">import { Component } from '@angular/core';
import { AngularFire, FirebaseListObservable } from 'angularfire2'; //&lt;- add

@Component({
  moduleId: module.id,
  selector: 'sample-app',
  templateUrl: 'sample.component.html',
  styleUrls: ['sample.component.css']
})
export class SampleAppComponent {
  title = 'sample works!';
  items: FirebaseListObservable&lt;any[]&gt;;
  constructor(af: AngularFire) {
		this.items = af.database.list('/items');
  }
}</pre><p></p>

<p></p><pre class="crayon-plain-tag">&lt;h1&gt;{{title}}&lt;/h1&gt;
&lt;ul *ngFor="let item of items | async"&gt;
  &lt;li class="text"&gt;
    {{item.value}}
  &lt;/li&gt;
&lt;/ul&gt;</pre><p></p>

<p><code>*ngFor="let item of items | async"</code> のasyncはAsyncPipeと呼ばれるAngular2にはじめから組み込まれているPipeのひとつです。これはitemsのObservableをsubscribeしたときの引数を自動的に取得して表示してくれるPipeです。詳しい使い方や解説は以下のページにありますので、こちらをご一読ください。</p>

<p><a href="https://angular.io/docs/ts/latest/guide/pipes.html#!#async-pipe" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Async Pipe</a></p>

<p><code>ng serve</code> コマンドを改めて実行すると、Firebaseで登録されたデータが表示されるようになります。</p>

<h2>データの追加</h2>

<p>次にブラウザからデータを追加する処理を実装していきましょう。</p>

<p></p><pre class="crayon-plain-tag">export class SampleAppComponent {
  title = 'sample works!';
  items: FirebaseListObservable&lt;any[]&gt;;
  constructor(af: AngularFire) {
    this.items = af.database.list('/items');
  }
  addItem(value:string) {
    this.items.push({value:value});
  }
}</pre><p></p>

<p></p><pre class="crayon-plain-tag">&lt;h1&gt;{{title}}&lt;/h1&gt;
&lt;ul *ngFor="let item of items | async"&gt;
  &lt;li class="text"&gt;
    {{item.value}}
  &lt;/li&gt;
&lt;/ul&gt;

&lt;form (ngSubmit)="addItem(newItem.value);newItem.value=''"&gt;
  &lt;input type="text" #newItem&gt;
  &lt;button&gt;send&lt;/button&gt;
&lt;/form&gt;</pre><p></p>

<p><code>FirebaseListObservable</code>にデータを<code>push</code>するだけで配列データにデータを追加することができます。</p>

<h2>データの更新</h2>

<p>今度はリストのデータを更新してみましょう。</p>

<p></p><pre class="crayon-plain-tag">export class SampleAppComponent {
  title = 'sample works!';
  items: FirebaseListObservable&lt;any[]&gt;;
  constructor(af: AngularFire) {
    this.items = af.database.list('/items');
  }
  addItem(value:string) {
    this.items.push({value:value});
  }
  updateItem(key:string,value:string) {
	  this.items.update(key,{value:value});
  }
}</pre><p></p>

<p></p><pre class="crayon-plain-tag">&lt;h1&gt;{{title}}&lt;/h1&gt;
&lt;ul *ngFor="let item of items | async"&gt;
  &lt;li class="text"&gt;
    {{item.value}}
    &lt;input [(ngModel)]="item.value" (keyup)="updateItem(item.$key,item.value)"/&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;form (ngSubmit)="addItem(newItem.value);newItem.value=''"&gt;
  &lt;input type="text" #newItem&gt;
  &lt;button&gt;send&lt;/button&gt;
&lt;/form&gt;</pre><p></p>

<p><code>this.items.update(key,{value:value});</code>のように更新したいデータの$keyと値を渡すことで更新ができます。
Firebaseのオブジェクトには$keyというプロパティがあり、各データのKey名を取得することができます。
pushで追加されたデータは、一意なKeyが自動で振られるようになっています。</p>

<h2>データの削除</h2>

<p>続いて、リストのデータを削除してみましょう。</p>

<p></p><pre class="crayon-plain-tag">export class SampleAppComponent {
  title = 'sample works!';
  items: FirebaseListObservable&lt;any[]&gt;;
  constructor(af: AngularFire) {
    this.items = af.database.list('/items');
  }
  addItem(value:string) {
    this.items.push({value:value});
  }
  updateItem(key:string,value:string) {
	  this.items.update(key,{value:value});
  }
  deleteItem(key:string) {
	  this.items.remove(key);
  }
  deleteAll() {
	  this.items.remove();
  }
}</pre><p></p>

<p></p><pre class="crayon-plain-tag">&lt;h1&gt;{{title}}&lt;/h1&gt;
&lt;ul *ngFor="let item of items | async"&gt;
  &lt;li class="text"&gt;
    {{item.value}}
    &lt;input [(ngModel)]="item.value" (keyup)="updateItem(item.$key,item.value)"/&gt;
    
    &lt;button (click)="removeItem(item.$key)"&gt;削除&lt;/button&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;form (ngSubmit)="addItem(newItem.value);newItem.value=''"&gt;
  &lt;input type="text" #newItem&gt;
  &lt;button&gt;send&lt;/button&gt;
&lt;/form&gt;

&lt;button (click)="removeAll()"&gt;全削除&lt;/button&gt;</pre><p></p>

<p>更新と同様に<code>this.items.remove(key);</code>と削除したいデータのKeyを渡すことで、そのデータのみを削除することができます。</p>

<p><code>this.items.remove();</code>とKeyを渡さずにremoveするとリスト全体が削除されるので注意してください。</p>

<p>このように非常に簡単にデータの読み書きが実装できてしまいます。</p>

<h2>オブジェクトデータの取り扱い</h2>

<p>続いて、今度はリストデータでなく単一のオブジェクトのデータを扱ってみましょう。Firebaseの管理画面でDatabaseのデータに以下のような<code>constants</code>を追加しました。</p>

<p></p><pre class="crayon-plain-tag">{
  constants: {
    title: "My first firebase app"
  },
  items: {
    ...
  }
}</pre><p></p>

<p>ルール設定ページにいき、constantsは読み込み専用に設定しておきます。</p>

<p></p><pre class="crayon-plain-tag">{
  "rules": {
    "constants": {
      ".read": "true",
      ".write": "false"
    },
    "items": {
      ".read": "true",
      ".write": "true"
    }
  }
}</pre><p></p>

<p>src/app/sample.component.tsを、以下のように修正します。</p>

<p></p><pre class="crayon-plain-tag">import { Component } from '@angular/core';
import { AngularFire, FirebaseListObservable, FirebaseObjectObservable } from 'angularfire2'; //&lt;- add

@Component({
  moduleId: module.id,
  selector: 'sample-app',
  templateUrl: 'sample.component.html',
  styleUrls: ['sample.component.css']
})
export class SampleAppComponent {
  constants: FirebaseObjectObservable&lt;any&gt;;
  items: FirebaseListObservable&lt;any[]&gt;;
  constructor(af: AngularFire) {
    this.constants = af.database.object('/constants');
    this.items = af.database.list('/items');
  }
  addItem(value:string) {
    this.items.push({value:value});
  }
  updateItem(key:string,value:string) {
    this.items.update(key,{value:value});
  }
  deleteItem(key:string) {
    this.items.remove(key);
  }
  deleteAll() {
    this.items.remove();
  }
}</pre><p></p>

<p>そして、ビューのタイトル部分をconstantsから取得して表示させるようにします。</p>

<p></p><pre class="crayon-plain-tag">&lt;h1&gt;{{(constants | async)?.title}}&lt;/h1&gt;
&lt;ul *ngFor="let item of items | async"&gt;
  &lt;li class="text"&gt;
    &lt;input [(ngModel)]="item.value" (keyup)="updateItem(item.$key,item.value)"/&gt;

    &lt;button (click)="deleteItem(item.$key)"&gt;削除&lt;/button&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;form (ngSubmit)="addItem(newItem.value);newItem.value=''"&gt;
  &lt;input type="text" #newItem&gt;
  &lt;button&gt;send&lt;/button&gt;
&lt;/form&gt;

&lt;button (click)="deleteAll()"&gt;全削除&lt;/button&gt;</pre><p></p>

<p>こうすることで、オブジェクトのデータを読み込んで画面に表示させることができました。</p>

<h2>オブジェクトの更新</h2>

<p>オブジェクトの更新をする際は、リストと同様にupdate関数を用いて変更を行うことができます。その時にはルールで書き込みが許可されている必要があります。</p>

<p></p><pre class="crayon-plain-tag">this.constants.update({title:"new title"});</pre><p></p>

<h2>オブジェクトの削除</h2>

<p>オブジェクトの削除を行う際にはremove関数を用いますが、以下のようにremoveするとconstantsごと削除されます。</p>

<p></p><pre class="crayon-plain-tag">this.constants.remove(); // constantsごと削除</pre><p></p>

<p>１つのプロパティのみを削除したい場合は、以下のように削除したいプロパティまで取得した上でremove関数を実行します。</p>

<p></p><pre class="crayon-plain-tag">af.database.object('/constants/title').remove(); //titleだけ削除</pre><p></p>

<h2>まとめ</h2>

<p>このようにAngular2とFirebaseを使うことで、簡単にリアルタイムにデータのやり取りを行うことができるようになります。</p>

<p>今回はサンプルということで誰でも読み書きできる形にしていますが、ユーザー認証をFirebaseで作ったり、既にあるログイン情報を利用して認証させたりできるので、新規でも追加でも導入しやすくなっています。</p>

<p>次回はその認証機能を用いてログインすると書き込みができるようにしていきたいと思います。</p>
]]></content:encoded>
		
		<series:name><![CDATA[Angular2で学ぶFirebase入門]]></series:name>
	</item>
		<item>
		<title>Angular1から2への移行プロセス、詳しく語ります</title>
		<link>/frontainer/18780/</link>
		<pubDate>Thu, 28 Apr 2016 03:05:16 +0000</pubDate>
		<dc:creator><![CDATA[林 優一]]></dc:creator>
				<category><![CDATA[最新動向]]></category>
		<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[システム開発]]></category>
		<category><![CDATA[Angular]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[TypeScript]]></category>

		<guid isPermaLink="false">/?p=18780</guid>
		<description><![CDATA[Angular2のリリースが近づいてきている今、Angular1で開発された資産を、どうやってAngular2に移行していけばよいのでしょうか？ この記事では、実際に移行を行う上でのプロセスを具体的に示していきます。 編...]]></description>
				<content:encoded><![CDATA[<p>Angular2のリリースが近づいてきている今、Angular1で開発された資産を、どうやってAngular2に移行していけばよいのでしょうか？</p>

<p>この記事では、実際に移行を行う上でのプロセスを具体的に示していきます。</p>

<p><em>編集部注: この記事は、2016年3月21日に開催された「<a href="http://ngjapan.org/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">ng-japan 2016</a>」のセッション「Angular1.4で作られた自社マイクロサービスを2へマイグレーション」についての、講演者自身によるレポートです。講演内容に加えて、講演者自身による解説や追記によって、よりわかりやすく詳細な記事に仕上げていただきました。</em></p>

<p><img src="/wp-content/uploads/2016/04/DSC027921.jpg" alt="" width="550" height="382" class="aligncenter size-full wp-image-18853" srcset="/wp-content/uploads/2016/04/DSC027921.jpg 550w, /wp-content/uploads/2016/04/DSC027921-300x208.jpg 300w, /wp-content/uploads/2016/04/DSC027921-207x144.jpg 207w" sizes="(max-width: 550px) 100vw, 550px" /></p>

<p>セッションの講演資料と動画はこちらになります。</p>

<h3>講演資料</h3>

<iframe class="embedly-embed" src="//cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.slideshare.net%2Fslideshow%2Fembed_code%2Fkey%2FMVkoVxeu8i10TT&#038;url=http%3A%2F%2Fwww.slideshare.net%2Ffrontainer%2Fangular142&#038;image=http%3A%2F%2Fcdn.slidesharecdn.com%2Fss_thumbnails%2Fng-japan2016-160321133747-thumbnail-4.jpg%3Fcb%3D1458569152&#038;key=internal&#038;type=text%2Fhtml&#038;schema=slideshare" width="500" height="417" scrolling="no" frameborder="0" allowfullscreen></iframe>

<h3>講演動画（4:19:52付近から始まります）</h3>

<iframe width="560" height="315" src="https://www.youtube.com/embed/4QVMhM75XK8" frameborder="0" allowfullscreen></iframe>

<h2>Angular1から2へ移行する前に</h2>

<p>Angular2への移行を始める前に、Angular1の構成やソースコードを整理する必要があります。公式サイトに「<a href="https://angular.io/docs/ts/latest/guide/upgrade.html" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">UPGRADING FROM 1.X</a>」という1からアップグレードする手順の解説があります。その手順に加え、実際のアプリケーションの構成を考慮すると以下のような流れになります。</p>

<ul>
<li>事前準備</li>
<li>ng-upgrade</li>
<li>コンポーネント(ディレクティブ)の移植</li>
<li>サービスの移植</li>
<li>ページコンポーネントの移植</li>
<li>フィルターの移植</li>
<li>ルーターの移植</li>
<li>ng1ライブラリの置き換え</li>
<li>ng-upgradeの除去</li>
</ul>

<p>※ セッションでは上記の「サービスの移植」までの前段のみを取り上げています。</p>

<h2>事前準備</h2>

<p>Angular2では1とは異なる点が多く、そのままではすぐにアップグレードすることはできません。そこでまずは1で近い構成にする事前準備が必要になります。</p>

<h3>AngularJS Style Guideに従った構成になっていること</h3>

<p><a href="https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">johnpapa/angular-styleguide Angular 1 Style Guide</a></p>

<p>上記スタイルガイドの<a href="https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#rule-of-1" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Rule of 1</a>及び<a href="https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#folders-by-feature-structure" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Folders-by-Feature Structure</a>、<a href="https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#modularity" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Modularity</a>の３点には最低限合わせておきましょう。</p>

<p>Angular2ではコンポーネントが中心になり、ファイルの構成もこのスタイルガイドと近い構成になっていきます。あらかじめコンポーネントごとにファイルを分けて置くことで、基本的な設計もコンポーネント指向に近づけておきます。</p>

<p>「<a href="http://qiita.com/laco0416/items/edfa917583af4593ad6c" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">その使い方はもう古いかも？AngularJS老化チェック</a>」という記事の内容が現在のコードに当てはまらないかどうかをチェックしておきましょう。当てはまったものはスタイルガイドに沿ってリファクタリングを行いましょう。</p>

<h3>モジュールローダー（モジュールバンドラー）（Browserify/Webpackなど）を使っていること</h3>

<p>Angular2は必要なスクリプトを読み込んでいって作ることもできますが、モジュールローダー（モジュールバンドラー）前提で話が進んでいくことがあり、導入されていないと理解が難しい状況になります。何も導入していなければBrowserifyかWebpackをおすすめします。公式サイトではSystemJSを用いていますが、Browserify/Webpackを使う場合、必ずしもSystemJSを使う必要はありません。</p>

<ul>
<li><a href="http://browserify.org/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Browserify</a></li>
<li><a href="https://webpack.github.io/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Webpack</a></li>
<li><a href="https://github.com/systemjs/systemjs" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">SystemJS</a></li>
</ul>

<h3>TypeScriptへ移行</h3>

<p>Angular2入門のページではBabelやTypeScriptなどの利用を、とありますが、マイグレーション時においては<a href="https://www.typescriptlang.org/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">TypeScript</a>への移行を先にすることを強くお勧めします。</p>

<p>例えば先ほどの「UPGRADING FROM 1.X」のページですが、JavaScript用のドキュメントは準備中となっています（2016年4月現在）。</p>

<p>このような状況からみて、今後の新しい機能についてもTypeScript優先で情報提供されることがあることでしょう。また、既にAngular2に取り組んでいる先人たちのノウハウ（StackOverflowなど）もTypeScriptをベースにしているものが多いので、これを機にTypeScriptを使うと良いでしょう。型などを用いることで移行時の不具合を見つける手助けにもなります。</p>

<h3>コンポーネントディレクティブの使用</h3>

<p>Angular1.5からコンポーネント機能が提供されました。Angular2の基本はコンポーネントで構成されます。そのため、既存のアプリケーションをコンポーネント化していくことで移行がスムーズになります。対応ブラウザに問題がなければまずはAngular1.5にアップグレードし、ディレクティブや共通部分をコンポーネント化しましょう。</p>

<p><a href="https://docs.angularjs.org/guide/component" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Angular Developer Guide / Component</a></p>

<p>ここまでを一通り行い、Angular2への移行の準備が整います。</p>

<p>この時点で難しいと思われる方もいるかもしれません。しかしながら、Angular2でも準備と同様の環境が求められます。Angular2はパフォーマンス改善やコンポーネント指向など魅力的な変更があるのでぜひとも移行していきたいところなのです。いずれにしても、今後も大きなフロントエンド開発においてはこのようなモダン構成が求められることでしょうから、そうした練習としてAngular2を採用するのも良いでしょう。</p>

<h2>Angular2の導入</h2>

<p>Angular2の方へ入っていきたいと思います。最低限必要なものとして以下のものが必要になります。（2016/4/24時点）</p>

<ul>
<li>angular2@2.0.0-beta.15</li>
<li>systemjs@0.19.26</li>
<li>refrect-metadata@0.1.2</li>
<li>rxjs@5.0.0-beta.2</li>
<li>zone.js@0.6.10</li>
<li>es6-shim@0.35.0</li>
</ul>

<p><code>npm install</code>でまとめて導入できます。</p>

<p></p><pre class="crayon-plain-tag">npm install angular2@2.0.0-beta.15 systemjs@0.19.26 refrect-metadata@0.1.2 rxjs@5.0.0-beta.2 zone.js@0.6.10 es6-shim@0.35.0</pre><p></p>

<p>まずAngularの起動方法を変更します。</p>

<p></p><pre class="crayon-plain-tag">&lt;body ng-app="app"&gt;...&lt;/body&gt;</pre><p></p>

<p>これは以下のように<code>angular.bootstrap</code>関数を用いて定義するようにします。この際HTMLに<code>ng-app</code>は不要です。</p>

<p></p><pre class="crayon-plain-tag">angular.bootstrap(document.body,['app'],{strictDi:true});</pre><p></p>

<p>さらにこの部分を<strong>ng-upgrade</strong>と呼ばれているangular2/upgradeの<code>UpgradeAdapter</code>というものを用いてAngularを起動していきます。</p>

<p></p><pre class="crayon-plain-tag">import {UpgradeAdapter} from 'angular2/upgrade';
export const Adapter = new UpgradeAdapter();</pre><p></p>

<p></p><pre class="crayon-plain-tag">import {Adapter} from './adapter.ts';
Adapter.bootstrap(document.body,['app'],{strictDi:true});</pre><p></p>

<p>このようにupgradeの中にある<code>UpgradeAdapter</code>を経由して実行をします。今後1と2を混在させた状態でアプリケーションを動かすためのハブのような役割をこのAdapterが行ってくれます。今後いろいろな場面でAdapterを呼び出して使うことになるので、あらかじめ別のファイルにしておいて外部から参照できるようにしておきましょう。</p>

<p>続いて<code>angular2-polyfill.js</code>を読み込みます。後々<code>Observable</code>を使う時に必要になるので、忘れないように入れておきましょう。</p>

<p></p><pre class="crayon-plain-tag">import 'angular2/bundles/angular2-polyfills';

// for old browsers
import 'es6-shim/es6-shim.min.js';
import 'angular2/es6/dev/src/testing/shims_for_IE';</pre><p></p>

<p>ここでユニットテストなどで<code>PhantomJS</code>などで実行しているとエラーが出ます。<code>es6-shim</code>を入れることで回避することができます。</p>

<p><a href="https://github.com/radify/karma-es6-shim" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">karma-es6-shim</a></p>

<p>TypeScriptにもes6の型定義ファイルが必要になるので、<code>es6-shim</code>の型定義ファイルを入れておきましょう。</p>

<p></p><pre class="crayon-plain-tag">typings install es6-shim --save --ambient</pre><p></p>

<p>以上で移行の前段はできました。ここからはAngular1で作ったものを徐々に2へと置き換えて行く作業になります。</p>

<h2>コンポーネントを置き換え</h2>

<p>まずは他にあまり依存しない、Directiveのrestrict:&#8217;E&#8217;で実装されていたものをAngular2へ移行していきます。大きなところからやりたくなりますが、<strong>移行は末端から順番にやるようにしましょう</strong>。</p>

<p>以下はAngular1のコンポーネントを使って書かれたローディングの例です。</p>

<p></p><pre class="crayon-plain-tag">const template = require('./loading.html');
class LoadingCtrl {
    constructor(private LoadingService) {}
}
angular.module(‘app')
    .component('globalLoading',{
        controller: LoadingCtrl,
        transclude: true,
        template: template
    });
export default LoadingCtrl;</pre><p></p>

<p></p><pre class="crayon-plain-tag">class LoadingService {
    count:number = 0;
    loading:boolean = false;
    constructor() {}
    start() {
        ++this.count;
        this.loading = true;
    }
    stop() {
        --this.count;
        if (this.count === 0) {
            this.loading = false;
        }
    }
}
angular.module(‘app')
    .service('LoadingService',LoadingService);</pre><p></p>

<p></p><pre class="crayon-plain-tag">&lt;div ng-show="LoadingService.loading" class="loading"&gt;
    &lt;div class="loading-text" ng-transclude&gt;&lt;/div&gt;
&lt;/div&gt;</pre><p></p>

<p>このローディングコンポーネントをAngular2に置き換えていきます。
まずはコンポーネント部分を置き換えていきましょう。</p>

<p></p><pre class="crayon-plain-tag">import {Adapter} from '../../core/adapter';
import {Component} from 'angular2/core';
import {LoadingService} from './LoadingService';
const template = require('./loading.html');

@Component({
    selector: 'global-loading',
    template: template
})
export class LoadingCtrl {
    constructor(private LoadingService:LoadingService) {}
}
angular.module('app').directive('globalLoading',&lt;angular.IDirectiveFactory&gt;Adapter.downgradeNg2Component(LoadingCtrl));</pre><p></p>

<p>Angular2の基本はコンポーネントになっているので、出来る限りコンポーネントに置き換えておきましょう。</p>

<p>Angular2で書かれたコンポーネントは1環境では呼び出すことができません。そこでディレクティブに<code>Adapter.downgradeNg2Component()</code>を用いて登録することで、Angular1環境下においても使うことができるようになります。</p>

<p>続いて、サービス部分の移植です。</p>

<p></p><pre class="crayon-plain-tag">import {Adapter} from '../../core/adapter';
import {Injectable} from 'angular2/core';

@Injectable()
export class LoadingService {
    count:number = 0;
    loading:boolean = false;
    constructor() {}
    start() {
        ++this.count;
        this.loading = true;
    }
    stop() {
        --this.count;
        if (this.count === 0) {
            this.loading = false;
        }
    }
}
angular.module('app').factory('LoadingService',Adapter.downgradeNg2Provider(LoadingService));
Adapter.addProvider(LoadingService);</pre><p></p>

<p><code>@Injectable()</code>をつけてInject(DI)可能であることを示します。
コンポーネント同様に、Angular2記法のServiceをAngular1からもDIできるように<code>Adapter.downgradeNg2Provider()</code>を使ってダウングレードして登録しておきます。</p>

<p>加えて、<code>Adapter.addProvider()</code>で作ったサービスを<code>Provider</code>に登録する必要があります。これは<code>UpgradeAdapter</code>を使っている場合で、Angular2のコンポーネントからDIさせるために必要になります。</p>

<p>これを行わないと以下のようなエラーとなります。</p>

<p><code>
EXCEPTION: Error: [$injector:unpr] Unknown provider: LoadingServiceProvider &lt;- LoadingService &lt;- LoadingInterceptor &lt;- $http &lt;- $templateFactory &lt;- $view &lt;- $state</code></p>

<p>同様にHTMLもAngular2記法に置き換えを行います。</p>

<p>ディレクティブでは<code>ng-transclude</code>を使っていましたが、Angular2では<code>ng-content</code>になっているのは注意が必要です。</p>

<p>また、細かいところですが、<code>angular.constants</code>もなくなっているので、<code>const</code>でオブジェクトを作っておき、それを<code>import</code>して使うように置き換えましょう。</p>

<h3>ServiceのAngular2移行</h3>

<p>さて次に、ServiceをAngular2化していきます。
基本はLoadingのときと同じですが、通信周りには変更があります。</p>

<p>Ajax通信を行う際は<code>$http</code>を使って通信を行っていました。Angular2では<code>Http</code>でAjax通信を行うことができます。<code>$http</code>は<code>Promise</code>を返していましたが、<code>Http</code>は<code>Observable</code>を返すという違いがあります。</p>

<p>以下はAngular2記法に置き換えたいわゆるモデルです。作りは先程のローディングの場合と殆ど変わりませんが、<code>Http</code>は<code>Observable</code>を返すので、処理の変更箇所を減らすために<code>Promise</code>で返すようにしています。</p>

<p></p><pre class="crayon-plain-tag">import {Adapter} from '../../core/adapter';
import {Injectable} from 'angular2/core';
import {CONSTANTS} from '../../core/constants';
import {Http, Response} from 'angular2/http';
@Injectable()
export class AccountModel {
    constructor(protected http:Http) {}
    get() {
        return this.http.get(`${CONSTANTS.API_PATH}account`).toPromise();
    }    
}
angular.module('app').factory('AccountModel', Adapter.downgradeNg2Provider(AccountModel));
Adapter.addProvider(AccountModel);</pre><p></p>

<p><code>Http</code>の返す<code>Observable</code>については<a href="https://github.com/ReactiveX/rxjs" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">RxJSのドキュメント</a>を参照してください。</p>

<p><a href="https://github.com/Reactive-Extensions/RxJS" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Reactive-Extensions/RxJS</a></p>

<p>最後にあらかじめコンポーネントにしておいたページでフォームの部分を移植してみます。
まずテンプレートをAngular2記法に置き換えます。</p>

<p>テンプレートの記法は以下の参考リンクを上から順番に見ていくと良いでしょう。</p>

<ul>
<li>*ngIfのようなシンタックスについて &#8211; <a href="https://angular.io/docs/ts/latest/guide/template-syntax.html" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">TemplateSyntax</a></li>
<li>Angular1ではこう書いてたけど2だとどうなるの？というとき &#8211; <a href="https://angular.io/docs/ts/latest/cookbook/a1-a2-quick-reference.html" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">ANGULAR 1 TO 2 QUICK REFERENCE</a></li>
<li>慣れてきて一覧でみたい時 &#8211; <a href="https://angular.io/docs/ts/latest/guide/cheatsheet.html" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">ANGULAR CHEAT SHEET</a></li>
</ul>

<h3>formの移植</h3>

<p>続いてformの制御部分を移植します。メールアドレスの入力フォームで、バリデーションが通らないと更新ボタンが押せない（disabled）になるというよくあるものです。</p>

<p></p><pre class="crayon-plain-tag">&lt;form ng-submit="$ctrl.save()"&gt;
    &lt;label class="layout-block"&gt;メールアドレス変更&lt;/label&gt;     &lt;div class="form-item"&gt;
        &lt;div class="form-input form-fill"&gt;
            &lt;input ng-model="$ctrl.newEmail" type=“email" name="email" placeholder="新しいメールアドレス" required&gt;
            &lt;span class="form-error"&gt;
                &lt;span ng-if="!form.email.$error.email"&gt;メールアドレスを正しく入力してください&lt;/span&gt;
                &lt;span ng-if="!form.email.$error.required"&gt;必ず入力してください&lt;/span&gt;
            &lt;/span&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;button ng-disabled="!emailform.$valid" class="btn btn-default btn-revert"&gt;メールアドレスを変更する&lt;/button&gt;
&lt;/form&gt;</pre><p></p>

<p>formの制御のために<code>[ngFormModel]="name"</code>を使います。（これ以外にも複数方法があります）また、inputタグには<code>ngControl="name"</code>を使って名前を付けます。</p>

<p>下記のように書きかえます。</p>

<p></p><pre class="crayon-plain-tag">&lt;form [ngFormModel]="form" (submit)="save();"&gt;
	&lt;div class="form-input"&gt;
		&lt;input [(ngModel)]="user['lastname-phonetic']" ngControl="lastphonetic" type="text" value="" placeholder="セイ"&gt;
		&lt;span class="form-error" *ngIf="form.controls.lastphonetic.errors"&gt;
			&lt;span class="form-error-message"&gt;
				&lt;div [hidden]="!form.controls.lastphonetic.errors.required"&gt;セイは必須項目です&lt;/div&gt;
				&lt;div [hidden]="form.controls.lastphonetic.errors.required &amp;&amp; form.controls.lastphonetic.errors.invalidPhonetic"&gt;全角カタカナで入力してください&lt;/div&gt;
			&lt;/span&gt;
		&lt;/span&gt;
	&lt;/div&gt;
	&lt;button [disabled]="!form.valid" class="btn btn-default btn-revert"&gt;更新&lt;/button&gt;
&lt;/form&gt;</pre><p></p>

<p>フォームのエラー状況によって振り分けしたい部分は[ngFormModel]でつけた名前.controls.inputにつけたngCotrol名.errors.エラー名でエラーが発生しているかどうかを確認することができるようになりますが、スクリプトも修正が必要です。</p>

<p></p><pre class="crayon-plain-tag">import {FORM_DIRECTIVES,Control,ControlGroup,Validators} from 'angular2/common';

@Component({
    selector: 'profile-form',
    templateUrl: 'webroot/components/profile/profile.html',
    directives: [FORM_DIRECTIVES]
})

this.form = new ControlGroup({
	lastphonetic: new Control('',Validators.compose([
		Validators.required,
		ValidateService.phonetic
	]))
});</pre><p></p>

<p>コンポーネント内でform関連のディレクティブを使うときには@Componentのdirectivesに配列で使うディレクティブを指定します。</p>

<p>先ほどngModelFormにつけた名前と同じ名称にnew ControlGroup()します。
そしてngControlにつけた名前と同じ名称でnew Control()します。
第一引数がデフォルトの値、第二引数にバリデーション関数を指定します。
複数指定する場合はValidators.componse([])を１つの場合はValidators.requiredなどを指定します。</p>

<p>Validatorsにはあらかじめ定義されているバリデーターがあります。</p>

<p></p><pre class="crayon-plain-tag">static required(control: modelModule.Control): {
	[key: string]: boolean;
};
static minLength(minLength: number): Function;
static maxLength(maxLength: number): Function;
static pattern(pattern: string): Function;
static nullValidator(c: any): {
	[key: string]: boolean;
};</pre><p></p>

<p>Angular1ではtype=&#8221;email&#8221;でメールアドレスのチェックが行われていましたが、Angular2では上記のチェック以外は行われない点は注意してください。</p>

<p>今回はメールアドレスなので、カスタムバリデータを作成します。</p>

<p></p><pre class="crayon-plain-tag">export const ValidateService = {
	email(control:Control) {
		if (!/^[a-z0-9!#$%&amp;'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i.test(control.value)) {
			return {email: true};
		}
	}
}</pre><p></p>

<p>returnする名称はエラーが発生した時に form.controls.lastphonetic.errors.emailで取得することができる値です。</p>

<p>バリデーションの正規表現についてはAngular1で使用されていたものと同じです。</p>

<p><a href="https://github.com/angular/angular.js/blob/g3_v1_5/src/ng/directive/input.js#L27" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Angular directive/input.js</a></p>

<p>セッションでは時間の関係で移行手順の途中までしかお話できませんでした。
移行作業は作られたアプリケーションによって異なりますが、公式サイトの手順だけではハマりどころも発生することでしょう。</p>

<p>今後移行手順に関してはさらに整理され情報も出てくると思いますので、事前準備をしっかりと行い、ng-upgradeを用いて少しずつ移行を進めていきましょう。</p>
]]></content:encoded>
			</item>
	</channel>
</rss>
