AngularJSのカスタムディレクティブのlinkとcontrollerの使い分け

こんにちは。パンをたべました、きたけーです。

AngularJSのカスタムディレクティブをつくろうとして、悩むのがlinkとcontrollerの使い分けです。どちらもスコープや要素を操作して、ディレクティブの挙動を決めることができます。

この使い分けに関してはAngularの公式ドキュメントに指針が書いてあって( https://docs.angularjs.org/guide/directive )、

Best Practice: use controller when you want to expose an API to other directives. Otherwise use link.

(他のディレクティブに対してAPIを公開したい場合にはcontroller、そうじゃなかったらlinkをつかう)

とあります。

記事のタグの編集の具体例でみてみます。 (コードはCoffeeです)

app = angular.module 'app'

app.directive 'tagManager', ->
  scope: {}
  restrict: 'EA'
  controller: ['$scope', '$log', ($scope, $log) ->
    this.tags = $scope.tags = ['後で読む', 'これは便利']
  
    this.addTag = () ->
      newTag = $scope.newTag
      if this.tags.indexOf(newTag) < 0
        this.tags.push(newTag)
      $scope.newTag = ''

    this.deleteTag = ($index) ->
      $scope.tags.splice($index, 1)
  ]
  templateUrl: 'partials/tag_manager.tpl.html'
  replace: true

app.directive 'tag', ->
  transclude: true
  scope: { }
  restrict: 'EA'
  require: '^tagManager'
  templateUrl: 'partials/tag.tpl.html'
  replace: true
  link: (scope, element, attrs, tagManager) ->
    element.find('.delete-btn').on 'click', ->
      tagManager.deleteTag(attrs.tagIndex)
      scope.$apply()

この場合、tagManagerがtagの管理を司っているので、controllerでaddTagやdeleteTagといったAPIを公開しています。各tagでは、(こちらは公開するAPIがないので)linkで削除ボタンが押されたときに、tagManagerのAPIを呼び出すイベントリスナーを登録しています。

ぼやき: Angularのドキュメント、Best Practiceがいたるところに書かれているので暇さえされば何度でも目を通したほうがよさそう。