目次


IBM Cloud Node.js アプリケーション入門, 第 2 回

Node.js を使用して受付用訪問者記録アプリケーションを作成する

クライアント・サイドのコードを開発する

Comments

コンテンツシリーズ

このコンテンツは全3シリーズのパート#です: IBM Cloud Node.js アプリケーション入門, 第 2 回

このシリーズの続きに乞うご期待。

このコンテンツはシリーズの一部分です:IBM Cloud Node.js アプリケーション入門, 第 2 回

このシリーズの続きに乞うご期待。

このシリーズの第 1 回では、IBM Cloud 上で Node.js アプリケーションを作成する方法を説明しました。けれども作成したアプリケーションは、応答性に優れているというわけでも視覚的に魅力的というわけでもありません。今回の記事で、Bootstrap のテーマを使用して見栄えの良いアプリケーションに変身させる方法、そして AngularJS ライブラリーを使用して応答性を向上させる方法を説明します。

アプリケーションを作成するために必要なもの

  • 無料の IBM Cloud アカウント
  • HTML の基礎知識
  • JavaScript の基礎知識
  • この全 2 回からなるシリーズの第 1 回のアプリケーションと情報

アプリを実行するコードを入手する

アプリケーションを更新する

新しいユーザー・インターフェースを備えたアプリケーションを実行するには、第 1 回の記事で作成した開発環境に戻り、以下の手順に従います。

  • app.js を開き、サーバーの public ディレクトリーから静的ファイルを呼び出す以下の行をコメントアウトします。
    app.use(express.static(__dirname + '/public'));
  • public/index.html と /public に含まれるすべてのディレクトリーを削除します。オンライン開発環境内のファイルを削除するには、対象のファイルを右クリックして「Delete (削除)」を選択します。
  • public/ui.html という名前の新しいファイルを作成し、そのファイルに、このリンク先のコードを含めます。
  • アプリケーションを再起動します。
  • <該当する URL>/ui.html を参照して、新しいユーザー・インターフェースを表示します。または、このリンク先のページで私のコピーを見ることもできます。

仕組み

このアプリケーションで使用するプロセスを、以下の図 1 に示します。ユーザーがアプリケーションを参照すると、IBM Cloud 上のアプリケーションが ui.html を使用して応答します。この HTML ファイルには、大量の JavaScript コードが含まれています。

ui.html 内のクライアント・サイドのコードで app.js 内のサーバー・サイドのコードと通信する方法は 3 つあります。

  1. サーバーに visitors データ構造の値をリクエストします。サーバーがレスポンスでその情報を返すと、クライアントが処理してユーザーに表示します。この動作は、起動時、変更をリクエストした後、そして (別のユーザーが情報を変更した場合に備え) 数分間隔で行われます。
  2. 訪問者をログインさせるよう求めます。
  3. 訪問者をログアウトさせるよう求めます。
図 1. アプリケーションのプロセス
アプリケーションのプロセス
アプリケーションのプロセス

クライアント・サイドのアプリケーションでは訪問者を表示するために、リアクティブ・プログラミングを使用しています。

行ごとの説明

コードの内容について詳しく説明します。このリンク先のソース・コードを見ながら読み進めることができます。

HTML でのコメントの構文は、<!-- コメント --> です。

例:

<!-- jQuery -->

AngularJS を扱うには、jQuery ライブラリーに関する知識が前提条件となります。このライブラリーには有用な関数がいくつも含まれています。その 1 つは <script src...> です。

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>

<script src...> 構文は、URL から JavaScript をインポートするために使用します。上記の例では、jQuery の最小化 (読みやすさのためではなく、帯域幅のために最適化された) バージョンがインポートされます。読んで理解しやすいバージョンはこのリンク先のページで確認できますが、必ずしも jQuery を読んで理解しなければ、AngularJS を使用できないというわけではありません。jQuery をインポートするだけで、その機能を AngularJS で使用できるようになります。

アプリケーションのルック・アンド・フィールは、Bootstrap のテーマによって定義されています。Bootstrap の使用方法を学ぶには、このリンク先のページでサンプルを表示し、ソース・コードを読んで、さまざまなアイテムを作成する方法を確認できます。

<!-- Bootstrap -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>

AngularJS は、リアクティブ・プログラミングで使用するライブラリーです。

<!--  Angular  -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.js"></script>

Angular には他にもモジュールがあります。そのうちの 1 つは、潜在的に危険なソースからの HTML を表示する場合に使用する、sanitize というモジュールです。後で、このモジュールが必要になります。

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-sanitize.js"></script>

    
<script>

以下のコマンドを使用して、Angular アプリケーションを作成します。

var myApp = angular.module("myApp", ['ngSanitize']);

Angular で最も重要となる概念は、スコープです。scope 変数にはコントローラーの関数のほとんどに加え、ユーザーに表示される変数が格納されます。コントローラー外部からスコープにアクセスする必要はありませんが、デバッグには var scope; が役立つと思います。

以下の呼び出しでは、コントローラーを作成し、そのコントローラーのスコープである $scope パラメーターを使用して、指定された関数を実行します。

myApp.controller("myCtrl", function($scope) {
	// Make the scope available outside the controller, 
	// which is very useful for debugging
  	scope = $scope;

以下の変数は、サーバー・サイドの visitors と同じになります。とりあえず、ダミーの値を入れておきます。

$scope.visitors = {a: "b"};

訪問者がログインするときに、その値を選択するのではなく、入力するとします。以下の変数は、ユーザーが訪問者の名前を入力できるテキスト・フィールドのモデルです。つまり、テキスト・フィールドと変数は、常に同じ値を持つことになります。いずれか一方が変更されると、もう一方も AngularJS によって変更されます。この変数が、テキスト・フィールドのモデルです (後で説明します)。

$scope.loginUser = "";

以下の関数は、サーバー・サイドから visitors を読み取り、それをクライアント内で更新します。

$scope.refreshVisitors = () => {

変数 $ は、jQuery 関数を格納するデータ構造です。これらの変数のうち、$.ajax ではデータを非同期に読み取ることができます。ここでは、この変数を呼び出して visitors データ構造を読み取ります。app.js ではコンテンツの型を指定していないため、ここでデータ型を指定します。

  		$.ajax({
  			url: "/test/visitors",
  			async: true,
  			dataType: "json",

次に続くのは、コールバック関数です。データ型は JSON であるため、このコールバック関数は解析された JavaScript オブジェクトとパラメーターとして受け取ります。その新しい値に scope 変数を設定してから、$scope.$apply を呼び出します。AngularJS のようなリアクティブ・プログラミング・フレームワークでは、変数が変更された場合は常に、その変数に基づく他のどのアイテムに変更が必要かを判断する必要があります。通常、AngularJS ではそうしたアイテムを自動的に判断できますが、コールバック変数から非同期で変更が送られてくる場合、それを判断するのは不可能です。そのため、手作業で $scope.$apply を呼び出す必要があります。

  			success: (obj) => {
  				$scope.visitors = obj;
  				$scope.$apply();
  			}
  		});
  	};

この時点 (コントローラーが作成された時点) で visitors を更新するだけでなく、setInterval を使用して 1 分間隔で更新します。

  	$scope.refreshVisitors();
  	
  	// Refresh visitors every minute (in case somebody 
// else changes it)
  	setInterval($scope.refreshVisitors, 60*1000);

ここでは当然、以下の関数を呼び出すのが理に適っています。この関数は、現在の時刻として、エポックからの経過時間をミリ秒数で返すためです。この関数がなぜ必要になるのかは、後でこれを使用するときに説明します。

	// Date.now() doesn't function correctly inside {{}}, so
	// use this
	$scope.now = () => Date.now().valueOf();

以下の関数は、訪問者をログアウトします。そのために呼び出している URL は、このシリーズの第 1 回の記事で Web ページから呼び出した URL と同じです。訪問者をログインする関数もほとんど同じなので (URL が異なるだけです)、ここには記載しません。

	// Log a visitor out (ask the server to do it, then refresh 
    // the visitors data structure)
	$scope.logout = name => {
		$.ajax({

訪問者の名前には、HTML クエリー文字列では無効な文字が含まれている可能性があります。encodeURI 関数は、そのような文字をエスケープしてクエリーを有効なものにし、app.js のコードで正しい訪問者名を見つけられるようにします。

			url: `logout?user=${encodeURI(name)}`,
  			async: true,
  			success: () => $scope.refreshVisitors()
  		});		
	};

前回の記事で app.js 内で開発した関数の多くも役立ちます。これが、Web アプリケーションに Node.js を使用する利点の 1 つです。Node.js ではサーバーとクライアントがどちらも JavaScript で作成されるため、多くの場合、サーバーとクライアントの両方で同じコードを再利用できます。

// Functions from app.js
	
	$scope.visitorNames = … 
	$scope.currentVisitorNames = … 
	$scope.nonCurrentVisitorNames = … 
	$scope.tdiffToString = …

以下の関数は、app.js バージョンとは少々異なります。app.js バージョンでは、時刻と日付の文字列表現を取得するには Date(値) を使用するだけで十分です。一方、この関数では Date オブジェクトを再利用しており、AngularJS による評価の取りまとめ方法の影響で、ここで同じ関数を使用すると、表示される日付がすべて同じになってしまいます。そうならないようにするために、毎回新しい Date オブジェクトを作成します。

$scope.histEntryToRow = (entry) => {
	return `<tr>
			<td>${new Date(entry.arrived)}</td>
			<td>${new Date(entry.left)}</td>
			<td>${$scope.tdiffToString(entry.left –
entry.arrived)}</td>
			</tr>`;
		
	};

以下の関数も異なります。なぜなら、クライアント・サイドのコードでは Bootstrap のテーマを使用するためです。テーブルの外観を直接指定するのではなく、この関数では Bootstrap のクラスを使用します。

	// Given a history, create a table with it
	$scope.histToTable = (history) => {
… 			
		return `<details>
			<table class="table table-condensed table-border">
				<tr>

…
`;
	};



});


</script>	
	
</head>

body タグには、2 つの AngularJS 属性を含めます。1 つはアプリケーション用の属性、もう 1 つはコントローラー用の属性です。複雑な Angular アプリケーションでは、複数のコントローラーを使用することもあります。

<body ng-app="myApp" ng-controller="myCtrl">

HTML の div 要素は、ページ上の長方形コンテナーです。Bootstrap のパネルは、panel クラスを設定した div として実装されます。2 番目のクラス panel-primary は色を指定します。Bootstrap では、背景色を使用して情報を伝えます (このことは、パネルの場合だけでなく、テーマのすべての要素に適用されます)。

サンプル・データ・テーブル
明るい灰色デフォルト
青色基本
緑色成功
水色情報
橙色警告
赤色危険

通常、パネルの属性には、見出しとパネル本体の 2 つがあります。

<div class="panel panel-primary">
		<div class="panel-heading">
        	<h3 class="panel-title">Current visitors</h3>
        </div>
        <div class="panel-body">
        	<table class="table">
        		<tr>
        			<th>Name</th>
        			<th></th>
        			<th>Time here</th>
        			<th>History</th>

ここで関与してくるのが、AngularJS です。ng-repeat 属性は、リスト内に含まれるすべての値に対してタグ (この例ではテーブルの表) を繰り返すよう指定します。このリストは現在の訪問者のリストであり、現在の値は name 変数に格納されます。

<tr 
ng-repeat="name in currentVisitorNames()"
>
        			<td>

AngularJS は構文 {{<式>}}を検出すると、その式を評価して、二重波括弧を結果で置き換えます。この例の場合、訪問者の名前がテーブルのセル内に表示されることを意味します。

    					{{name}}
    				</td>
    				<td>

通常の on-click 属性は AngularJS の評価には組み込まれません。例えば、AngularJS のスコープ関数 logout で ng-repeat インデックス名を使用するには、ng-click 属性を使用します。

    					<button class="btn btn-danger" 
ng-click="logout(name)">
    						Logout
    					</button>
    				</td>
    				<td>

二重波括弧 ({{…}}) 内で直接 Date.now() を実行しようとすると、失敗します。それは、この関数は新しいオブジェクトを作成する必要があるためです。一方、この関数を関数内で実行すると、AngularJS は関数を実行できるときに実行し、通常どおりに動作します。

	    				The last {{
tdiffToString(now()-visitors[name].arrived)}}    					
    				</td>
    				<td>

以下のコードには、いくつかの新しい要素があることがわかります。まずその 1 つは、details タグです。このタグを使用することで、ユーザーがクリックしたときにだけ表示される情報を生成できます。この機能は、履歴を扱うのにうってつけです。履歴を確認できるようにする必要はありますが、通常は履歴を表示する必要はありません。このタグに含まれる ng-if 属性は、AngularJS にこのタグを表示するかどうかを決定するよう指示します。条件が false に評価されると、ユーザーに不在履歴テーブルは表示されません。最後に、このテーブルは HTML であることから、これを単に括弧で囲むことはできません。そうすると、HTML ではなくテキストとして解釈されてしまいます。そこで使用しているのが、ng-bind-html 属性です。

: この属性を扱うために、前述の ngSanitize が必要になっています。sanitize モジュールは、信頼できないソースから取得された可能性のあるリンクやその他の危険な可能性のあるコンテンツを HTML から削除します。AngularJS に特定のリンクを解釈させる必要がある場合は、該当するケースを例外にするよう AngularJS に指示する方法が存在しています。

    					<details 
ng-if="visitors[name].history && visitors[name].history.length > 0" 
ng-bind-html="histToTable(visitors[name].history)">
    					</details>    				
    				</td>
    			</tr>
        	</table>
        </div>
    </div>   <!-- current visitors -->

現在いない訪問者を表示するパネルでも、同じ手法と定義を使用するため、説明を繰り返す必要はないでしょう。

一番下にあるログイン用のパネルは、他のパネルとは異なります。ユーザーが新しい訪問者の名前を指定できるよう、テキスト入力が必要になるからです。

    <div class="panel panel-success">
    	<div class="panel-heading">
    		<h3 class="panel-title">Log In a Visitor</h3>
    	</div>
    	<div class="panel-body">

ng-model 属性は、入力属性の値を scope 変数に関連付けます。この関連付けは双方向の関係です。つまり、ユーザーがテキスト・フィールドに入力すると loginUser の値が変更され、loginUser の値が変更されると、テキスト・フィールド (およびその下にあるボタン) に新しい値が表示されます。

            <input type="text" ng-model="loginUser">
            <br />
            <button class="btn btn-success" ng-click="login(loginUser)">
                    Log on {{loginUser}}
                   </button>
            </div>
            </div>
         </body>
         </html>

まとめ

このシリーズの両方の記事を読み終わった今、ユーザー入力に応答してそのデータを Cloudant データベース内に格納する単純な Web アプリケーションを作成するための知識が身に着いたはずです。大いに有用とは思えないかもしれませんが、IBM Cloud 内に用意されているあらゆるサービスと組み合わせることで、幅広い目標を達成する極めて高度なアプリケーションを作成できます。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Cloud computing, セキュリティ
ArticleID=1012675
ArticleTitle=IBM Cloud Node.js アプリケーション入門, 第 2 回: Node.js を使用して受付用訪問者記録アプリケーションを作成する
publish-date=10112018