ui-routerを読み直し

(1年前にまとめたもの。こっちに転載。最新のUI-Routerについては Angular UI-Router v1さわる - ryotah’s blog から)

動機

  • ui-router のこと、実はあまり把握していなかった
  • Nested States & Views とか Multiple & Named Views とか一応使っているけどあんまりよくわかっていない
  • ひとまず angular-ui/ui-router Wiki に目を通すか ← イマココ

書いてあること

仕事で利用したTips

遷移時にデータを渡したい

// foo.ts
$stateProvider
.state('foo', {
  url: '/foo',
  template: '<foo></foo>',
  params: {
    // non-url parameter
    data: null,
  }
})

// bar.controller.ts
go() {
  this.$state.go('foo', {
    data: {
      // ...
    },
  });
}

動的にtemplateUrlを変更したい

$templateProvider を使う

$stateProvider
.state('foo', {
  url: 'foo',
  controller: 'FooController',
  /*@ngInject*/
  templateProvider: function($templateFactory, BarService) { 
    return $templateFactory.fromConfig({
      templateUrl: BarService.getBool() ? 'a.html' : 'b.html'
    });
  }
});

履歴、location bar のコントロール

location - {boolean=true|string=} - If true will update the url in the location bar, if false will not. If string, must be “replace”, which will update url and also replace last history record.

遷移先を途中で変更する

$stateProvider.state の resolve 内で遷移先を変更する

resolve: {
  /*@ngInject*/
  data: function($stateParams, $state, $q, $timeout) {
    const data = $stateParams.data;
    if (data) {
      return "something";
    }
    // go to xxx if no data
    $timeout(() => {
      $state.go("xxx");
    }, 0);
    return $q.defer('error message');
  }
}

副作用なく、location barのクエリだけを変更したい

  • $state.go('.', { query: "query" }, { notify: false });
    • $stateChangeStart と $stateChangeSuccess events を broadcast しない

ui-sref, ui-sref-active

  • ui-sref  - You just need to be aware that the path is relative to the state that the link lives in
  • ui-sref-active
    • A directive working alongside ui-sref to add classes to an element when the related ui-sref directive’s state is active

State Manager

Nested States and Nested Views

Nested Statesの設定

Dot Notationによる設定

$stateProvider
  .state('contacts', {})
  .state('contacts.list', {});
<a ui-sref="contacts">Go Contacts</a>
<a ui-sref="contacts.list">Go Contacts List</a>

Parent Propertyによる設定

$stateProvider
  .state('contacts', {})
  .state('list', {
    parent: 'contacts'
  });
<a ui-sref="contacts">Go Contacts</a>
<!-- "contacts.list" にはならない -->
<a ui-sref="list">Go Contacts List</a>

stateに設定している名称は違うが、両方とも contacts(親)、list(子)の関係になっている。 このへんちょっとややこしい。

親Statesから引き継げるもの

Abstract States

子Stateを持つことができるが遷移できないState。

利用例として考えられるもの:

  • urlを子Stateに追加したい場合
  • ui-viewを含むtemplateを子Stateで利用したい場合
  • Resolved dependencies via resolve を子Stateで利用したい場合
  • Custom data properties via data を子Stateで利用したい場合
  • To run an onEnter or onExit function that may modify the application in someway.

Multiple Named Views

Absolute Names

viewname@statename の形式を利用すると ui-view を絶対名で指定できる。 例えば、Nested States だけど Views を入れ子にしたくない場合、以下のような書き方が可能。

<div ng-app="ngapp">
  <div ui-view></div>
  <div ui-view="list"></div>
</div>
$stateProvider
.state('state1', {
  url: '/state1',
  template: 'state1'
})
.state('state1.list', {
  url: '/list',
  views: {
    // このケースのように State がルートの場合、@の後ろには何もかかない
    'list@': {
      template: 'state2'
    }
  }
});

URL Routing

  • Basic Parameters
    • /user/:id, /user/{id}, /user/{id:int}
  • Query Parameters
    • /contacts?myParam1&myParam2
  • Absolute Routes
    • url: '^/list' のようにすると、Nested State でも Url は結合されない
  • $stateParams は親Stateのパラメーターなどは取得できない(自分自身のパラメーターのみ取得可能)
    • 親Stateで resolve すれば取得可能
$stateProvider.state('contacts.detail', {
   url: '/contacts/:contactId',   
   controller: function($stateParams){
      $stateParams.contactId  //*** Exists! ***//
   },
   resolve:{
      contactId: ['$stateParams', function($stateParams){
          return $stateParams.contactId;
      }]
   }
}).state('contacts.detail.subitem', {
   url: '/item/:itemId', 
   controller: function($stateParams, contactId){
      contactId //*** Exists! ***//
      $stateParams.itemId //*** Exists! ***//  
   }
})