Jon Koops Jon Koops - 5 months ago 53
AngularJS Question

Getting the name of a state in its `onEnter` hook

I'm building an application where I want to toggle a property in a service the moment a user enters and leaves a route. To do this I need to know about the state's name in the

onEnter
and
onExit
hooks. This is relatively easy for the
onExit
hook since I can just inject the
$state
service and read the name of the current state. But since the current state has not been set yet when the
onEnter
hook is called there is no way of knowing what the state we're transitioning to.

I still need to to have fine control over other parts of the state so I'd rather not have any for loops. I'm looking for a way to be able to pass the
onEnter
function to the state, whilst still retrieving the state's name inside of the function itself.

Here is the code I've written:

function onEnter($state, Steps) {
var stateName = $state.current.name; // Not possible. The current state has not been set yet!
var step = Steps.getByStateName(stateName);

step.activate();
}

function onExit($state, Steps) {
var stateName = $state.current.name; // No problem. We know about the state.
var step = Steps.getByStateName(stateName);

step.deactivate();
}

$stateProvider
.state('step1', {
url: '/step1',
templateUrl: 'templates/step1.html',
controller: 'StepOneController',
onEnter: onEnter,
onExit: onExit
});


My solution I'm using for now is to use a factory to create context for the
onEnter
function passed to the state. This is far from ideal because I still need to pass the state's name to it.

Here is an example of said workaround:

function onEnterFactory(stateName) {
return function onEnter(Steps) {
var step = Steps.getByStateName(stateName);

step.activate();
}
}

$stateProvider
.state('step1', {
url: '/step1',
templateUrl: 'templates/step1.html',
controller: 'StepOneController',
onEnter: onEnterFactory('step1')
});

Answer

Just use this in onEnter onExit hooks. onEnter is invoked by following command

$injector.invoke(entering.self.onEnter, entering.self, entering.locals.globals);

Where second parameters in this for function, so your code should looks in following way:

function onEnter(Steps) {
  var stateName = this.name; 
  var step = Steps.getByStateName(stateName);

  step.activate();
}

function onExit(Steps) {
  var stateName = this.name;
  var step = Steps.getByStateName(stateName);

  step.deactivate();
}

$stateProvider
  .state('step1', {
    url: '/step1',
    templateUrl: 'templates/step1.html',
    controller: 'StepOneController',
    onEnter: onEnter,
    onExit: onExit
  });

There is a link to fiddle where you can check example