简体   繁体   English

Angular ui-router将值解析为字符串

[英]Angular ui-router resolve value as string

With ui-router, I add all resolve logic in state function like this; 使用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);
        }
      }
    });

However IMO, resolve object must belong to a controller, and it's easy to read and maintain if it is defined within a controller file. 但是,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.
    });

However, When I define it as MyCtrl.resolve , because of IIFE, I get the following error. 但是,当我将其定义为MyCtrl.resolve ,由于IIFE,我收到以下错误。

Failed to instantiate module due to: ReferenceError: MyCtrl is not defined

When I define that one as string 'MyCtrl.resolve' , I get this 当我将那个定义为字符串'MyCtrl.resolve' ,我得到了这个

Error: 'invocables' must be an object.

I see that controller is defined as string, so I think it's also possible to provide the value as string by using a decorator or something. 我看到控制器被定义为字符串,所以我认为也可以通过使用装饰器或其他东西将值提供为字符串。

Has anyone done this approach? 有人做过这种方法吗? So that I can keep my routings.js clean and putting relevant info. 这样我就可以保持我的routings.js干净并提供相关信息。 in a relevant file? 在相关文件中?

It sounds like a neat way to build the resolve, but I just don't think you can do it. 这听起来像是建立决心的一种巧妙方式,但我认为你不能做到这一点。

Aside from the fact that "resolve" requires an object, it is defined in a phase where all you have available are providers. 除了“解决”需要一个对象这一事实之外,它是在一个阶段定义的,其中所有可用的都是提供者。 At this time, the controller doesn't even exist yet. 此时,控制器甚至还不存在。

Even worse, though, the "resolve" is meant to define inputs to the controller, itself. 更糟糕的是,“解决”意味着定义控制器本身的输入。 To define the resolve in the controller, then expect it to be evaluated before the controller is created is a circular dependency. 要在控制器中定义解析,然后期望创建控制器之前对其进行求值是循环依赖关系。

In the past, I have defined resolve functions outside of the $stateProvider definition, at least allowing them to be reused. 在过去,我已经在$stateProvider定义之外定义了resolve函数,至少允许它们被重用。 I never tried to get any fancier than that. 我从来没有尝试过任何比这更好的人。

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
  }
});

This question is about features of ui-router package. 这个问题是关于ui-router包的功能。 By default ui-router doesn't support strings for resolve parameter. 默认情况下,ui-router不支持resolve参数的字符串。 But if you look at the source code of ui-router you will see, that it's possible to implement this functionality without making direct changes to it's code. 但是如果你看一下ui-router的源代码,你会看到,可以实现这个功能,而无需直接更改它的代码。

Now, I will show the logic behind suggested method and it's implementation 现在,我将展示建议方法背后的逻辑及其实现

Analyzing the code 分析代码

First let's take a look at $state.transitionTo function angular-ui-router/src/urlRouter.js. 首先让我们看一下$ state.transitionTo函数angular-ui-router / src / urlRouter.js。 Inside that function we will see this code 在该函数内部,我们将看到此代码

  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);
  }

Obviously this is where "resolve" parameters are resolved for every parent state. 显然,这是为每个父状态解析“resolve”参数的地方。 Next, let's take a look at resolveState function at the same file. 接下来,让我们看一下同一文件中的resolveState函数。 We will find this line there: 我们会在那里找到这条线:

dst.resolve = $resolve.resolve(state.resolve, locals, dst.resolve, state);
var promises = [dst.resolve.then(function (globals) {
    dst.globals = globals;
})];

This is specifically where promises for resolve parameters are retrieved. 这特别是在检索解析参数的promise时。 What's good for use, the function that does this is taken out to a separate service. 什么是好用的,这样做的功能被带到一个单独的服务。 This means we can hook and alter it's behavior with decorator. 这意味着我们可以使用装饰器来挂钩和改变它的行为。

For reference the implementation of $resolve is in angular-ui-router/src/resolve.js file 作为参考,$ resolve的实现在angular-ui-router / src / resolve.js文件中

Implementing the hook 实现钩子

The signature for resolve function of $resolve is $ resolve的解析函数的签名是

this.resolve = function (invocables, locals, parent, self) {

Where "invocables" is the object from our declaration of state. “invocables”是我们声明国家的对象。 So we need to check if "invocables" is string. 所以我们需要检查“invocables”是否为字符串。 And if it is we will get a controller function by string and invoke function after "." 如果是,我们将通过字符串获取控制器函数并在“。”之后调用函数。 character 字符

//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;
}]);

EDIT: 编辑:

You can also override $controllerProvider with provider like this: 您还可以使用以下提供程序覆盖$ controllerProvider:

app.provider("$controller", function () {

}

This way it becomes possible to add a new function getConstructor, that will return controller constructor by name. 这样就可以添加一个新函数getConstructor,它将按名称返回控制器构造函数。 And so you will avoid using $window object in the hook: 所以你将避免在钩子中使用$ 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]);
       }
    }; 

Full code demonstrating this method http://plnkr.co/edit/f3dCSLn14pkul7BzrMvH?p=preview 演示此方法的完整代码http://plnkr.co/edit/f3dCSLn14pkul7BzrMvH?p=preview

You need to make sure the controller is within the same closure as the state config. 您需要确保控制器与状态配置位于同一个闭包中。 This doesn't mean they need to be defined in the same file. 这并不意味着它们需要在同一个文件中定义。

So instead of a string, use a the static property of the controller: 因此,使用控制器的静态属性而不是字符串:

resolve: MyCtrl.resolve,

Update 更新

Then for your Controller file: 然后为您的Controller文件:

var MyCtrl;
(function(MyCtrl, yourModule) {

    MyCtrl = function() { // your contructor function}
    MyCtrl.resolve = { // your resolve object }

    yourModule.controller('MyCtrl', MyCtrl);

})(MyCtrl, yourModule)

And then when you define your states in another file, that is included or concatenated or required after the controller file: 然后,当您在另一个文件中定义状态时,在控制器文件之后包含或连接或需要:

(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);

For production code you will still want to wrap all these IIFEs within another IIFEs. 对于生产代码,您仍然希望将所有这些IIFE包装在另一个IIFE中。 Gulp or Grunt can do this for you. Gulp或Grunt可以为您做到这一点。

If the intention is to have the resolver in the same file as the controller, the simplest way to do so is to declare the resolver at the controller file as a function: 如果打算将解析器与控制器放在同一个文件中,最简单的方法是将控制器文件中的解析器声明为函数:

//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
});

This should work. 这应该工作。

//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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM