Download - 活動報告6 オートコンプリート的なディレクティブを作る-
2015/04 月活動内容
1. angular.js1.3 系を利用して再利用可能なディレクティブを作成するその②
Look at me mom...dad and I are having so much fun!
1. 概要1. 今回作る予定のディレクティブ
オートコンプリート的な directive を作る。前回のが初級編だとしらたたぶん中級編とかになるのかな。
完成イメージ化こちら
input にフォーカスインすると設定されたリストを表示する
keyup で入力文字に一致する内容のみを表示する
ラベル表示になる
2. ディレクティブを考える1. restrict
まずは、 Directive の運用方法を何にするかを考えてみる。ピローンって部分は directive が独自に表示させなきゃいけない部分だし、 E にする。
指定できるオプションは下の表みてください。
A 属性名として利用可能
E 要素名として利用可能
C クラス名として利用可能
M コメントとして利用可能
restrict のオプション
2 . ディレクティブを考える2. scope
次は scope の生成方法を考えてみる。いろんな要素に使うと思うので今回も分離スコープにする。
false 利用箇所の scope を共有
true 利用箇所の scope から派生した scope を生成
オブジェクトリテラル
分離スコープを生成
scopeのオプション
指定する属性はきっと全部使う。ざっと思いつくのは ①リストを表示するためにデータバインドする( = ) ②リストに表示する名称を特定するプロパティを文字列としてもらう (@) ③リストを選択した時に実行されるコントローラー側の function(&) ④選択したものを保持しておくためにデータバインド (=)
@ 文字列として結びつける
= データバインディングとして結びつける
& function として結びつける
属性
2 . ディレクティブを考える
3. template
ペローンの部分を directive に記述する場合は、 template を使う。今回は template に直接 HTML を書くけど、 template を別に切り出すこともできる。その場合に、 directive で読み込む場合は templateUrl にパスを指定すれば OK 。HTML はこんな感じ。
ここまでをコーディングするとこんな感じ。
2 . ディレクティブを考える3. template
HTML の内容をざっくり解説。bootstrap3 系をつかってるので、各 class には bootstrap のを指定してる。独自に作ったのは、この directive 自体に適用されるものと、「 d-complete-input 」。
ng-show :
anglar が提供する directive
指定された式が true の場合は要素を表示するng-repeat :
anglar が提供する directive
指定された配列分処理を繰り返すng-click :
angular が提供する directive
クリックイベントを検知して、指定された function を実行する
独自の CSS 定義
2 . ディレクティブを考える5. link
前回と違い、今回は結構実装しないとダメっぽい。作るメソッドはこんな感じ。① 初期処理② バインドされた値を watch する処理③ フォーカスインした時の処理④ フォーカスアウトした時の処理⑤ リストがクリックされた時の処理⑥ 入力された文字列に合致するものを抽出する処理
link 関数に指定するパラメータは前回と同じ下の 3 つ
scope directiveの scope
element 指定された要素
attrs 属性
2 . ディレクティブを考える
5. link
② バインドされた値を watch する処理angular には $watch っていう scope の値が変更されるのを監視するメソッドがある。これを利用してリストの中身が変更されたら処理を実行するようにしたりできる。
なんで watch するようにするかって、バインドされたデータを一度保持させたいからってのと、後々 soket.io 使った時にリストの中身とか変えると思うから。link 関数が実行されたときに、 socpe は生成されているけど、値はまだバインドされてなかった。HTML 生成時はちゃんとバインドされてるけどね。
右の図が watch のメソッド。第 1 パラメータに監視対象の変数名を指定して、第 2 パラメータはfunction になる。function のパラメータは 2 つあって、変更後の値と変更前の値をそれぞれうけとることができる。
今の段階では、 angular.copy の部分が必要だった。angular.copy でディープコピーしてます。
2 . ディレクティブを考える5. link
③ フォーカスインした時の処理要素のインプットにフォーカスインした時にリストを表示する処理を記述する。template に ng-show してるところで指定した isFocus を変更してピローンする。
この要素のになる input を find して、それに focus イベントを設定する。んで、実行されたら scope.isFocus を true にする。分離スコープが親スコープと双方間バインディングしていて、 ajax やインベント待ちのような場合は、 angular は変更を検知できない。scope.$apply はデータバインドを更新する処理で、これにより view 側がちゃんと変更されるようになる。
変更を検知すると ng-show してるところ(赤枠)の値がバインドによって変更されるため ul タグがピローンする。
変更検知!
2 . ディレクティブを考える5. link
④ フォーカスアウトした時の処理要素のインプットがフォーカスアウトした時にリストを非表示にする処理を記述する。この時リストにあるもの以外が指定されてたら、インプットを赤くする。
この処理でのポイントは 300ミリ秒遅延させてるところ。よくわからんけど、こうしないとうまく動作しなかった。
2 . ディレクティブを考える5. link
⑤ リストがクリックされた時の処理リストのアイテムにそれぞれ ng-click を設定することで、クリックされたものがどれかをパラメータでもらえるようにした。
処理内容は普通です。scope.execute が存在していたら、指定された function
を実行するって感じです。
2 . ディレクティブを考える5. link
⑥ 入力された文字列に合致するものを抽出する処理
これも別に普通の Javascript です。
基本ディープコピーしてます。後は入力文字列を含むものをチェックしてリストにプッシュしてからのディープコピーでリストの内容を変更してます。
3. ディレクティブを使う1. HTMLに指定する
最後に、作ったディレクティブを使ってみる。
replace を指定しているから、実際 HTML にコンパイルされるときは、 auto-complete-directive って名称はでないで、template の HTML が展開される。