使用ui.router,我们有一个状态控制器:
controller('widget', function($repository, $stateParams){
$scope.widget = $repository.get($stateParams.id);
})
注册时:
.state('widget',
controller: 'widget',
template: '/widgetTemplate.html'
我们遇到了一个案例,我们希望将此控制器作为模板的一部分重用:
<div ng-controller="widget" ng-include="/widgetTemplate.html"></div>
但是似乎没有一种简单的方法可以使用正确的ID注入模拟的$ stateParams对象。类似的东西:
<div ng-controller="widget" ng-inject="{$stateParams: {id: 1234}}" ng-include="/widgetTemplate.html"></div>
除了编写增强ng-controller或重构代码以使用继承范围的自定义指令外,是否有任何开箱即用的方法可以做到这一点?
答案 0 :(得分:4)
我不相信有一种开箱即用的方式。 ng-controller
只使用普通的控制器实例化,没有机会注入任何东西。
但这是一个有趣的&#34;功能&#34;,实际上,可以通过自定义指令相对简单地构建。
以下是一个说明性示例(免责声明:绝对不会在晦涩的情况下进行测试):
.directive("ngInject", function($parse, $interpolate, $controller, $compile) {
return {
terminal: true,
transclude: true,
priority: 510,
link: function(scope, element, attrs, ctrls, transclude) {
if (!attrs.ngController) {
element.removeAttr("ng-inject");
$compile(element)(scope);
return;
}
var controllerName = attrs.ngController;
var newScope = scope.$new(false);
var locals = $parse(attrs.ngInject)(scope);
locals.$scope = newScope;
var controller = $controller(controllerName, locals);
element.data("ngControllerController", controller);
element.removeAttr("ng-inject").removeAttr("ng-controller");
$compile(element)(newScope);
transclude(newScope, function(clone){
element.append(clone);
});
// restore to hide tracks
element.attr("ng-controller", controllerName);
}
};
});
用法如您所述:
<div ng-controller="MainCtrl">
{{name}}
<div ng-controller="SecondCtrl" ng-inject="{foo: name, bar: 'bar'}">
</div>
</div>
当然,控制器可以注入这些变量:
.controller("SecondCtrl", function($scope, foo, bar){
});
答案 1 :(得分:1)
在某些地方,我使用了一个控制器用于状态和指令,看起来与您尝试做的类似。
您可以定义一个重用您的控制器和模板的指令。它会将您想要设置的状态添加为范围内可用的参数:
.directive('widget', function(){
return {
restrict: 'E',
template: '<div>id in directive {{widgetId}}</div>',
controller: 'widget',
scope: {
widgetId:'='
}
};
})
然后更新您的控制器以查看范围或状态参数:
.controller('widget', function($scope, $stateParams){
$scope.widgetId = $scope.widgetId || $stateParams.id;
})
最后,您可以使用它通过特定ID引用窗口小部件:
<widget widget-id="789"></widget>
这是一个带样本的傻瓜:http://plnkr.co/edit/0rSfr4jt48tSyHXwgnS5?p=preview
答案 2 :(得分:1)
答案似乎是“没有开箱即用”的方式。受到回复的启发here is what I ended up implementing。
用法:
<div ng-component="test.controller({$stateParams: { id: 1}})" template="test.html"></div>
<div ng-component="test.controller({$stateParams: { id: 2}})">
<div>Transcluded Template ID: {{id}}</div>
</div>
实现:
.directive('ngComponent', function($compile, $parse, $controller, $http, $templateCache) {
return {
restrict: 'A',
transclude: true,
scope: true,
compile: function(tElement, tAttr) {
return function(scope, element, attrs, ctrl, transclude) {
//credit for this method goes to the ui.router team!
var parseControllerRef = function(ref, current) {
var preparsed = ref.match(/^\s*({[^}]*})\s*$/),
parsed;
if (preparsed) ref = current + '(' + preparsed[1] + ')';
parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/);
if (!parsed || parsed.length !== 4) throw new Error("Invalid component ref '" + ref + "'");
return {
controller: parsed[1],
paramExpr: parsed[3] || null
};
};
var ref = parseControllerRef(attrs.ngComponent);
scope.$eval(ref.paramExpr);
if(attrs.template) {
$http.get(attrs.template, {cache: $templateCache}).then(function(result){
var template = $compile(result.data)(scope);
element.append(template);
},
function(err){
//need error handling
});
}
else {
transclude(scope, function(clone) {
element.append(clone);
})
}
var locals = {
$scope: scope
}
angular.extend(locals, scope.$parent.$eval(ref.paramExpr));
var controller = $controller(ref.controller, locals);
element.data("ngControllerController", controller);
//future: may even allow seeing if controller defines a "link" function or
//if the attrs.link parameter is a function.
//This may be the point of demarcation for going ahead and writing a
//directive, though.
};
}
};
})
.controller('test.controller', function($scope, $stateParams) {
$scope.id = $stateParams.id;
})
我使用了实现uiSref的代码的修改版本(有时我希望angular会使这些小块成为公共API的一部分)。
ngComponent是一种“轻量级”指令,可以在您的标记中声明,而无需实际构建指令。你可能会把它放得更远一些,但是在某些时候你还是需要编写一个指令。