Angular1 Ui Route

angular1

// Angular 1 - UI Router:

Angular 1's built-in routing solution (ng-route) has been superceded by 
ui-router.  To use UI-Router, we need to update our app/src/app.ts to inject 
the new module into our main module:

angular.module('ngcourse', [
  'ngcourse.tasks',
  'ngcourse.server',
  'ngcourse.router'
]);

To create our own "router" module which serves as a wrapper around ui-router, 
creates app/src/services/router/router-service.js:

export class RouterConfig {
  static $inject = ['$stateProvider', '$urlRouterProvider', '$locationProvider'];
  constructor(
    private $stateProvider,
    private $urlRouterProvider,
    private $locationProvider
  ) {
    $urlRouterProvider.otherwise('/tasks');
    $locationProvider.htmlMode(false);

    $stateProvider
      .state('tasks', {
        url: '/tasks',
        views: {
          '': {
            template: 'my tasks'
          }
        }
      });
  }
}

Let's configure the router within our app.ts file:

angular.module('ngcourse.router', ['ui.router'])
  .config(RouterConfig);

We will also need to add a directive for ui-router in index.html:

<div ui-view></div>

Our index.html main section should now look like:

<ngc-main>
  <div ui-view></div>
</ngc-main>

$stateProvider
  .state('tasks', {
    url: '/tasks',
    template: 'my tasks'
  })
  .state('tasksDetail', {
    url: '/tasks/details',
    template: 'task details'
  })
  .state('account', {
    url: '/my-account',
    template: 'my account'
  })
  // States with parameters
  .state('tasksDetailById', {
    url: '/tasks/{_id}',
    template: 'task details with id'
  })
  // This can include regular expression
  .state('tasksDetailsByRegex', {
    url: '/tasks/{_id:[A-Za-z0-9-_]{0,}}'
    template: 'tasks details with regex'
  });

The rule of thumb when using routing is that routes should be defined for 
top-level components.  Generally, micro-components should not be used in 
routing but instead use within the templates of macro components who pass 
data into them.

There are two ways that we can add a component as a routing state.

The inline template way:

.state('task', {
  url: '/tasks',
  views: {
    '': {
      template: '<ngc-tasks></ngc-tasks>'
    }
  }
});

The 'controller' way:

import {TaskListComponent} from 'components/task-list/task-list-component';

.state('tasks', {
  url: '/tasks',
  views: {
    '': {
      controller: TaskListComponent,
      controllerAs: 'ctrl',
      template: TaskListComponent.template
    }
  }
});

Both approaches are equivalent, but with the former approach, there is no need 
to define this component on a module using .directive().

Resolves are parameters to our states.  Resolves are not used often, and we will 
remove this when we learned the functionality.

.state('account', {
  url: '/my-account',
  template: 'My Account',
  resolve: {
    greeting: function($timeout) {
      return $timeout(function(){
        return 'Hello';
      }, 3000);
    }
  }
});

The "resolve" property in a state configuration allows us to specify a set of 
dependencies that will need to be resolved prior to transitioning to the new 
state.  Those dependencies become injectable in the route's controller.  In the 
above example, greeting property of the resolve is set to a function that 
returns a promise that resolves to 'Hello' after 3 seconds.  The UI-Router will 
wait until the promise resolves, then make the transition.  The state's 
controller will be able to dependency-inject 'greeting', which will be set to 
'Hello' by the time the controller is initiated.

One of the most powerful features of ui-router versus ng-route is nested views.  
To allow ui-router to know what view it is updating, we can add a name to the 
view as seen in ui-view="child@parent":

.state('parent', {
  url: '/parent',
  views: {
    'parent': {
      template: 'parent view <div ui-view="child@parent"></div>'
    }
  }
})
.state('parent-child', {
  url: '/child1'
  views: {
    'child@parent': {
      template: 'child 1'
    }
  }
})
.state('parent.child2', {
  url: '/child2',
  views: {
    'child@parent': {
      template: 'child 2'
    }
  }
});

Nesting views allows sophisticated routing where parts of the view are defined 
by the parent state and parts are defined (or overridden) by child states.  Note 
that states get nested implicitly, based on names.  For example 'parent.child1' 
will be a child of 'parent'.  UI-Router also provides a facility for nesting 
states explicitly.  Child state's URL is understood to be relative to parents.  
Because 'parent.child1' is a child of 'parent', and parent's URL is '/parent', 
the URL for child1 is '/parent/child1'.

In the example above, the parent view provides part of the view (the text 
"parent view") and a placeholder where child states would go.  When we visit 
child1 and child2, the parent's part of the view remains unchanged, while the 
child's section changes.

Alternatively, the child can override the parent's part of the view:

.state('parent.child2.grandchild', {
  url: '/grandchild',
  views: {
    'child@parent': {
      template: 'parent overridden'
    }
  }
});

In this case, the 'grandchild' overrides the view earlier defined by child2.

When oeverriding parent's views, we need to refer to them using the ..@.. which 
allows us to specify an absolute path to the view.

We can easily transition between states using ui-sref directive:

<button ui-sref="tasks">Go to task</button>

We can also transition using $state.go():

$state.go('tasks-details', {_id: taskId});

However, let's wrap this in a service.  We can use the same router-service.ts 
file:

export class RouterService {
  static $inject = ['$state'];
  constructor(private $state) { }

  goToTask(taskId) {
    this.$state.go('tasks.details', {
      _id: taskId
    });
  }

  goToTaskList() {
    this.$state.go('tasks', {}, {
      reload: true
    });
  }
};

The $stateParams can be injected into our components using the $inject and used:

$stateParams._id

But, let's wrap it:

getTaskId() {
  return this.$stateParams._id;
};

If we want to change the value of the parameters without triggering a state 
transition, we need to update the values in the 3 different places where the 
UI route keeps them:

function quietlyUpdateParams(key, value) {
  $state.params[key] = value;
  $stateParams[key] = value;
  $state.$current.params[key] = value;
}

An example of this is the Google Maps, where the URL is continously updated as 
the user moves around the map.
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License