詳解angularJs中自定義directive的數據交互

就我對directive的粗淺理解,它通常用於獨立Dom元素的封裝,應用場合爲控件重用和邏輯模塊分離。後者我暫時沒接觸,但數據交互部分倒是同樣的。因此舉幾個前者的例子,以備之後忘記。
directive自己的做用域$scope能夠選擇是否封閉,不封閉則和其controller共用一個做用域$scope。例子以下:
<body ng-app="myApp" ng-controller="myCtrl">
<test-directive></test-directive>
<script>
  angular.module("myApp",[])
      .controller("myCtrl",function($scope){
        $scope.data = {
          name:"白衣如花"
        };
      })
      .directive("testDirective",function(){
        return {
          restrict:"E",
          template:"<h1>{{data.name||'未定義'}}</h1>"
        }
      });
</script>
</body>
顯示結果爲:白衣如花,能夠知道directive中的data.name就是myCtrl控制器中的$scope.data.name。 
  那麼封閉的directive呢?怎麼封閉,封閉效果是什麼樣的,封閉後怎麼數據交互?這些都是我這幾天摸索的東西。
<body ng-app="myApp" ng-controller="myCtrl">
<test-directive></test-directive>
<script>
  angular.module("myApp",[])
      .controller("myCtrl",function($scope){
        $scope.data = {
          name:"白衣如花"
        };
      })
      .directive("testDirective",function(){
        return {
          restrict:"E",
          scope: {},
          template:"<h1>{{data.name||'未定義'}}</h1>"
        }
      });
</script>
</body>
結果顯示爲:未定義。因此在directive定義時,添加屬性scope就能夠把directive的做用域和父控制器的做用域分離開來。
好,瞭解了開放和封閉以後,進入主題,如何進行數據交互。我的以爲數據交互分爲:父控制器獲取directive的變量;directive獲取父控制器的變量;父控制器調用directive的函數;directive調用父控制器的函數。
1.父控制器獲取directive的變量。好比封裝了一個輸入框接受用戶輸入,父控制器點擊搜索按鈕要獲取用戶輸入:
<body ng-app="myApp" ng-controller="myCtrl">
<p>名字:{{outerName}}</p>
<test-directive inner-name="outerName"></test-directive>
<script>
  angular.module("myApp",[])
      .controller("myCtrl",function($scope){
      })
      .directive("testDirective",function(){
        return {
          restrict:"E",
          scope: {
            innerName: "="
          },
          template:"<input type='text' ng-model='innerName' placeholder='白衣如花'>"
        }
      });
</script>
</body>
顯示結果以下:

分析:從數據流向提及,testDirective中的一個input輸入綁定在innerName中,innerName是directive私有做用域擁有的變量,外部控制器不能直接用。經過innerName: "="傳遞給html中的inner-name屬性,
而inner-name屬性則綁定在外部控制器的outerName變量中,因此最後顯示在最上面的<p>標籤內。上述代碼等價於以下代碼:
<test-directive name="outerName"></test-directive>
scope: {
  innerName: "=name"
},
由inerName: "="變成了innerName: "=name",而html屬性綁定也由inner-name變成了name。
 2.directive獲取父控制器的變量。這個應用場合應該挺多的,好比公共的頁眉頁腳,公共的展現面板等。
這個用上面例子的"="同樣能夠實現,因而咱們知道了"="是雙向綁定。可是咱們要防止directive內部意外修改數據該怎麼辦呢?因而單向綁定"@"就出場了。
<body ng-app="myApp" ng-controller="myCtrl">
<input type='text' ng-model='outerName' placeholder='白衣如花'>
<test-directive inner-name="{{outerName}}"></test-directive>
<script>
  angular.module("myApp",[])
      .controller("myCtrl",function($scope){
      })
      .directive("testDirective",function(){
        return {
          restrict:"E",
          scope: {
            innerName: "@"
          },
          template:"<p>名字:{{innerName}}</p>" +
          "<button ng-click='innerName=innerName+1'>點擊</button>"
        }
      });
</script>
</body>
值得注意的是:@在html的屬性綁定時須要{{}}開標識,而=則不用。個人理解是,對於父控制器而言,@是數據傳遞,而=是數據綁定,因此有這些區別。directive中加入了一個按鈕用於驗證修改數據後
父控制器是否發生改變,結果是=有變化,@無變化,很容易得出結論:=是雙向綁定,@是雙向綁定。
 3.directive調用父控制器的函數。應用場合,暫時想不到(汗)。
變量用=和@來傳遞,函數則用&。例子以下:
<body ng-app="myApp" ng-controller="myCtrl">
<p>名字:{{outerName}}</p>
<test-directive on-click="click(name)"></test-directive>
<script>
  angular.module("myApp",[])
      .controller("myCtrl",function($scope){
        $scope.click = function (name) {
          $scope.outerName = name || "白衣如花";
        }
      })
      .directive("testDirective",function(){
        return {
          restrict:"E",
          scope: {
            onClick: "&"
          },
          template:"<input type='text' ng-model='innerName' placeholder='白衣如花'>" +
          "<button ng-click='onClick({name: innerName})'>點擊</button>"
        }
      });
</script>
</body>
數據傳遞流程和之上的例子差很少,惟一要注意的是參數傳遞時,{name: innerName}前者是形參,後者是實參。多個參數時,參數順序不重要,形參一一對應。
4.父控制器調用directive的函數。這個是前段時間遇到的難點,狀況較其餘複雜一些。應用場合也很廣泛,好比初始化,重置等。
<body ng-app="myApp" ng-controller="myCtrl">
<button ng-click="click()">重置</button>
<test-directive action="action"></test-directive>
<script>
  angular.module("myApp",[])
      .controller("myCtrl",function($scope){
        $scope.action = {};
        $scope.click = function () {
          $scope.action.reset();
        }
      })
      .directive("testDirective",function(){
        return {
          restrict:"E",
          scope: {
            action: "="
          },
          template:"<input type='text' ng-model='name' placeholder='白衣如花'>",
          controller: function ($scope) {
            $scope.action.reset = function () {
              $scope.name = "白衣如花"
            }
          }
        }
      });
</script>
</body>
又一次用到了=,利用了js中函數也是屬性的原理。彷佛,理解了=的雙向綁定,就很容易調用directive內部函數了。可是初始化呢?
首先想到的是相似的=來引用傳遞:
<body ng-app="myApp" ng-controller="myCtrl">
<test-directive action="action"></test-directive>
<script>
  angular.module("myApp",[])
      .controller("myCtrl",function($scope){
        $scope.action = {};
        $scope.action.init();
      })
      .directive("testDirective",function(){
        return {
          restrict:"E",
          scope: {
            action: "="
          },
          template:"<input type='text' ng-model='name' placeholder='白衣如花'>",
          controller: function ($scope) {
            $scope.action.init = function () {
              $scope.name = "白衣如花"
            }
          }
        }
      });
</script>
</body>
可是運行卻發現,錯誤顯示$scope.action.init is not a function,看提示應該是運行到第7行的時候,$scope.action.init函數還未定義。怎麼辦呢?把directive提到controller以前試試?同樣是錯誤。
嗯,能夠不用函數,直接在directive的controller中執行$scope.name = "白衣如花",彷佛很完美,但若是是有參數的初始化呢?事實上js分離後,我遇到的問題是根據http請求的返回結果來初始化directive,因爲
網絡快慢不必定,致使控件初始化時不必定有http請求的返回(沒有有效的初始化參數),也不能保證http請求返回後directive已經初始化(不能用=來進行函數調用)。 
需求很明瞭了,若是能監控參數變化,再執行初始化,此時能保證directive已經加載,並且有有效的參數。正好angularjs提供了$watch。代碼以下:
<body ng-app="myApp" ng-controller="myCtrl">
<test-directive action="action"></test-directive>
<script>
  angular.module("myApp",[])
      .controller("myCtrl",function($scope){
        $scope.action = {name: "白衣如花"};
      })
      .directive("testDirective",function(){
        return {
          restrict:"E",
          scope: {
            action: "="
          },
          template:"<input type='text' ng-model='name' placeholder='白衣如花'>",
          link: function (scope, elem, attrs) {
            scope.$watch(attrs.action, function (value) {
              scope.action.init();
            })
          },
          controller: function ($scope) {
            $scope.action.init = function () {
              $scope.name = $scope.action.name
            }
          }
        }
      });
</script>
</body>
這是我對於directive數據交互的粗淺理解。想要更詳細瞭解,請參看官方文檔:https://docs.angularjs.org/guide/directive
相關文章
相關標籤/搜索