Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent hitting child states ui-router

Here is what i have so far with ui-router states:

$stateProvider
  .state('tools', {
    url: '/tools/:tool',
    template: '<div ui-view=""></div>',
    abstract: true,
    onEnter: function ($stateParams, $state, TOOL_TYPES) {
      if (TOOL_TYPES.indexOf($stateParams.tool) === -1) {
        $state.go('error');
      }
    }
  })
  .state('tools.list', {
    url: '',
    templateUrl: 'app/tools/tools.tpl.html',
    controller: 'ToolsController'
  })
  .state('tools.view', {
    url: '/:id/view',
    templateUrl: 'app/tools/partials/tool.tpl.html',
    controller: 'ToolController'
  });

As you can see parent state has parameter tool which can be only in TOOL_TYPES array. So in case when tool is not available, i want to redirect to the error page.

Actually, everything works as expected, but i get two errors:

TypeError: Cannot read property '@' of null

TypeError: Cannot read property '@tools' of null

So i guess, child states have been 'hit' anyway. Is it possible to prevent this? Or maybe there is some other way to achieve what i want?

like image 489
UngaBunga Avatar asked Feb 05 '15 15:02

UngaBunga


1 Answers

Angular ui-router's documentation mentions that onEnter callbacks gets called when a state becomes active, hence, the child states were activated.

To solve this problem you need to implement two things:

  1. Create a resolve that returns a rejected promise once a specific condition does not apply to that state. Make sure that the rejected promise is passed with information regarding the state to redirect to.

  2. Create a $stateChangeError event handler in the $rootScope and use the 6th parameter which is the representation of the information you have passed in the rejected promise. Use the information to create your redirect implementation.

DEMO

Javascript

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

  .value('TOOL_TYPES', [
    'tool1', 'tool2', 'tool3'
  ])

  .config(function($stateProvider, $urlRouterProvider) {

    $stateProvider

      .state('error', {
        url: '/error',
        template: 'Error!'
      })

      .state('tools', {
        url: '/tools/:tool',
        abstract: true,
        template: '<ui-view></ui-view>',
        resolve: {
          tool_type: function($state, $q, $stateParams, TOOL_TYPES) {

            var index = TOOL_TYPES.indexOf($stateParams.tool);

            if(index === -1) {
              return $q.reject({
                state: 'error'
              });
            }

            return TOOL_TYPES[index];
          }
        }
      })

      .state('tools.list', {
        url: '',
        template: 'List of Tools',
        controller: 'ToolsController'
      })
      .state('tools.view', {
        url: '/:id/view',
        template: 'Tool View',
        controller: 'ToolController'
      });

  })

  .run(function($rootScope, $state) {
    $rootScope.$on('$stateChangeError', function(
      event, toState, toStateParams, 
      fromState, fromStateParams, error) {

        if(error && error.state) {
          $state.go(error.state, error.params, error.options);
        }

    });
  })

  .controller('ToolsController', function() {})
  .controller('ToolController', function() {});
like image 79
ryeballar Avatar answered Oct 20 '22 00:10

ryeballar