AngularjsでDirectiveを作るときに考えていること
最近、仕事や趣味でAngularjsのDirectiveを作る機会が何回かありました。Directiveは非常に強力な機能ですが、使い方を誤ると碌なことにならないので現在どのような方針でDirectiveを作っているのかを書きます。
いまや最も優れたJavaScriptフレームワーク「AngularJSリファレンス」出版記念会 イベントレポートでも触れられているように、ベストプラクティスが見つかっていない感じなので色々な人の意見を聞きたいです。
基本的な方針
可能な限りDirectiveは作らない
DirectiveはAngularjsの中でも仕様が複雑で理解しづらく、ファイルが分散するので作るだけの明確な理由がないものについてはDirectiveにするべきではない。Angularjsはビルトインディレクティブが充実しており、大半の問題はこれを利用すれば解決する。無駄な重荷を負う必要はない。
どのような時にDirectiveを作るか
以下の様な場合にDirectiveを作る
- この実装をDirectiveにせずに放置した場合
- HTMLが異常に長くなったりネストが深くなったりして、人間が理解できるものではなくなる
- Controllerのscopeに大量のメソッドが生えることになる
- 異なる場所で大量のHTMLを何度も書くハメになる
restrict: 'EA'
は使わない
作ろうとしているDirectiveが属性的(Attribute)なものなのか、要素的(Element)なものなのかをよく考えて決める。
公式に決まりがあるわけではないが、必ずどちらかに決めて restrict
を指定する。
- Attribute は以下のいずれかもしくは全てに当てはまる場合に用いる
- テンプレートを展開しない
- 対象とするDOMに対して何かしらの入力(テキストボックスに値を入れるとか、クリックするとか、ドラッグするとか)を実装することが主な責務である
- Element は以下のいずれかもしくは全てに当てはまる場合に用いる
- テンプレートを展開する
- 対象とするDOMを利用して何かしらの出力(表示とか)を実装することが主な責務である
Attribute Directiveはまだあんまり整理できていない
以下、Attributeとして作成するDirectiveをAttribute Directive、Elementとして作成するDirectiveをElement Directiveと表記する。
Directiveを設計する
BEMで考える
BEMという方法論がある。これ自体はCSSの命名ルール等でよく利用されるが、AngularjsのDirectiveをどう分割するか、どのように設計するかを考えるときのヒントとしてBEMを用いる。
BlockとElement Directiveを対応させる
BlockもしくはModifierの単位でDirectiveを分割する。Blockが入れ子になっている場合は最も外側のBlockから以下の問答をして'Yes'が多ければ子供に分割していく
- 自分の子Blockを再利用して他のところで使いたいか
- 自分の子Blockを「もう一度書け」と言われた時に、コードを見ないで書くことは不可能か
- ユーザがこのアプリケーションを使った時、子Blockに求める機能と親Blockに求める機能が全く別か
Attribute Directive に関しては知見がなくて何も言えない…。
ElementとjqLiteで操作する範囲を対応させる
link
オプションの中でDOM Elementを操作したいケースが存在するかもしれない。それは問題ない。
ただしlink
オプションで提供されるjqLiteオブジェクトのうち、操作して良いのはElementに当たる部分のみである。その範囲を超えてDOMを操作してはいけない。
ModifierとIsolate Scopeを対応させる
原則としてIsolate Scopeを利用する。外部から渡されるオブジェクトは以下のものに分類する。
- データ
- Blockの状態(≒ Modifier)
また、以下の様な原則を守る。
- データとBlockの状態が混在したオブジェクトを渡さない
- Blockの状態を単一で巨大なオブジェクトとして渡さない
【補足】BEMについて
BEMについての解説はここを参照する。
Directiveを書く
コーディングに関するルールを列挙する。これは正しいかどうか確信がないので信用しすぎないこと。