我有一个指令,它创建一个允许用户执行搜索的 UI。该指令包装内容,这些内容将被嵌入并成为每个单独搜索结果的模板。像这样的东西:
<search>
<div class="someStyle" ng-click="selectResult(result)">{{result.Name}}</div>
</search>
我希望 ng-click 在控制器的范围内调用 selectResult 函数,但让result
对象来自指令。如何使用指令中的隔离范围来完成此操作?
我有一个指令,它创建一个允许用户执行搜索的 UI。该指令包装内容,这些内容将被嵌入并成为每个单独搜索结果的模板。像这样的东西:
<search>
<div class="someStyle" ng-click="selectResult(result)">{{result.Name}}</div>
</search>
我希望 ng-click 在控制器的范围内调用 selectResult 函数,但让result
对象来自指令。如何使用指令中的隔离范围来完成此操作?
您可以构建自己的搜索 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;
}
}
})
构建搜索嵌入指令如下:
.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 函数。这里的例子。
嵌入的内容将始终使用指令元素所在的范围,即您的控制器范围。这就是为什么如果您希望函数的result
参数从隔离范围获取它的值,那么您需要在隔离范围和控制器范围的属性selectResult
之间建立双向绑定。result
在隔离范围内将属性设置result
为所需值后,控制器的范围result
属性将更新为相同的值。因此,嵌入的内容将使用result
与隔离范围同步的控制器result
。
1)resultAttr='result'
向指令元素添加属性。
<search resultAttr='result'>
<div class="someStyle" ng-click="selectResult(result)">{{result.Name}}</div>
</search>
result
2)在指令中定义隔离范围时为属性建立双向绑定:
scope: {
result: "=resultAttr"
}
3)result
在指令中设置为某个值
我希望 ng-click [in the directive] 在控制器的范围内调用 selectResult 函数...
...但结果对象来自指令 [范围]。
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>
<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'
}
});