Javascript 角度ui路由器将值解析为字符串
使用ui路由器,我在状态函数中添加了所有解析逻辑,如下所示Javascript 角度ui路由器将值解析为字符串,javascript,angularjs,angular-ui-router,angularjs-routing,angularjs-controller,Javascript,Angularjs,Angular Ui Router,Angularjs Routing,Angularjs Controller,使用ui路由器,我在状态函数中添加了所有解析逻辑,如下所示 //my-ctrl.js var MyCtrl = function($scope, customers) { $scope.customers = customers; } //routing.js $stateProvider.state('customers.show', { url: '/customers/:id', template: templa
//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);
}
}
});
但是,当我将其定义为MyCtrl.resolve
时,由于IIFE,我得到了以下错误
Failed to instantiate module due to: ReferenceError: MyCtrl is not defined
当我将它定义为string'MyCtrl.resolve'
时,我得到
Error: 'invocables' must be an object.
我看到控制器被定义为string,所以我认为也可以通过使用decorator或其他东西来提供string值
有人用过这种方法吗?这样我就可以保持routings.js的整洁,并放置相关信息。在一个相关的文件中?这听起来是一个构建解析的好方法,但我认为你做不到 除了“解析”需要一个对象这一事实之外,它是在一个阶段定义的,在这个阶段中,所有可用的都是提供者。此时,控制器甚至还不存在 然而,更糟糕的是,“解析”意味着定义控制器本身的输入。要在控制器中定义解析,然后期望在创建控制器之前对其进行评估,这是一个循环依赖项 过去,我在
$stateProvider
定义之外定义了解析函数,至少允许重用它们。我从来没有试过找比这更喜欢的人
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
}
});
您需要确保控制器与状态配置在同一个闭包中。这并不意味着它们需要在同一个文件中定义 因此,不要使用字符串,而是使用控制器的静态属性:
resolve: MyCtrl.resolve,
更新
然后,对于控制器文件:
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);
然后,当您在另一个文件中定义状态时,该文件在控制器文件之后包含、连接或必需:
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中。大口喝或咕噜可以帮你 这个问题是关于ui路由器包的特性。默认情况下,ui路由器不支持resolve参数的字符串。但是如果你看一下ui路由器的源代码,你会发现,不直接修改代码就可以实现这个功能 现在,我将展示建议方法背后的逻辑及其实现 分析代码 首先,我们来看看$STATE.TIVATION函数角UI路由器/SRC/urLouTr.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);
}
这是检索解析参数承诺的具体位置。什么是好的使用,功能,这是采取了一个单独的服务。这意味着我们可以通过decorator钩住并改变它的行为
为了便于参考,$resolve的实现在angular ui router/src/resolve.js文件中
实现钩子
$resolve的resolve函数的签名为
this.resolve = function (invocables, locals, parent, self) {
其中“invocables”是我们国家声明的对象。所以我们需要检查“invocales”是否是字符串。如果是,我们将通过字符串获得控制器函数,并在“.”字符后调用函数
//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]);
}
};
演示此方法的完整代码如果打算将冲突解决程序与控制器放在同一个文件中,最简单的方法是在控制器文件中将冲突解决程序声明为函数:
//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];
};
}
配置状态,它不需要有完整的控制器对象,正如您看到的控制器只是一个字符串。这是真的,但在运行时,为了让Angular计算解析,它需要创建控制器。在评估解析之前,它无法创建控制器。当每个文件都使用IIFE时,如何使控制器处于相同的闭包中?在构建时在连接的js周围添加IIFE。Uglify可以选择将代码包装成一个闭包,一个生命。这样,您的代码将在相同的生命周期内。这将在生产模式下工作,但在开发模式下不工作,因为开发模式不会运行任何丑陋的程序。我用一个解决方案更新了我的原始答案,该解决方案应能在有或没有构建过程的情况下工作。我喜欢它,除了您从
$window
而不是$controller
获取控制器之外。是否可以使用$controller
?不管怎样,我都会给你赏金,因为你尽了最大的努力来解决这个问题。不幸的是,这几乎是不可能的$控制器实例化控制器并尝试解析依赖项。但因为我们有自定义依赖项(解析参数)$controller会说它不知道“customers”参数。可以修饰$controllerProvider服务并覆盖其注册功能。这将提供一种获取控制器功能列表的方法。但如今,这是不允许的。Angular公开$get函数的结果,而不是实际的服务。所以“register”功能被忽略了,下一个选项是修改$injector。但是,angular不允许修饰这个服务,简单地重写它的方法也不会带来任何效果。好的,看起来你可以用provider函数重写$controllerProvider。通过这种方式,您可以向其添加必要的“getConstructor”,并避免使用$window对象获取控制器构造函数。我用这些新发现更新了plunker。我不建议实施此解决方案。UI Router 1.0不再使用$resolve
服务,因此此解决方案的使用寿命有限。如前所述,此时控制器还不可用。我使用的设计模式是使用解析器
服务。在解析函数作用域中使用this.self.name
(您在其中发出Customer.get()
请求),可以获取状态的名称。然后,执行类似于返回解析器.prepare(this.self.name)
的操作,您可以将所有逻辑提取到解析器中,并减少