[英]Angular ui-router resolve value as string
使用ui-router,我在状态函数中添加所有解析逻辑,如下所示;
//my-ctrl.js
var MyCtrl = function($scope, customers) {
$scope.customers = customers;
}
//routing.js
$stateProvider.state('customers.show', {
url: '/customers/:id',
template: template,
controller: 'MyCtrl',
resolve: { // <-- I feel this must define as like controller
customers: function(Customer, $stateParams) {
return Customer.get($stateParams.id);
}
}
});
但是,IMO, resolve
对象必须属于控制器,如果在控制器文件中定义它,则易于读取和维护。
//my-ctrl.js
var MyCtrl = function($scope, customers) {
$scope.customers = customers;
}
MyCtrl.resolve = {
customers: function(Customer, $stateParams) {
return Customer.get($stateParams.id);
};
};
//routing.js
$stateProvider.state('customers.show', {
url: '/customers/:id',
template: template,
controller: 'MyCtrl',
resolve: 'MyCtrl.resolve' //<--- Error: 'invocables' must be an object.
});
但是,当我将其定义为MyCtrl.resolve
,由于IIFE,我收到以下错误。
Failed to instantiate module due to: ReferenceError: MyCtrl is not defined
当我将那个定义为字符串'MyCtrl.resolve'
,我得到了这个
Error: 'invocables' must be an object.
我看到控制器被定义为字符串,所以我认为也可以通过使用装饰器或其他东西将值提供为字符串。
有人做过这种方法吗? 这样我就可以保持我的routings.js干净并提供相关信息。 在相关文件中?
这听起来像是建立决心的一种巧妙方式,但我认为你不能做到这一点。
除了“解决”需要一个对象这一事实之外,它是在一个阶段定义的,其中所有可用的都是提供者。 此时,控制器甚至还不存在。
更糟糕的是,“解决”意味着定义控制器本身的输入。 要在控制器中定义解析,然后期望在创建控制器之前对其进行求值是循环依赖关系。
在过去,我已经在$stateProvider
定义之外定义了resolve函数,至少允许它们被重用。 我从来没有尝试过任何比这更好的人。
var customerResolve = ['Customer', '$stateParams',
function(Customer, $stateParams) {
return Customer.get($stateParams.id);
}
];
// ....
$stateProvider.state('customers.show', {
url: '/customers/:id',
template: template,
controller: 'MyCtrl',
resolve: {
customers: customerResolve
}
});
这个问题是关于ui-router包的功能。 默认情况下,ui-router不支持resolve参数的字符串。 但是如果你看一下ui-router的源代码,你会看到,可以实现这个功能,而无需直接更改它的代码。
现在,我将展示建议方法背后的逻辑及其实现
首先让我们看一下$ state.transitionTo函数angular-ui-router / src / urlRouter.js。 在该函数内部,我们将看到此代码
for (var l = keep; l < toPath.length; l++, state = toPath[l]) {
locals = toLocals[l] = inherit(locals);
resolved = resolveState(state, toParams, state === to, resolved, locals, options);
}
显然,这是为每个父状态解析“resolve”参数的地方。 接下来,让我们看一下同一文件中的resolveState函数。 我们会在那里找到这条线:
dst.resolve = $resolve.resolve(state.resolve, locals, dst.resolve, state);
var promises = [dst.resolve.then(function (globals) {
dst.globals = globals;
})];
这特别是在检索解析参数的promise时。 什么是好用的,这样做的功能被带到一个单独的服务。 这意味着我们可以使用装饰器来挂钩和改变它的行为。
作为参考,$ resolve的实现在angular-ui-router / src / resolve.js文件中
$ resolve的解析函数的签名是
this.resolve = function (invocables, locals, parent, self) {
“invocables”是我们声明国家的对象。 所以我们需要检查“invocables”是否为字符串。 如果是,我们将通过字符串获取控制器函数并在“。”之后调用函数。 字符
//1.1 Main hook for $resolve
$provide.decorator('$resolve', ['$delegate', '$window', function ($delegate, $window){
var service = $delegate;
var oldResolve = service.resolve;
service.resolve = function(invocables, locals, parent, self){
if (typeof(invocables) == 'string') {
var resolveStrs = invocables.split('.');
var controllerName = resolveStrs[0];
var methodName = resolveStrs[1];
//By default the $controller service saves controller functions on window objec
var controllerFunc = $window[controllerName];
var controllerResolveObj = controllerFunc[methodName]();
return oldResolve.apply(this, [controllerResolveObj, locals, parent, self]);
} else {
return oldResolve.apply(this, [invocables, locals, parent, self]);
}
};
return $delegate;
}]);
您还可以使用以下提供程序覆盖$ controllerProvider:
app.provider("$controller", function () {
}
这样就可以添加一个新函数getConstructor,它将按名称返回控制器构造函数。 所以你将避免在钩子中使用$ window对象:
$provide.decorator('$resolve', ['$delegate', function ($delegate){
var service = $delegate;
var oldResolve = service.resolve;
service.resolve = function(invocables, locals, parent, self){
if (typeof(invocables) == 'string') {
var resolveStrs = invocables.split('.');
var controllerName = resolveStrs[0];
var methodName = resolveStrs[1];
var controllerFunc = $controllerProvider.getConstructor(controllerName);
var controllerResolveObj = controllerFunc[methodName]();
return oldResolve.apply(this, [controllerResolveObj, locals, parent, self]);
} else {
return oldResolve.apply(this, [invocables, locals, parent, self]);
}
};
演示此方法的完整代码http://plnkr.co/edit/f3dCSLn14pkul7BzrMvH?p=preview
您需要确保控制器与状态配置位于同一个闭包中。 这并不意味着它们需要在同一个文件中定义。
因此,使用控制器的静态属性而不是字符串:
resolve: MyCtrl.resolve,
更新
然后为您的Controller文件:
var MyCtrl;
(function(MyCtrl, yourModule) {
MyCtrl = function() { // your contructor function}
MyCtrl.resolve = { // your resolve object }
yourModule.controller('MyCtrl', MyCtrl);
})(MyCtrl, yourModule)
然后,当您在另一个文件中定义状态时,在控制器文件之后包含或连接或需要:
(function(MyCtrl, yourModule) {
configStates.$inject = ['$stateProvider'];
function configStates($stateProvider) {
// state config has access to MyCtrl.resolve
$stateProvider.state('customers.show', {
url: '/customers/:id',
template: template,
controller: 'MyCtrl',
resolve: MyCtrl.resolve
});
}
yourModule.config(configStates);
})(MyCtrl, yourModule);
对于生产代码,您仍然希望将所有这些IIFE包装在另一个IIFE中。 Gulp或Grunt可以为您做到这一点。
如果打算将解析器与控制器放在同一个文件中,最简单的方法是将控制器文件中的解析器声明为函数:
//my-ctrl.js
var MyCtrl = function($scope, customers) {
$scope.customers = customers;
}
var resolverMyCtrl_customers = (['Customer','$stateParams', function(Customer, $stateParams) {
return Customer.get($stateParams.id);
}]);
//routing.js
$stateProvider.state('customers.show', {
url: '/customers/:id',
template: template,
controller: 'MyCtrl',
resolve: resolverMyCtrl_customers
});
这应该工作。
//my-ctrl.js
var MyCtrl = function($scope, customer) {
$scope.customer = customer;
};
//routing.js
$stateProvider
.state('customers.show', {
url: '/customers/:id',
template: template,
resolve: {
customer: function(CustomerService, $stateParams){
return CustomerService.get($stateParams.id)
}
},
controller: 'MyCtrl'
});
//service.js
function CustomerService() {
var _customers = {};
this.get = function (id) {
return _customers[id];
};
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.