次は、クライアント側を作っていきます。前回解説した通り、画面は下記のようなレイアウトです。
「体感温度を表示する部分」と「エアコン操作の部分」を独立したUIコンポーネントにしています。それでは、順を追って説明していきます。
まずは、ブラウザーに最初に読み込まれるindex.htmlから見ていきましょう。「grunt build」コマンドを実行すると、依存スクリプトなどが自動的にindex.htmlに挿入されます。事前に「bower install」コマンドの実行をお忘れなく。
自分で変更する必要がある箇所は「ui-view」で展開するUIテンプレートです。今回は体感温度を表示する「heatIndex」とエアコンを操作するための「airConditiner」を指定します。
31 <body ng-app="hemsjsApp"> .. ... 36 <!-- Add your site or application content here --> 37 <div ui-view="heatIndex"></div> 38 <div ui-view="airConditioner"></div>
この「ui-view」に対応する「html」と「controller」の指定をclient/app/main.jsで行っています。ui-routerの機能を使います。
1 'use strict'; 2 3 angular.module('hemsjsApp') 4 .config(function ($stateProvider) { 5 $stateProvider 6 .state('main', { 7 url: '/', 8 controller: 'MainCtrl', 9 views: { 10 heatIndex: { 11 templateUrl: 'app/heatIndex/heatIndex.html', 12 controller: 'HeatIndexCtrl' 13 }, 14 airConditioner: { 15 templateUrl: 'app/airConditioner/airConditioner.html', 16 controller: 'AirConditionerCtrl' 17 } 18 } 19 }); 20 });
では、ここで指定したコードがどうなっているのか見てみましょう。
まずは、データをViewに提供するコントローラーのコードです。
1 'use strict'; 2 3 angular.module('hemsjsApp').controller('HeatIndexCtrl', function ($scope, $http, socket) { 4 $scope.thermoHygro = {}; 5 6 socket.socket.on('ThermoHygroHistory:save', function (thermoHygro) { 7 $scope.thermoHygro = thermoHygro; 8 }); 9 10 $http.get('/api/thermoHygroHistories/last').success(function (thermoHygro) { 11 $scope.thermoHygro = thermoHygro; 12 }); 13 });
AngularJSのコントローラーでは、Viewに提供するデータと処理を記述します。$scopeのプロパティは値の変更が監視されており、その変更内容は自動的にDOMに反映されます。
かつてのJavaScriptコードは、対象となるエレメントを取得しテキストノードを変更するコードが多くなる傾向にありました。AngularJSを使うと、そういったコードを書かなくてよいため、非常にシンプルです。
6〜8行目では、新しく温度湿度データを取得した際に発生するイベントを拾っています。サーバーからのpushなので、Socket.IOを利用しています。受け取ったデータを$scope.thermoHygroに代入するだけでViewの表示が更新されます。
Socket.IOによるpushが行われるのは、サーバー側が新しく温度湿度データを取得したときだけでした。次の取得まで値が表示されないのは不便なので、10〜12行目で最後に取得したデータをリクエストし、レスポンスを$scope.thermoHygroに代入しています。
次にViewを見てみましょう。
1 <div class="wrapper border"> 2 <div class="label"><i class="fa fa-home"></i> 体感温度</div> 3 <div class="content"> 4 <div> 5 <span id="heatIndex" class="number">{{ thermoHygro.heatIndex | number:1 }}</span> 6 <span id="heatIndexUnit" class="unit gray">℃</span> 7 </div> 8 <span id="temperature" class="number gray">{{ thermoHygro.temperature | number:0 }}</span><span class="unit">℃</span> 9 <span id="humidity" class="number gray">{{ thermoHygro.humidity | number:0 }}</span><span class="unit">%</span> 10 <div id="measuredAt" class="gray">測定: {{ thermoHygro.createdAt | date:'HH:mm:ss'}}</div> 11 </div> 12 </div>
先ほどコントローラーで定義した$scope.thermoHygroは、ViewではthermoHygroという変数名でアクセスできます。それぞれの値に対してAngularJSのフィルターを使って有効数字やフォーマットを指定し、テキストノードにバインドしています。
こちら側は、ボタンをクリックしたらSocket.IOでサーバーにイベントを伝達するのみです。
ボタンをクリック/タップした際のハンドラーをコントローラーで記述しています。
1 'use strict'; 2 3 angular.module('hemsjsApp').controller('AirConditionerCtrl', function ($scope, $log, socket) { 4 $scope.togglePower = function () { 5 socket.socket.emit('AC:toggle'); 6 }; 7 8 socket.socket.on('AC:toggled', function (data) { 9 $log.log('エアコンの電源を切り替えました'); 10 }); 11 });
ScoketIOでサーバーに伝達する処理は4〜6行目の$scope.togglePowerメソッドに記述しています。単にSocket.IOでemitしているだけです。ボタンをクリックしたときにこのメソッドがcallされる想定です。
エアコンの電源切り替えが終了したとき、Socket.IOで「AC:toggled」イベントが送られてきます。そのハンドラーが8〜10行目になりますが、現在はログ出力しているだけです。将来、画面にポップアップしてフィードバックする際に利用できることでしょう。
Viewに進みましょう。
1 <div class="wrapper"> 2 <div class="label"><i class="fa fa-cog"></i> エアコン管理</div> 3 <button id="btn-ac-control" ng-click="togglePower()"> 4 <div><i class="fa fa-power-off"></i></div> 5 <div class="description">ON / OFF</div> 6 </button> 7 </div>
最も重要な箇所が3行目の「ng-click="togglePower()"」です。ボタンエレメントをクリックしたときに、コントローラーで定義した$scope.togglePowerをcallします。
これでUI側の作り込みも完了です。
「grunt serve:dist」コマンドでサーバーを起動します。minifyなどが行われた後、ブラウザーが自動的に開くでしょう。
ここにスマートフォンでアクセスすると、同じように体感温度が表示され、ボタンをタップするとエアコンの電源が切り替わります。しばらく待つと、WebSocketで最新の体感温度に更新されるでしょう。
ユーザー認証の機能もひな型ができていますので、グローバルに公開する際はその機能を組み込むようにしましょう。
以上、いかがでしたでしょうか。angular-fullstackジェネレーターを利用することで、拡張性の高いコード構成で、双方向通信も可能なInternet of Thingsの世界を実現させました。
この記事が種となって、皆さんが面白いシステムを作るきっかけとなれば幸いです。
岩永 義弘(いわなが よしひろ)
株式会社インターネットイニシアティブ
データ分析とワインが好物。通信アノマリ検知システムや地震速報配信システムの開発と運用を経た後、Webアプリのフロントエンドに手を伸ばし、図らずもフルスタックな道を突き進んでいる。最近は趣味でHTTP/2の実装を楽しんでいる。
Twitter:@y_iwanaga_
Bot:@quake_alert @WeatherAlertJP
Copyright © ITmedia, Inc. All Rights Reserved.