繁体   English   中英

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

[英]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.

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