Client-Side Routing

Navigate between named states, read and modify the URL, manage history, and intercept route transitions.

AngularTS has two routing layers. $location manages the browser URL and history directly. $state works at the application level with named states, parameters, resolves, and transition hooks.

Exact routing API signatures live in TypeDoc:

Work With The URL

Use $location when code needs to inspect or change the raw URL.

$location.getPath(); // "/dashboard"
$location.getSearch(); // { tab: "overview" }
$location.getHash(); // "summary"
$location.getUrl(); // "/dashboard?tab=overview#summary"
$location.absUrl(); // "https://app.example.com/dashboard?tab=overview#summary"

Setter methods return $location, so related URL changes can be chained.

$location
  .setPath('/settings/profile')
  .setSearch({ tab: 'security' })
  .setHash('billing-section');

$location.setSearchParam('page', 2);

Changes to $location are applied asynchronously. $locationChangeStart and $locationChangeSuccess are broadcast on $rootScope around navigation.

Configure URL Mode

Configure $location before the application runs.

angular.createModule('demo', []).config({
  $location: {
    html5Mode: {
      enabled: true,
      requireBase: false,
      rewriteLinks: true,
    },
    hashPrefix: '!',
  },
});

When requireBase is enabled, the application document must include a <base> tag.

Use $state.go() for normal application navigation. It accepts absolute state names, parent-relative names, and sibling-relative names.

$state.go('contacts.detail', { id: 42 });

$state.go('^.list');

$state.go('.detail', { id: 42 });

$state.go($state.current, $state.params, { reload: true });

go() returns a transition promise. Use transition options when you need to control reloads, parameter inheritance, URL updates, or relative navigation.

Use $state.href() when templates or controllers need a URL without starting navigation.

const relative = $state.href('contacts.detail', { id: 42 });
const absolute = $state.href('contacts.detail', { id: 42 }, { absolute: true });

Check Active States

Use matches() for ancestors or glob patterns and pass exact when only the current state should match.

$state.matches('contacts.detail', undefined, { exact: true });
$state.matches('contacts.detail', { id: 42 }, { exact: true });

$state.matches('contacts');
$state.matches('*.detail');

These helpers are useful for active navigation styling and conditional UI.

Register States At Runtime

$stateRegistry stores state definitions and can register states after bootstrap, which is useful for lazy-loaded feature modules.

const detail = $stateRegistry.getState('contacts.detail');
const allStates = $stateRegistry.getStates();

$stateRegistry.register({
  name: 'profile',
  url: '/profile',
  component: 'profilePage',
});

Handle Navigation Events

Listen on $rootScope for URL-level events when you need a broad guard.

angular.getModule('demo').run([
  '$rootScope',
  '$state',
  'authService',
  ($rootScope, $state, authService) => {
    $rootScope.on('$locationChangeStart', (event, newUrl) => {
      if (newUrl.includes('/admin') && !authService.isAuthenticated()) {
        event.preventDefault();
        $state.go('login', { returnUrl: newUrl });
      }
    });
  },
]);

For state-level lifecycle work, prefer transition hooks.

Example: Programmatic Navigation

class OrderController {
  static $inject = ['$state'];

  order!: Order;

  constructor(private $state: ng.StateService) {}

  viewOrder(id: number) {
    this.$state.go('orders.detail', { orderId: id });
  }

  backToList() {
    this.$state.go('^');
  }

  get orderLink(): string | null {
    return this.$state.href('orders.detail', { orderId: this.order.id });
  }
}