回答ツリーを表示し回答に対するレスを受け付け可能にし、レスの数と総回答数をバインドする
Two-Way Data Binding
AngularJSの主なトピックの一つになっている双方向データバインディングについて書く。
Viewの使い方では、Modelデータの表示とng-repeatディレクティブを活用したModelデータの繰り返し処理を利用してリスト表示をやってみた。前回までの作業で掲示板スレッドの回答リストを表示する部分は以下のようになっている。
<div class="large-6 columns"> <div class="row"> <div class="small-2 columns"> <span class="radius label">{{thread.answers.length}}</span> </div> <div class="small-10 columns"> <ul class="inline-list" ng-repeat="e in thread.answers"> <li>{{e.body}}</li> <li><label><input type="checkbox" /> ツリーを表示</label></li> </ul> <div class="checkbox-inline"> <label><input type="checkbox"> すべての回答を表示</label> </div> </div> </div> </div>
この部分をTwo-Way Data Binding化して、
- レスポンス入力フォームの実装
- レスポンス数の表示
上の2つの機能を実装しながら、AngularJSの双方向データバインディングを把握する。
ng-showでチェックボックスを非表示化
Two-Way Data Binding関連の実装の前に、レスポンスの付いている回答のみに[ツリーを表示]のチェックボックスが表示されるようにする。AngularJSでは表示/非表示の制御に
ng-show
というディレクティブを利用できる。
<li ng-show="e.tree"><label><input type="checkbox" ng-model="e.on" /> ツリーを表示</label>
冒頭のHTMLを並行して確認することになるが、liにng-showを追加することで、ng-repeat中のe.treeの値がtrueの場合はリストを表示することができる。そして、
ng-model=”e.on”
というディレクティブも追加した。このng-modelを設定することで、チェックボックスのオンオフ操作で、e.onのtrue/falseを切り替えることが可能になる。
Modelの使い方で定義したデータ構造には、onというデータは定義していないのだが、ng-modelに直接設定することで、Modelデータをダイレクトに操作することができる。
e.onの値をtrue(チェックボックスをオンにしたタイミング)で回答へのレスポンスデータを表示することになる。そのため、
<li ng-show="e.tree"><label><input type="checkbox" ng-model="e.on" /> ツリーを表示</label> <ul class="inline-list" ng-show="e.on" ng-repeat="f in e.tree"> <li>{{f.response}}</li> </ul> </li>
ulを入れ子にして、そのulにng-showを設定しチェックボックスのオンオフ操作で表示/非表示を切り替えている。
上の画像の水色で囲った部分がチェックボックスをオンにすることで表示されたレスポンスデータで、下のfirebugのデバッグ表示の中で水色でラインを引いた部分(e.on)がチェックボックスの操作に応じてtrue/falseに切り替わることが確認できる。
カウンタとレスポンス入力用フォームの追加
レスポンス数を数えるカウンタと特定の回答に対するレスポンス入力フォームを作成する。それぞれliタグで以下のように追加した。
カウンタ
<li ng-if="e.on"> <span class="radius success label">{{responseCount($index)}}</span> </li>
ng-ifディレクティブを設定してチェックボックスがオンの場合のみliタグを表示するようにしている。
- ng-if
- ng-show
双方は表示を制御する点で似ているのだが、ng-ifは非表示にするだけではなく、処理も実行しないという点でng-showと異なるディレクティブとなっている。そのため、レスポンスが無い回答については、数を変える以下の処理が省略されることになる。
{{responseCount($index)}}
responseCount()を動かすためにはControllerで
$scope.responseCount()
を定義しておかないといけない。Controller内にresponseCountを以下のように設定した。
$scope.responseCount = function ($index) { var count = 0; angular.forEach($scope.thread.answers[$index].tree, function (e) { if (e.response) { count++ } }); return count; }
引数で渡している$indexはng-repeatのプロパティで、各要素のインデックス番号が設定されている。そのため、$indexを引数として設定することで、その要素に対しる操作をすることが可能になる。
レスポンス入力フォーム
基本的に回答入力フォームと同じ要領でfoundationベースのHTMLを組むことができる。
<li> <span class="row" ng-show="e.on"> <span class="small-12 columns"> <textarea id="right-label" placeholder="{{e.body}}のレスポンスを入力" ng-model="Response"></textarea> </span> <span class="small-12 columns"> <button class="right inline" ng-click="addResponse($index, Response)" type="submit">レスする</button> </span> </span> </li>
ng-modelディレクティブでResponseという$scopeの値を設定した上で、ng-clickで引数としてその値を利用可能な状態にしている。addResponse()はController内で以下のように定義した。
$scope.addResponse = function ($index, Response) { $scope.thread.answers[$index].tree.push({ response: Response, tree: false }); }
フォームの入力データであるResponseをModelに追加(push)しているのが上の処理になる。Controller編で書いたaddAnswer()とほぼ同じメソッドになるが、$indexを渡して特定の回答に紐づくレスポンスをModelに追加しているという点が異なる。
ここまでの作業でTwo-Way Data Bindingなフォームが実装されたことになる。
- レスポンスがある回答はツリー表示可能
- ツリー表示のチェックボックスをオンにするとレスポンスと数が表示される
- フォームも表示され、レスをすると回答が追加され数もカウントされる
AngularJSを利用すれば、Modelデータを中心としたユーザーアクションとデータのバインディングを効率的に実装することが可能。
Two-Way Data Binding編の次はBehaviorの使い方です。
AngularJS使い方ガイド記事一覧