angular-ui / ui-router

The de-facto solution to flexible routing with nested views in AngularJS
http://ui-router.github.io/
MIT License
13.53k stars 3k forks source link

RFC: state machine guard function #618

Closed pechorin closed 9 years ago

pechorin commented 10 years ago

Hi everyone. I develop site with roles system. Sometimes user can access some states and sometimes not.

The easiest way to handle this constrains is:

// $currentUser -> service representing current user
$rootScope.$on('$stateChangeStart', function(event, toState) {
  if (toState.name == 'private_page' && $currentUser.canAccess(toState.name) == false) {
  event.preventDefault;
  alert("go away, please");
}
})

This is okay solution, but not in cases where we have many states and many different constraints. This produce many event handlers and personally i think what guard constraints is a "state machine" responsibility ;) Also i don't like idea to define "guard functions" in separate place. I think this constraints should be defined in-place like this:

$stateProvider.state('private_page', function() {
  parent: ..,
  url: ..,
  templateUrl: ...,
  controller: ...,
  guard: function ($currentUser) {
    if ($currentUser.canAccess('private_page') == false) {
      alert('go away please');
      return false
    } 
  }
})

I patch some internals and implementation looks pretty easy:

      // prevent event if guard returns false
      if (isFunction( to.self.guard )) {
        var guardResult = $injector.invoke(to.self.guard);

        if (guardResult == false) return TransitionPrevented;
      }

      // Broadcast start event and cancel the transition if requested
      var evt = $rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams);

So if community like this idea i will provide patch and tests. The main motivation for me: "State machine should handle guard conditions by self".

dmackerman commented 10 years ago

@TheSharpieOne: good call. :+1:

nateabele commented 9 years ago

This is implemented in 1.0 with $transitionProvider hooks.