答案似乎是“不开箱即用”的方式。受到回复的启发,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 是一种“轻量级”指令,可以在您的标记中声明,而无需实际构建指令。你可能会走得更远一点,但在某些时候你会越界到需要编写指令。