在使用 Angularjs 的 directive 時,
有時我們會想要在外層 (即使用 directive 的 controller 或其他 directive) 去呼叫 directive 裡面的 function,
去改變一些 directive 的內部狀態,
這時使用 service 就是一個比較好的做好,
service 可以作為 directive 及使用 directive 的元件 (例如外部 controller 或其他 directive)
的溝通橋梁,
利用把 directive 的 scope 或 內部 controller 綁定設定到 service 中,
我們即可在外部將 service 注入,並使用 service 來控制 directive。
下面給出一個範例 : 可以看到,<my-form-directive> 是我們實作的一個 directive,
裡面有一個名為 "pristineForm" 的 function ,
其可以設定 $scope 裡面的 myForm (為在 directive template 中 name="myForm" 的 <form>)
成初始狀態 ( 使用 formController.$setPristine() )。
在 <my-form-directive> 外,有一個 <div> 被設定了 ng-click="ctrl.pristineForm() ,
會去呼叫外層 controller 的 pristineForm(),
而 pristineForm() 會利用引入的 directiveHandler 這個 service ,
去呼叫 myFormDirective 這個 directive 內部的 pristineForm()。
下面是詳細的程式碼:
Javascript :
angular.module("app", []) .controller("controller", ["directiveHandler", function(directiveHandler) { var self = this; self.pristineForm = function(directiveName) { var directiveName = directiveName ? directiveName : 'myFormDirective'; directiveHandler.getDirective(directiveName).pristineForm(); } }]) .directive("myFormDirective", ["directiveHandler", function(directiveHandler) { return { restrict: "E", template: `<div> <form name="myForm"> Username : <input type="text" name="username" ng-model="username" required/> <div ng-show="myForm.username.$dirty && myForm.username.$invalid"> Please fill in username. </div> <div>Form $pristine : {{myForm.$pristine}}</div> </form> </div>`, replace: true, scope: { }, link: function($scope, $elm, $attrs, $ctrl) { var directiveNameAttr = $attrs.dirName ? $attrs.dirName : "myFormDirective"; directiveHandler.registerDirective(directiveNameAttr, $scope); $scope.username = ""; $scope.pristineForm = function() { $scope.myForm.$setPristine(); } } }; }]).factory('directiveHandler', function() { var instance_map = {}; var service = { registerDirective: registerDirective, getDirective: getDirective, deregisterDirective: deregisterDirective }; return service; function registerDirective(name, ctrl) { instance_map[name] = ctrl; } function getDirective(name) { return instance_map[name]; } function deregisterDirective(name) { instance_map[name] = null; } });HTML :
<div ng-app="app" ng-controller="controller as ctrl"> <my-form-directive dir-name="myFormDir"></my-form-directive> <div ng-click="ctrl.pristineForm('myFormDir')"><button>Set Form Pristine</button></div> <my-form-directive dir-name="myFormDir2"></my-form-directive> <div ng-click="ctrl.pristineForm('myFormDir2')"><button>Set Form Pristine</button></div> </div>
說明:
在 directiveHandler 中,設定了一個內部管理的 instance_map,
用 key-value 的次式將需要的 directive instance (可能是 isolate scope 的 scope,可能是 directive 設定的 controller 等,依情況自行決定) 存起來,
設定了三個 function,
主要的為 registerDirective(name, ctrl) 和 getDirective(name),
registerDirective(name, ctrl) 主要在 directive 初始化時使用一次,
用想要的 name 作為 key,把想要暴露給外部使用的 instance 作為 ctrl 設定進來。
getDirective(name) 在外部使用,例如此例的 controller,
用 name 值來取得 directive 的內部 instance,例如此例即為 <my-form-directive> 的 isolate scope,
取得 instance 後,即可執行 instance 上所設定的 function,當然取得 instance 上設定的值也是可以的。
在 html 中使用 <my-form-directive> 時,給了一個 dir-name 屬性,
dir-name 屬性在 directive 用來作為 name 參數呼叫 registerDirective(name, ctrl),
如果有多個 directive 的話,為了區分各個 directive,
給定不同的 name 作為 instance 在 directiveHandler 中的 key 值將會
使管理各個 directive 的 instance 更為方便。
為了演示,我們在 html 中使用了兩個 <my-form-directive>,並給它們設定了不同的 dir-anem 屬性值。
參考資料:
- How to call a method defined in an AngularJS directive? (最喜歡Mudassir Ali的回答)
沒有留言 :
張貼留言