簡體   English   中英

AngularJS:如何將指令中的對象傳遞給包含的模板

[英]AngularJS : How to pass an object from the directive to transcluded template

我有一個指令,該指令創建一個允許用戶執行搜索的UI。 偽指令包裝將被覆蓋並成為每個單獨搜索結果模板的內容。 像這樣:

<search>
  <div class="someStyle" ng-click="selectResult(result)">{{result.Name}}</div>
</search>

我想用ng-click在控制器的作用域上調用selectResult函數,但是讓result對象來自指令。 如何在指令的隔離范圍內完成此操作?

除了使用ng-transclude之外,您還可以構建自己的搜索transclude指令,該指令可用於將結果放入已轉換的作用域。 例如,您的搜索偽指令可能看起來像這樣,其中包含ng-repeat和search-transclude偽指令,您需要在其中包含被轉換的內容:

.directive("search", function (SearchResults) {
    return {
        restrict: "AE",
        transclude: true,
        scope: {},
        template: '<div ng-repeat="result in results">Search Relevance:' +
        '{{result.relevance}}' +
        //the most important part search-transclude that receives the current
        //result of ng-repeat
        '<div search-transclude result="result"></div></div>',
        link: function (scope, elem, attrs) {
            //get search results
            scope.results = SearchResults.results;
        }
    }
})

如下構建搜索transclude指令:

.directive("searchTransclude", function () {
    return {
        restrict: "A",
        link: function (scope, elem, attrs, ctrl, $transclude) {
            //create a new scope that inherits from the parent of the
            //search directive ($parent.$parent) so that result can be used with other
            //items within that scope (e.g. selectResult)
            var newScope = scope.$parent.$parent.$new();
            //put result from isolate to be available to transcluded content
            newScope.result = scope.$eval(attrs.result);
            $transclude(newScope, function (clone) {
                elem.append(clone);
            });
        }
    }
})

如果包含在創建搜索指令的作用域中,那么被包含在內的內容現在將可以看到selectResult函數。 這里的例子。

被排除的內容將始終使用指令元素所在的范圍,即您的控制器范圍。 這就是為什么如果要讓selectResult函數的result參數從隔離范圍中獲取它的值,則需要在隔離范圍和控制器范圍的result屬性之間建立兩種方式的綁定。 result屬性設置為隔離范圍中的所需值后,控制器的范圍result屬性將更新為相同值。 因此,被排除的內容將使用控制器的result ,該result與隔離范圍的result同步。

1)將resultAttr='result'屬性添加到指令元素。

<search resultAttr='result'> <div class="someStyle" ng-click="selectResult(result)">{{result.Name}}</div> </search>

2)在指令中定義隔離范圍時,為result屬性建立雙向綁定:

scope: { result: "=resultAttr" }

3)在指令中將result設置為某個值

我想要ng-click [在指令中]在控制器的作用域上調用selectResult函數...

  1. 要將函數(或屬性)傳遞到隔離范圍,請在指令標記上使用屬性。

...但是結果對象來自指令[scope]。

  1. 如果您希望指令標記的內容可以訪問指令的范圍,則不要使用transclude。 指定transclude: true angular NOT允許指令標記的內容訪問指令的作用域-與您想要的相反。

要完成#1 ,您可以讓用戶指定模板,如下所示:

  <div ng-controller="MainCtrl">

    <search external-func='selectResult'>
      <div class="someStyle" ng-click="selectResult(result)">{{result.Name}}</div>
    </search>

  </div>

注意,用戶需要向<search>標簽添加額外的屬性。 但是,該html可能與angular的哲學相得益彰,該哲學認為html應該向開發人員提示在元素上將使用什么javascript。

然后您指定隔離范圍,如下所示:

      scope: {
        selectResult: '=externalFunc'
      },

要完成#2 ,請不要在指令中指定transclude: true

var app = angular.module('myApp',[]);

app.controller('MainCtrl', ['$scope', function($scope) {

  $scope.selectResult = function(result) {
    console.log("In MainCtrl: " + result.Name);
  };

}]);

app.controller('DirectiveCtrl', ['$scope', function($scope) {

  $scope.results = [ 
    {Name: "Mr. Result"},
    {Name: "Mrs. Result"}
  ]

}]);

app.directive('search', function() {

  return {
      restrict: 'E',

      scope: {
        selectResult: '=externalFunc'
      },

      template: function(element, attrs) {
      //                    ^       ^
      //                    |       |
      //    directive tag --+       +-- directive tag's attributes

        var inner_div = element.children();
        inner_div.attr('ng-repeat', 'result in results')

        //console.log("Inside template func: " + element.html());

        return element.html();  //Must return a string.  The return value replaces the innerHTML of the directive tag.
      },

      controller: 'DirectiveCtrl'
  }

}]);

如果您讓用戶更詳細地指定其模板,則html可以更好地記錄javascript的功能:

<search external-func='selectResult'>
  <div class="someStyle" 
    ng-click="selectResult(result)"
    ng-repeat="result in results">{{result.Name}}
  </div>
</search>

但是,如果您堅持使用簡約的html:

<search>
  <div class="someStyle" ng-click="selectResult(result)">{{result.Name}}</div>
</search>

...然后您可以動態添加ng-repeat屬性(如上所示),還可以將外部函數動態映射到隔離范圍:

var app = angular.module('myApp',[]);

app.controller('MainCtrl', ['$scope', function($scope) {

  $scope.selectDog = function(result) {
    console.log("In MainCtrl: you clicked " + result.Name);
  };

  $scope.greet = function(result) {
    console.log('MainCtrl: ' + result.Name);
  };

}]);

app.controller('DirectiveCtrl', ['$scope', function($scope) {

  $scope.results = [ 
    {Name: "Mr. Result"},
    {Name: "Mrs. Result"}
  ]

}]);

app.directive('search', function() {

  return {
    restrict: 'E',

    scope: {
      externalFunc: '&externalFunc'  //Cannot write => externalFunc: '&'
    },                               //because the attribute name is
                                     //'external-func', which means
                                     //the left hand side would have to be external-func.
    template: function(element, attrs) {
      //Retrieve function specified by ng-click:
      var inner_div = element.children();
      var ng_click_val = inner_div.attr('ng-click'); //==>"selectResult(result)"

      //Add the outer_scope<==>inner_scope mapping to the directive tag:
      //element.attr('external', ng_click_val); //=> No worky! Angular does not create the mapping.
      //But this works:
      attrs.$set('externalFunc', ng_click_val) //=> external-func="selectResult(result)"
      //attrs.$set('external-func', ng_click_val); //=> No worky!

      //Change ng-click val to use the correct call format:
      var func_args = ng_click_val.substring(ng_click_val.indexOf('(')); //=> (result)
      func_args =  func_args.replace(/[\(]([^\)]*)[\)]/, "({$1: $1})"); //=> ({result: result})
      inner_div.attr('ng-click', 'externalFunc' + func_args); //=> ng-click="externalFunc({result: result})"

      //Dynamically add an ng-repeat attribute:
      inner_div.attr('ng-repeat', 'result in results')

      console.log("Template: " + element[0].outerHTML);
      return element.html();
    },

    controller: 'DirectiveCtrl'
  }
})

如果要使用多個參數調用外部函數,可以執行以下操作:

var app = angular.module('myApp',[]);

app.controller('MainCtrl', ['$scope', function($scope) {

  $scope.selectResult = function(result, index) {
    console.log("In MainCtrl: you clicked " 
                 +  result.Name 
                 + " " 
                 + index);
  };

}]);

app.controller('DirectiveCtrl', ['$scope', function($scope) {

  $scope.results = [ 
    {Name: "Mr. Result"},
    {Name: "Mrs. Result"}
  ]

}]);

app.directive('search', function() {
  return {
    restrict: 'E',

    scope: {
      external: '='
    },

    template: function(element, attrs) {
      //Extract function name specified by ng-click:
      var inner_div = element.children();
      var ng_click_val = inner_div.attr('ng-click'); //=>"selectResult(result, $index)"
      var external_func_name =  ng_click_val.substring(0, ng_click_val.indexOf('(') ); //=> selectResult
      external_func_name = external_func_name.trim();

      //Add the outer_scope<==>inner_scope mapping to the directive tag:
      //element.attr('externalFunc', ng_click_val); => No worky!
      attrs.$set('external', external_func_name);  //=> external="selectResult"

      //Change name of ng-click function to 'external':
      ng_click_val = ng_click_val.replace(/[^(]+/, 'external');
      inner_div.attr('ng-click', ng_click_val);

      //Dynamically add ng-repeat to div:
      inner_div.attr('ng-repeat', 'result in results');

      console.log("Template: " + element[0].outerHTML);
      return element.html();
    },

    controller: 'DirectiveCtrl'
  }
});

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM