AngularJSモジュールとコントローラ
モジュールとコントローラの定義
前回はAngularJSを使ったが、以前の例にはJavaScriptスクリプトがなかった。やはり本格的な処理を実装しようとすると、「スクリプトをどのように組み合わせるか」が重要になる。
モジュール
AngularJSでは「モジュール」というものを使ってスクリプトを結合する。モジュールとは、AngularJSで使われるプログラムのまとまりのようなものである。AngularJSではプログラムをモジュール単位で結合する。モジュールの中には、いくつかの小さなプログラムをまとめておくことができる。
AngularJSではng-appという属性を指定することで、そのタグにAngularJSの機能を割り当てることができた。このng-appを使うと、モジュール名を指定できる。たとえば、
<body ng-app="hoge">
このようにすると、この<body>タグでhogeモジュールの機能を使えるようになる。
コントローラ
モジュールはプログラムをまとめたものだが、それ自体に直接何らかの処理が含まれるわけではない。通常、このモジュールに「コントローラ」というものを作る。
コントローラは、さまざまな処理や値をまとめたオブジェクトである。このコントローラにメソッドやプロパティを作っていくことが、AngularJSのプログラムを書くことだといえる。モジュールには必要なだけコントローラをいくつでも保持できる。
コントローラの利用もモジュールと同じように、タグにコントローラ指定を書く。これはng-controllerという属性を使う。
<○○ ng-controller="コントローラ名">
これにより、この<○○>タグの内部で指定したコントローラを使える。モジュールには複数のコントローラを置けるため、必要に応じて「この部分ではAコントローラ」のように使用範囲を変えながら使える。もちろん複数のコントローラを同時に使うこともできる。
モジュールとコントローラ。この2つが何かを理解することが、AngularJS開発の第一歩である。
モジュールとコントローラの作成と使用
モジュールとコントローラについて、簡単な例を作って使い方を説明する。
まずHTMLファイルを作ってみよう。簡単な例は次のとおりである。
<!DOCTYPE html>
<html>
<head>
<title>AngularJS Sample</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="script.js"></script>
<style>
body { color:gray; }
h1 { font-size:18pt; font-weight:bold; }
span.label { display:inline-block;width:50px; color:red; }
input { width:100px; }
.msg { font-size:14pt; font-weight:bold;color:gray; }
</style>
</head>
<body ng-app="myapp" ng-init="num = 1000">
<h1>税金計算</h1>
<p>金額を入力してください。</p>
<div ng-controller="HeloController as ctl">
<div class="input">
<span class="label">tax:</span>
<input type="text" ng-model="ctl.tax">%
</div>
<div class="input">
<span class="label">price:</span>
<input type="text" ng-model="num">円
</div>
<p class="msg">税込: {{ctl.calcWithTax(num)}}円</p>
<p class="msg">税抜: {{ctl.calcWithoutTax(num)}}円</p>
</div>
</body>
</html>
ここでは<script src="script.js"></script>という形でスクリプトファイルをロードしている。このscript.jsというファイルがAngularJSの本体プログラムである。
一般的にJavaScriptプログラムは、HTMLファイルに直接埋め込んで書くか、別のスクリプトファイルを作ってそれをロードして取り込む。AngularJSでもどちらの方法でもプログラムを作成できる。
しかしMVCアーキテクチャを重視し、画面表示と処理の分離を考えるなら、HTMLの中にそのままJavaScriptコードを書くのはあまりよい方法ではないだろう。ここように別ファイルに分け、そこにコントローラを書くのが自然である。
コントローラの使用
ここではまず次のようにして、myappというモジュールを利用している。
<body ng-app="myapp" ng-init="num = 1000">
これにより、myappモジュールにあるコントローラを使える。ここでは次のようにコントローラを利用している。
<div ng-controller="HeloController as ctl">
myappモジュールにはHeloControllerというコントローラが用意されている。ただ、この名前は少し長い。asはコントローラに別名を付けることができる。ここでHeloController as ctlと指定すると、ctlという名前でHeloControllerを使えるようになる。
<input type="text" ng-model="ctl.tax">%
<input type="text" ng-model="num">円
入力フィールドの<input>タグには、ng-modelでモデルを指定する。numは<body>タグにng-init="num = 1000"という形で初期化されている値である。
ctl.taxはコントローラにある値である。ctl、つまりHeloControllerの中にあるtaxというプロパティをモデルとして設定している。
<p class="msg">税金: {{ctl.calcWithTax(num)}}円</p>
<p class="msg">税抜: {{ctl.calcWithoutTax(num)}}円</p>
以後、このように書かれている。これはそれぞれHeloControllerにあるcalcWithTax、calcWithoutTaxというメソッドの値を書いたものである。コントローラにある処理、つまりメソッドはこのように呼び出せる。
スクリプトの作成
今回は例で使用するスクリプトを作成する。以下に例のコードを示す。script.jsというファイル名でHTMLファイルと同じ場所に配置する。
angular.module('myapp',[])
.controller('HeloController',
function(){
this.tax = 8;
this.calcWithTax = function(val){
return Math.floor(val * (100 + this.tax * 1) / 100);
};
this.calcWithoutTax = function(val){
return Math.floor(val / (100 + this.tax * 1) * 100);
};
}
);
今回の例は、前に作った消費税計算を修正したものである。taxとpriceという2つの入力フィールドがあり、それぞれに税率(%)と金額を入力すると、リアルタイムで税込価格と税抜価格を計算して表示する処理が含まれている。
モジュールの作成
angular.module(名前, 配列);
モジュールの作成は、angularというオブジェクトのmoduleメソッドを使って実行する。angularというオブジェクトはAngularJSの基本となるオブジェクトである。AngularJSの機能はすべてこのangularオブジェクトにまとめられている。
moduleメソッドは、第1引数にモジュール名のテキストを指定する。第2引数には、このモジュールが参照する他のモジュールを配列にまとめて指定する。特に参照するものがなければ空配列にする。
また、ここでは特に使用しなかったが、moduleメソッドは生成されたモジュールオブジェクトを戻り値として返す。
コントローラの作成
モジュール.controller(名前, 関数);
コントローラは、moduleで作ったモジュールにあるcontrollerメソッドで作成する。第1引数にはコントローラ名を指定する。
問題は第2引数である。ここにコントローラに結び付く実装部分が含まれる。「関数」となっているが、これはコントローラのコンストラクタ関数になる。
コンストラクタ関数とは、JavaScriptでオブジェクトを生成するために使われるものである。ここでプロパティとメソッドを定義すればよい。
モジュールの形
今回は先ほど作成した例のコードを見てみよう。このサンプルは次のような形になっている。
angular.module('myapp',[])
.controller('HeloController',
function() {
...中略...
}
}
);
angular.moduleでmyappモジュールを作り、controllerを呼び出してHeloControllerを定義している。コンストラクタ関数内には、taxプロパティと、calcWithTax、calcWithoutTaxの2つのメソッドを用意している。
このように「モジュール作成」「コントローラ作成」「コンストラクタ関数で必要な値と処理を実装する」ことが、モジュールとコントローラ利用の基本である。
コンストラクタ関数
コントローラに用意されているコンストラクタ関数をどう作るかが、AngularJS利用の核心である。
コンストラクタ関数はJavaScriptのオブジェクトを生成する関数である。これはオブジェクトに提供するプロパティとメソッドを内部で定義する必要がある。
function(){
this.○○ = ☓☓; // プロパティ定義
this.△△ = function(){...} // メソッド定義
}
コンストラクタは、プロパティとメソッド、分かりやすくいえば値を保存する変数と処理を実装する関数を、その内部で定義する。これはthis.○○のように、this内のプロパティに代入する形で書く。
thisはコンストラクタ関数によって生成されるオブジェクト自身である。このようにしてオブジェクト自身のプロパティに値を割り当て、オブジェクトの内容を作っていく。コンストラクタ関数はAngularJSに限らず、JavaScript全般で使われる技術である。
簡単なデータベースの作成
今回はデータを扱う例を作ってみよう。コントローラに配列でデータを保持しておき、そこからデータを取得して表示する。要するに、簡単なデータベースを使う場合の例である。
HTML画面表示
まずHTMLファイルを作る。例のコードを以下に示す。次のように作成して保存する。
<!DOCTYPE html>
<html>
<head>
<title>AngularJS Sample</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="script.js"></script>
<style>
body { color:gray; }
h1 { font-size:18pt; font-weight:bold; }
span.label { display:inline-block;width:50px; color:red; }
input { width:100px; }
.msg { font-size:14pt; font-weight:bold;color:gray; }
</style>
</head>
<body ng-app="myapp" ng-init="num=0">
<h1>データ表示</h1>
<p>ID番号を入力してください。</p>
<div ng-controller="HeloController as ctl">
<div class="input">
<span class="label">ID:</span>
<input type="text" ng-model="num">
<button ng-click="ctl.doAction(num)">click</button>
</div>
<p class="msg">{{ctl.getData()} }</p>
</div>
</body>
</html>
ここでは3か所にAngularJSの機能が使われている。簡単にまとめると次のようになる。
myappモジュール設定
<body ng-app="myapp" ng-init="num = 0">
まず<body>にmyappモジュールを設定する。そしてnumという変数を初期化する。これで<body>でmyappモジュールの機能を使えるようになった。
num設定
<input type="text" ng-model="num">
用意した変数numはここで使用している。入力フィールドの値を管理するモデルとしてnumを設定する。これにより入力された値が常にnumに保持される。
getDataデータ表示
<p class="msg">{{ctl.getData()}}</p>
これがデータを取り出して表示する部分である。HeloControllerのgetDataというメソッドを呼び出す。このメソッドは現在入力された番号のデータをテキストとして返す。これにより必要なデータがいつでもここに表示される。
クリック時の動作設定
<button ng-click="ctl.doAction(num)">click</button>
今回新しく登場した属性がng-clickである。これはonclick属性に相当し、クリックしたときに処理を実行するものである。<p class="msg">に設定されている{{ctl.getData()}}はリアルタイムで表示が更新されるが、ときには「クリックすると処理を実行する」方式も必要になる。
モジュールとコントローラの定義
スクリプトを作成する。例は次のとおりである。ファイル名はscript.jsとして保存する。
var myapp = angular.module('myapp',[]);
myapp.controller('HeloController',
function(){
this.count = 0;
this.data = [
[0,'nobody','nodata...'],
[1,'성진','sungjin@foryou'],
[2,'원석','wonsuck@flower'],
[3,'병호','byeongho@devkuma.com']
];
this.getData = function(){
return this.data[this.count][0] + ': ' +
this.data[this.count][1] + ', ' +
this.data[this.count][2] + '.';
};
this.doAction = function(num){
this.count = num;
}
}
);
作成したらアクセスしてみる。入力フィールドに1から3の番号を入力すると、該当番号のデータが表示される。データを保存しているdataプロパティの内容を追加すれば、さらに多くのデータを扱える。
今回はmoduleメソッドで作ったモジュールを変数に代入し、その変数からcontrollerメソッドを呼び出す形になっている。どのように書いてもかまわないが、モジュールに複数のコントローラを指定する必要がある場合は、このように変数にして書くほうが便利である。
ここではthis.dataに配列でデータを保存している。これは2次元配列で、ID番号、名前、メールアドレスなどの値が保存されている。getDataメソッドは、countプロパティに設定されたインデックス番号のデータを取り出し、テキストにして返す。
ng-clickで実行するには?
今回はgetDataのほかに、doActionというメソッドも作成した。このメソッドはng-clickで呼び出されると処理される。
ここで行っているのは、引数のnumをcountプロパティに設定するだけの単純な処理である。この処理でcount値が変更されると、テンプレートで{{ctl.getData()}}が指定されている表示がすぐに更新され、変更後のデータに変わる。
表示部分はAngularJSが自動的に更新するため、アクションメソッドは単に保存している値を書き換えるだけでよい。通常のJavaScriptのアクション処理とはかなり違う感覚であることが分かる。