Directiveで実現できたこと
@konpyu
2014/07/25 ng-mtg#6
自己紹介
• KON Yuichi (@konpyu)!
• Software Engineer in peace of cake!
• Server side engineer!
• Love Angular
Agenda
・noteについて
・noteでのユースケース
・Directive Tips
文章、音楽、イメージなどの
コンテンツを作って発表
簡単に販売できる
簡単に購入できる
マガジン
・ノートを束ねる機能

・雑誌のように編集

・カテゴリ機能として

・有料にもできる
開発の規模感
• Rails 4.1 + nginx + MySQL + AngularJS(1.2系)
• coffeeで8500Lくらい
• 14 Controller, 33 Service, 36 Directive
• エンジニア4名、デザイナー2名
Directive use case in note
機能単位にコンポーネント化
DOM操作を要するもの
機能単位にコンポーネント化
・Controllerは極力薄く。Directiveに適切な$scope
を与えることを中心事にする
・機能ごとにDirectiveに追い出す
・なるべくIsolate Directiveにする
・Isolateじゃなくても、ng-includeは使わずDirective
にする
フォロー
数字が増える
フォローする
リンクが消える
クリックすると…
フォロー
html
js
userをisolate scopeで受け取る
Clickイベントをbindする
成功したらフラグを立てる
見た目はng-classで切り替え
イベントを発行。通知バーで表示反映
フォロー
ユーザがフォローされた時の
イベントを$scope.$onで受信し
表示を切り替え
スキ
アイコンが追加される
星がつく
クリックすると…
スキ
noteをisolate scopeで受け取る
Clickイベントをbindする
OKならフラグを立てる &
スキ一覧の配列にunshift
モーダル
ui-bootstrapで提供されて
いる$modalサービスを
Directiveでラップ
モーダル
Clickイベントで
$modal.open()を呼ぶ
Directiveのlink関数のscopeと$modalに渡す
コントローラ内の$scopeが混じって危険なのが微妙
やってることはどれも大体同じ
・isolate scopeで変更対象のscopeを受け取る
・clickイベントにAPIをcallする処理を紐付ける
・表示の反映はng-classに適切な変数をbind
・必要に応じて$rootScope.$broadcastして更新通知
DOM操作を要するもの
$(element)に対する処理をディレクティブに隠
して再利用できるようにしておく
Shift + Enterで改行するテキストエリア
Enterを押すと送信されちゃう。Shift + Enterを押した

時には改行にしたい
Shift + Enterで改行するテキストエリア
lazyload.jsをラップして遅延ロード
lazyload.jsをラップして遅延ロード
imgタグの代わりにlazy-load-imageタグを使う
・改行に応じて広がるテキストエリア
・表示と同時にフォーカスを当てる
・領域外の任意の位置をクリックすると消える通知
・自動補完
他にも…
DOMを操作したくなったら
Directiveの出番
・Testablity / コンポーネント化 / 再利用性
・ui-bootstrapのような外部ライブラリも検討
・$watch内でDOM操作をすると激重になるので注意
Directive Tips
Directiveの親子関係
・Directiveを入れ子にしたいとき
・子の中でrequire: 親のDirective名 を指定する
・linkの第四引数に親のcontrollerが入る
・親のcontrollerではやり取り用のAPIを用意
Directiveの親子関係
子のlinkの
第四引数に入る
やり取り用のAPIを用意
・Directiveの内部で起こった変更をどう他の
ControllerやDirectiveに伝播させるか
!
・Angularアプリ全体の設計に関わる問題
それなりの規模のアプリを構築
すると必ずぶち当たる問題
ex) フォローボタンを押したら、通知欄の「xxxさんを
フォローする」を消す
いまのnote
$rootScope
NavbarController
$rootScopeからイベントを$broadcast
受信側は$scope.$onで受け取り処理を行う
$scope.$on followUser (uid) ->
# 表示の反映処理
Directive
UserController
# フォロー成功イベントを発行
$rootScope.$broadcast followUser , uid
Directive
イベントベースの問題点
・多用するとコードが追いにくくなる(なってる)
・2 way data bindingのメリットを捨てている
MainCtrlを置く方法
[参考] http://gon.to/2013/05/01/sharing-data-state-on-
angularjs-alternatives-comparison-and-my-solution/
MainController
UserController NavbarController
Directive Directive
scope:
user: =
Follow.save

, (res) ->
scope.user.is_follow = true
scope:
user: =
$scope.obj.user = {}
$scope.obj.user =
{ name: kon , is_follow: false}
2 way data bindingされて
自動的に表示変更
MainCtrlベース
・子のcontrollerは親のcontrollerのscopeにアクセスできる
性質を利用
・MainCtrlにはbindingする変数定義のみ置く
・初期化は子のcontrollerで行う
・2 way data bind されるので$rootScope.$broadcast不要
・MainCtrlの肥大化が懸念
実際にあった悲劇
!?
継承に関するDot Rule
・$scopeの継承の仕組みはjavascriptのprototype chainと
同じ($scope is pure javascript object)
・子の$scopeにhogeが無ければ、親の$scopeへhogeを探
しに行く
・hogeが文字列や整数のようなprimitiveな変数だと
prototype chainが実行されない
・$scope.container.hoge のようにしてobjectを挟むとよい
まとめ
Directiveを用いると
・共通部分を追い出して使いまわせる
・カプセル化
・結果的にControllerを薄く出来る
・DOM操作がAngular wayに乗って行える
参考資料
!
Directive to Directive Communication
http://www.thinkster.io/angularjs/sMgLuIxf02/angularjs-directive-to-directive-
communication
!
PROPOSED ANGULAR S WEBPAGE STRUCTURE
http://gon.to/2013/05/18/proposed-angulars-project-structure
!
Nested Scopes in AngularJS
http://jimhoskins.com/2012/12/14/nested-scopes-in-angularjs.html
!
Combining AngularJS with existing Components
http://henriquat.re/directives/advanced-directives-combining-angular-with-
existing-components-and-jquery/angularAndJquery.html
!
参考資料
Rethinking AngularJS Controllers
http://toddmotto.com/rethinking-angular-js-controllers/
!
Advanced Design Patterns and Best Practice
http://trochette.github.io/Angular-Design-Patterns-Best-Practices/#/intro
!

Directiveで実現できたこと