在`onEnter`钩子中获取状态的名称

时间:2015-06-10 10:44:45

标签: angularjs angular-ui-router

我正在构建一个应用程序,我希望在用户进入和离开路径时切换服务中的属性。为此,我需要了解onEnteronExit挂钩中的州名称。这对于onExit钩子来说相对容易,因为我可以注入$state服务并读取当前状态的名称。但是,由于当调用onEnter挂钩时尚未设置当前状态,因此无法知道我们正在转换到的状态。

我仍然需要对状态的其他部分进行精细控制,所以我宁愿没有任何for循环。我正在寻找一种能够将onEnter函数传递给状态的方法,同时仍然在函数内部检索状态的名称。

以下是我写的代码:

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

我现在使用的解决方案是使用工厂为传递给状态的onEnter函数创建上下文。这远非理想,因为我仍然需要将州名称传递给它。

以下是所述解决方法的示例:

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

6 个答案:

答案 0 :(得分:4)

在我的一个项目中,我们使用了类似的东西

app.run(function($rootScope, $state, $location) {
    $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams,
    fromState) {
    $state.previous = fromState;
  });

要记住以前的状态。但您可能还记得新状态并将信息存储在某处。

答案 1 :(得分:4)

在onEnter onExit挂钩上使用 。 onEnter由以下命令调用:

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

$injector.invoke的第二个参数是它所调用的函数 this 的值。所以你的代码应如下所示:

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

以下是在onEnteronExit挂钩中访问州名称的工作示例:



angular.module('myApp', ['ui.router'])

.config(function($stateProvider) {
  function onEnter() {
    console.log('Entering state', this.name);
  }

  function onExit() {
    console.log('Exiting state', this.name);
  }

  $stateProvider.state('state-1', {
    url: '/state-1',
    template: '<p>State 1</p>',
    onEnter: onEnter,
    onExit: onExit
  }).state('state-2', {
    url: '/state-2',
    template: '<p>State 2</p>',
    onEnter: onEnter,
    onExit: onExit
  });
});
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.6/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.3.1/angular-ui-router.js"></script>

<div ng-app="myApp">
  <nav>
    <a ui-sref="state-1">State 1</a>
    <a ui-sref="state-2">State 2</a>
  </nav>

  <div ui-view></div>
</div>
&#13;
&#13;
&#13;

答案 2 :(得分:3)

您已经知道它将处于哪种状态,因为您在.state('statename',中定义了它。要不两次写入相同的名称,可以事先定义状态变量:

var steps = ["step1", "step2", "step3"]; // or, something like Steps.getSteps()

$stateProvider
    .state(steps[0], {
        url: '/step1',
        templateUrl: 'templates/step1.html',
        controller: 'StepOneController',
        onEnter: function(Steps) {
            var stateName = steps[0];
            var step = Steps.getByStateName(stateName);

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

你甚至可以通过这种方式使它变得动态:

var steps = [
    { name: "step1", url: "/step1" },
    { name: "step2", url: "/step2" },
    { name: "step3", url: "/step3" }
]; // Or something like Steps.getSteps();

for (var i = 0; i < states.length; i++) {
    var state = steps[i];
    $stateProvider.state(state.name,
        url: state.url,
        templateUrl: "templates/" + state.name + ".html",
        onEnter: function(Steps) {
            var step = Steps.getByStateName(state.name);
            step.activate();

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

            step.deactivate();
        }
}

答案 3 :(得分:1)

您可以稍微扩展您的工厂解决方案并使其更加灵活。

可能有一个provider对状态变化作出反应。
然后你可以将这个提供者/服务注入onEnter函数或者你可能需要的地方。

此处相关的plunker http://plnkr.co/edit/6Ri2hE

angular.module('app', ['ui.router'])
  .provider('myState', function myStateProvider() {
    var state;

    this.onEnter = function() {
      console.log('provider.onEnter', state);
    };

    this.$get = function($rootScope) {
      var myState = {};

      myState.initialize = function() {
        $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
          state = toState;
        });
      };

      myState.getState = function() {
        return state;
      };

      return myState;      
    };
  }) 

  .config(function($stateProvider, myStateProvider) {
    $stateProvider
      .state('step1', {
        url: '/step1',
        template: '<div>step1 template</div>',
        controller: function() {},
        onEnter: myStateProvider.onEnter  // usage example
      })
      .state('step2', {
        url: '/step2',
        template: '<div>step2 template</div>',
        controller: function() {},
        onEnter: function(myState) {     // other usage example 
          console.log('state.onEnter', myState.getState());
        }
      });
  })

  .run(function(myState) {
    myState.initialize();
  });
<a ui-sref="step1">state:step1</a>
<a ui-sref="step2">state:step2</a>
<div ui-view></div>

如果按顺序点击链接,这将console.log()以下。

imgur

答案 4 :(得分:1)

添加一个命名状态的'name'属性:

$stateProvider
  .state('step1', {
    name: 'step1' // <- property naming the state
    url: '/step1',
    templateUrl: 'templates/step1.html',
    controller: 'StepOneController',
    onEnter: onEnter
  });

然后this.name回调中的onEnter可以访问该路线的名称:

function onEnter() {
  var stateName = this.name; // <- Retrieve the state's name from its configuration
  var step = Steps.getByStateName(stateName);
  step.activate();
}

要两次不写相同的州名,您可以先将一个状态定义在一个单独的对象中,然后在将状态添加到$stateProvider之前用状态丰富状态:

var routes = {
  "step1": {
    url: '/step1',
    templateUrl: 'templates/step1.html',
    controller: 'StepOneController',
    onEnter: onEnter
  }
};

for(var routeName in routes) {
      var route = routes[routeName];
      // Enrich route with its name before feeding 
      // it to the $stateProvider
      route.name = routeName;
      $stateProvider.state(route.name, route);
}

答案 5 :(得分:0)

也许你可以使用resolve,

$stateProvider
  .state('step1', {
   url: '/step1',
   templateUrl: 'templates/step1.html',
   controller: 'StepOneController',
   resolve: {
       onenter : function( Steps ) {
          // use this.self.name to access state name
          var step = Steps.getByStateName(this.self.name);
          step.activate();
       }
   } 
} );

如果上面的this方法看起来不干净,那么也许可以使用装饰器来填充当前状态。

angular.config(function($provide) {
  $provide.decorator('$state', function($delegate, $rootScope) {
    $rootScope.$on('$stateChangeStart', function(event, state) {
      $delegate.next = state;
    });
    return $delegate;
  });
});

状态将在$state.next内部解析功能中可用。