AngularJS:如何将指令中的对象传递给transcluded模板

时间:2014-12-03 18:35:00

标签: javascript angularjs angularjs-directive angularjs-scope angularjs-ng-transclude

我有一个指令,可以创建一个允许用户执行搜索的UI。该指令包含将转换并成为每个单独搜索结果的模板的内容。像这样:

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

我希望ng-click能够在控制器的范围内调用selectResult函数,但是result对象来自指令。如何使用指令中的隔离范围来实现此目的?

3 个答案:

答案 0 :(得分:2)

您可以构建自己的搜索transclude指令,而不是使用ng-transclude,该指令可用于将结果放到transcluded范围内。例如,您的搜索指令可能看起来像这样,使用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函数存在于创建搜索指令的作用域中,则现在可以看到已转换的内容。示例here

答案 1 :(得分:1)

Transcluded内容将始终使用指令元素所在的范围,即您的控制器范围。这就是为什么如果你想让result函数的selectResult参数从孤立的范围中获取它的价值,那么你需要在孤立的范围之间建立双向绑定&#39; s和控制器范围的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设置为指令

中的某个值

答案 2 :(得分:1)

  

我喜欢ng-click [在指令中]来调用selectResult   控制器的作用范围......

  1. 要将函数(或属性)传递到隔离范围,请使用指令标记上的属性。
  2.   

    ...但是结果对象来自指令[scope]。

    1. 如果您希望指令标签的内容可以访问指令的范围,则不要使用转码。指定transclude: true告诉angular NOT允许指令标记的内容访问指令的范围 - 与你想要的相反。
    2. 要完成#1 ,您可以让用户指定模板,如下所示:

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

      注意,用户需要在<search>标记中添加额外的属性。然而,html可能更符合角度的理念,即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'
        }
      });