How to decorate current state resolve function in UI-Router? Current function isn't invoked

空扰寡人 提交于 2019-11-28 10:28:34

If I understand your requirement correctly, we can use native UI-Router built-in decorator:

decorator(name, func)

Allows you to extend (carefully) or override (at your own peril) the stateBuilder object used internally by $stateProvider. This can be used to add custom functionality to ui-router, for example inferring templateUrl based on the state name... (read more in the doc)

There is a working plunker

So, we can have this var auth

var auth = ['AuthService', '$stateParams',
  function(AuthService, $stateParams) {
    //how to invoke this function on needed states?
    if (AuthService.isAuthenticated()) {
      return AuthService.me();
    } else {
      return false;
    }
  }
];

And here we just use decorator with some "IF" logic

.config(['$stateProvider', 
  function($stateProvider) {

    $stateProvider.decorator('views', function(state, parent) {
      var result = {},
        views = parent(state);

      // some naive example when to not inject resolve
      if (state.name === "home") { 
        return views;
      }
      // child already has that in parent
      if (state.name.indexOf(".") > 0) { 
        return views;
      }

      angular.forEach(views, function(config, name) {

        // here inject the resolve (if not existing)
        config.resolve = config.resolve || {};
        // and extend it with the auth stuff above
        config.resolve.auth = auth;

        result[name] = config;
      });

      return result;
    });

  }
])

And later few our states, which will be extended by the above stuff

$stateProvider
    .state('home', {
      url: "/home",
      templateUrl: 'tpl.html',
    })
    .state('parent', {
      url: "/parent",
      templateUrl: 'tpl.html',
      controller: 'SharedCtrl',
    })
    .state('parent.child', {
      url: "/child",
      templateUrl: 'tpl.html',
      controller: 'SharedCtrl',
    });

Check it in action here

I realized that $delegate.current object contains only raw stateProvider config data. To wrap resolve function I add my function to $delegate.$current on each state change.

$provide.decorator('$state', function($delegate, $rootScope) {
  $rootScope.$on('$stateChangeStart', function(event, state, params) {
    if ($delegate.current === "err404" || $delegate.current === "login" || $delegate.current === "register") {
      return;
    }
    console.log("decorator", $delegate);
    $delegate.$current.resolve["auth"] = ['AuthService', '$stateParams', function(AuthService, $stateParams) {
      if (AuthService.isAuthenticated()) {
        console.log('AuthService.me()');
        return AuthService.me();
      } else {
        return false;
      }
    }]
  });
  return $delegate;
});

Update

I found related discussion on github, you can add universal resolve function into toState param:

app.run(['$rootScope', '$state',
  function($rootScope, $state) {

    $rootScope.$on('$stateChangeStart', function(event, toState) {

      if (toState.name === "login" || toState.name === "register") {
        return;
      }

      toState["resolve"]["auth"] = ['AuthService', '$stateParams', function(AuthService, $stateParams) {
        if (AuthService.isAuthenticated()) {
          return AuthService.me();
        } else {
          return false;
        }
      }];
    });
  }
]);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!