Angularjs UI Routerの使い方

  • 113
    いいね
  • 0
    コメント

はじめに

UI Routerは、すぐに使い方を忘れてしまう為備忘録でまとめます。

UI Routerとは

UI RouterはngRouteの高機能版のモジュールです。ルーティングの設定に使います。

準備

◎bower

$ bower install angular-ui-router

◎CDN
https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.js
https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.min.js

使い方

◎まずモジュールを追加します。

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

◎画面表示用のディレクティブui-viewをhtmlに追加

<body>
    <div ui-view></div>
</body>

◎$stateProviderサービスに、ルーティングの設定を追加していきます

angular.module('myApp')
.config(['$stateProvider',function($stateProvider){
}])

◎ページを切り替える

最もシンプルな状態だと下記になる。
(サンプルではng-templeteを使用しているが、htmlファイルを直接読み込む形で問題ない)

html側から呼び出す場合は、ui-srefに指定する値が、stateの第一引数に対応する。
■pageAボタンを押した場合

  • state・・・page-A
  • url・・・/pageA

■pageBボタンを押した場合

  • state・・・page-B
  • url・・・/pageB
<body>
  <div ui-view></div>
  <button class="mr10 btn btn-default" ui-sref="page-A">pageA</button>
  <button class="mr10 btn btn-default" ui-sref="page-B">pageB</button>
<script id="pageA.html" type="text/ng-template">
  <div>pageAだよ</div>
</script>
<script id="pageB.html" type="text/ng-template">
  <div>pageBだよ</div>
</script>
</body>
angular.module('myApp', ['ui.router'])
  .config(['$stateProvider', function($stateProvider) {
    $stateProvider
      .state('page-A', {
        url: '/pageA',
        templateUrl: 'pageA.html'
      })
      .state('page-B', {
        url: '/pageB',
        templateUrl: 'pageB.html'
      })
  }]);

サンプル

◎js側からページを切り替える

htmlからページを切り替える場合は、ui-srefを使用しますが、
js側からページを切り替える場合は$state.goを使用します。

$state.go('遷移先のページのstate')
angular.module('myApp', ['ui.router'])
  .config(['$stateProvider', function($stateProvider) {
    $stateProvider
      .state('page-A', {
        url: '/pageA',
        templateUrl: 'pageA.html'
      })
      .state('page-B', {
        url: '/pageB',
        templateUrl: 'pageB.html'
      })
  }])
  .controller('AppCtrl', function($scope, $state) {
    $scope.changePage = function(page) {
      $state.go(page);
    }
  });
  <button class="mr10 btn btn-default" ng-click="changePage('page-A')">pageA</button>
  <button class="mr10 btn btn-default" ng-click="changePage('page-B')">pageB</button>

サンプル

◎ページを入れ子にする

ページを入れ子にする(ui-viewを入れ子にする)場合は、stateの第一引数をドットで区切ります。

<button class="mr10 btn btn-default" ui-sref="page-A">pageA</button>
<button class="mr10 btn btn-default" ui-sref="page-A.page-B">pageB</button>
angular.module('myApp', ['ui.router'])
  .config(['$stateProvider', function($stateProvider) {
    $stateProvider
      .state('page-A', {
        url: '/pageA',
        templateUrl: 'pageA.html'
      })
      .state('page-A.page-B', {
        url: '/pageB',
        templateUrl: 'pageB.html'
      })
  }])

サンプル

ドットの代わりににparentを使用して入れ子構造を制御できる。
ドットとparentの両方使うことも可能です。

<body>
  <div ui-view></div>
  <button class="mr10 btn btn-default" ui-sref="page-A">pageA</button>
  <button class="mr10 btn btn-default" ui-sref="page-B">pageB</button>
  <script id="pageA.html" type="text/ng-template">
    <p>pageA</p>
    <div ui-view></div>
  </script>
  <script id="pageB.html" type="text/ng-template">
    <p>pageB</p>
  </script>
</body>
angular.module('myApp', ['ui.router'])
  .config(['$stateProvider', function($stateProvider) {
    $stateProvider
      .state('page-A', {
        url: '/pageA',
        templateUrl: 'pageA.html'
      })
      .state('page-B', {
        url: '/pageB',
        parent: 'page-A',
        templateUrl: 'pageB.html'
      })
  }])

サンプル

◎ui-sref-activeでボタンの表示を切り替える

ui-srefと併用してui-sref-activeを定義することで、ページ切り時にボタンにclassを付与することができる。
下記の例の場合、page-Bに遷移する際に、buttonにactiveクラスを追加する。

  <button class="mr10 btn btn-default" ui-sref="page-B" ui-sref-active="active">pageB</button>

サンプル

◎表示するui-viewを指定する

表示するui-viewに名前をつけることで、表示先のviewを指定することができる。
view名の付け方は、下記を参考にしてください。

view名をつけない場合は、viewsのプロパティをブランクにする。

  <div ui-view></div>
  <div ui-view="viewB"></div>
angular.module('myApp', ['ui.router'])
  .config(['$stateProvider', function($stateProvider) {
    $stateProvider
      .state('page-A', {
        url: '/pageA',
        views: {
          "": {
            templateUrl: 'pageA.html'
          }
        }
      })
      .state('page-B', {
        url: '/pageB',
        views: {
          "": {
            templateUrl: 'pageB.html'
          },
          "viewB": {
            templateUrl: 'pageB2.html'
          }
        }
      })
  }])

◎表示するstateを指定する。

@stateの形でviewsのプロパティを指定すると、どのstateのui-viewに表示するかを指定できる。
絶対パスのようなイメージ。

下記の例ではpageA〜pageCまでがそれぞれ、ui-viewを内包している。(わかりやすいように、view名はブランクにしている)

pageDボタンを押した場合、view名の指定が "@page-A.page-B"となっている為、
pageB配下のui-view内にpageDが表示される。

  <div ui-view></div>
  <button class="mr10 btn btn-default" ui-sref="page-A">pageA</button>
  <button class="mr10 btn btn-default" ui-sref="page-A.page-B">pageB</button>
  <button class="mr10 btn btn-default" ui-sref="page-A.page-B.page-C">pageC</button>
  <button class="mr10 btn btn-default" ui-sref="page-A.page-B.page-C.page-D">pageD</button>
  <button class="mr10 btn btn-default" ui-sref="page-A.page-B.page-C.page-Root">pageD Root</button>
  <script id="pageA.html" type="text/ng-template">
    <p>pageA</p>
    <div ui-view></div>
  </script>
  <script id="pageB.html" type="text/ng-template">
    <p>pageB</p>
    <div ui-view></div>
  </script>
  <script id="pageC.html" type="text/ng-template">
    <p>pageC</p>
    <div ui-view></div>
  </script>
  <script id="pageD.html" type="text/ng-template">
    <p>pageD</p>
  </script>

angular.module('myApp', ['ui.router'])
  .config(['$stateProvider', function($stateProvider) {
    $stateProvider
      .state('page-A', {
        url: '/pageA',
        views: {
          "": {
            templateUrl: 'pageA.html'
          }
        }
      })
      .state('page-A.page-B', {
        url: '/pageB',
        views: {
          "": {
            templateUrl: 'pageB.html'
          }
        }
      })
      .state('page-A.page-B.page-C', {
        url: '/pageC',
        views: {
          "": {
            templateUrl: 'pageC.html'
          }
        }
      })
      .state('page-A.page-B.page-C.page-D', {
        url: '/pageD',
        views: {
          "@page-A.page-B": {
            templateUrl: 'pageD.html'
          }
        }
      })
      .state('page-A.page-B.page-C.page-Root', {
        url: '/pageRoot',
        views: {
          "@": {
            templateUrl: 'pageD.html'
          }
        }
      })
  }])

下記のように、view名に@だけ指定した場合は、1番上の階層ui-viweを置き換えます。

views: {
    "@": {
    templateUrl: 'pageD.html'
}

ui-view名@stateでviewsのプロパティを指定することもできます。

views: {
   "viewB-2@page-A.page-B": {
       templateUrl: 'pageD.html'
   }
}

サンプル

◎ページ切替時にパラメーターを渡す

切り替え先のページにパラメーターを渡したい場合は下記の方法で渡します。

ui-srefの場合

ui-sref="page-A({param:'パラメーターA'})"

$state.goの場合

      $state.go('page-B', {
        param: 'パラメーターB'
      });

◎ページ切替時にパラメーターを受け取る

切り替え先のページでパラメーターを受け取るには下記の方法があります。
どちらも$stateParamsサービスを利用して受け取ります。

1.urlパラメーターとして受け取る

ルーティングのurlを下記の用に設定することで、urlパラメーターとして受け取ることができます。

url:'切り替え先のページ:パラメーター'
    $stateProvider
      .state('page-A', {
        url: '/pageA:param',
        templateUrl: 'pageA.html',
        controller: 'pageACtrl',
      });

    angular.module('myApp')
      .controller('pageACtrl', function($scope, $stateParams) {
        $scope.param = $stateParams.param;
      });

サンプル

2.urlパラメーターを使用しないで受け取る

下記の記事にもあるが、遷移先のcontrollerでパラメーターを受け取る為には、
paramsプロパティを追加する必要があるようです。

$state.goでのパラメータの引き渡し方

    $stateProvider
      .state('page-A', {
        url: '/pageA',
        templateUrl: 'pageA.html',
        controller: 'pageACtrl',
        params: {
          param: null
        }
      });

  angular.module('myApp')
      .controller('pageACtrl', function($scope, $stateParams) {
        $scope.param = $stateParams.param;
      });

サンプル

◎controllerを指定する

UI Routerは、ルーティングにcontrollerを指定することができます。
stateに直接controllerを記述することもできますし、controller名を指定することもできます。

また、controllerをルーティングで指定した場合、html側へのcontrollerの指定は不要です。
ルーティングと、html側両方にcontrolleを指定すると、2回controlleが呼ばれてしまいます。

下記の例の場合は、PageCCtrlが2回呼ばれます。

<body>
  <div ui-view></div>
  <button class="mr10 btn btn-default" ui-sref="page-A">pageA</button>
  <button class="mr10 btn btn-default" ui-sref="page-B">pageB</button>
  <button class="mr10 btn btn-default" ui-sref="page-C">pageC</button>
  <script id="pageA.html" type="text/ng-template">
    <p>{{msg}}</p>
  </script>
  <script id="pageB.html" type="text/ng-template">
    <p>{{msg}}</p>
  </script>
  <script id="pageC.html" type="text/ng-template">
    <p ng-controller="PageCCtrl">{{msg}}</p>
  </script>
</body>

angular.module('myApp', ['ui.router'])
  .config(['$stateProvider', function($stateProvider) {
    $stateProvider
      .state('page-A', {
        url: '/pageA',
        templateUrl: 'pageA.html',
        controller: function($scope) {
          $scope.msg = 'PageAのコントローラー';
        }
      })
      .state('page-B', {
        url: '/pageB',
        templateUrl: 'pageB.html',
        controller: 'PageBCtrl'
      })
      .state('page-C', {
        url: '/pageC',
        templateUrl: 'pageC.html',
        controller: 'PageCCtrl'
      })
  }])
  .controller('PageBCtrl', function($scope) {
    $scope.msg = 'PageBのコントローラー';
  })
  .controller('PageCCtrl', function($scope) {
    $scope.msg = 'PageCのコントローラー';
  });

サンプル

◎resolveをつかって通信後にページを表示する

通信後でデータを取得して、画面をつくる場合などに、
通信処理が完了後ページを表示したい場合がある。

そのようなケースでresolveを使用すると便利。

angular.module('myApp', ['ui.router'])
  .config(['$stateProvider', function($stateProvider) {
    $stateProvider
      .state('page-A', {
        url: '/pageA',
        templateUrl: 'pageA.html',
        controller: function($scope, myData) {
          $scope.mydata = myData.data;
        },
        resolve: {
          myData: function($http) {
            return $http.get('http://private-f7144-nogpi.apiary-mock.com/users');
          }
        }
      });
  }])

resolveで定義したサービスを、controllerに追加して使用する。

サンプル

詳しくは下記の記事が参考になる。

ui-routerにおけるresolveの威力 #AngularJS

得にここが重要。

resolveに渡す関数はPromiseを返却できるという点.
実は, resolveは 関数の戻り値に併せて, 自動的に下記を判別した上で挙動を切り替えるように実装されている.
関数が通常の値をreturnする場合: そのままControllerの引数とする
関数がPromiseを返却する場合: 非同期の完了を待ってから, then()の引数をControllerの引数とする

親のresolveで定義したプロパティを子stateでも使用できます。

angular.module('myApp', ['ui.router'])
  .config(['$stateProvider', function($stateProvider) {
    $stateProvider
      .state('page-A', {
        url: '/pageA',
        templateUrl: 'pageA.html',
        resolve: {
          hogeData: function() {
            return '子供でインジェクション';
          }
        }
      })
      .state('page-A.page-B', {
        url: '/pageB',
        templateUrl: 'pageB.html',
        controller: function($scope, hogeData) { //親stateで定義したhogeDataを子供で使用する
          $scope.hoge = hogeData;
        }
      })
  }])

サンプル

◎abstractプロパティでrootにアクセスさせない

rootのページなどで、stateは存在するが、ページとして表示しないものには、
abstractプロパティtrueを設定する。

 $stateProvider
  .state('/', {
    url: '',
    abstract: true,
    templateUrl: 'views/index.html'
 })

◎$stateChangeStartイベントで、ページ切り替え時の情報を取得する

ui-routerには、$stateChangeStartというイベントが用意されている。
イベント発火のタイミングは、stateが変わる直前です。

ログインチェックなどで使うことが多いようです。

取得できる値は下記になります。
toState・・・遷移後のstate
fromState・・・遷移元のstate
toParams・・・遷移後のパラメーター
fromParams・・・遷移元のパラメーター

angular.module('myApp', ['ui.router'])
.run(['$rootScope',function($rootScope){
  $rootScope.$on('$stateChangeStart',function(e, toState, toParams, fromState, fromParams){
    console.log(e, toState, toParams, fromState, fromParams)
  });

toStateの値をチェックして、遷移後のstateに必要な情報(ログイン情報)などが無かったら、
別のページに遷移させるなどの処理が可能です。

下記の例では、toState.isLoginプロパティがfalseの場合は、pageCに遷移させます。
その際に e.preventDefault()でイベント中止しないと、正しく動作しません。

angular.module('myApp', ['ui.router'])
  .run(['$rootScope', '$state', function($rootScope, $state) {
    $rootScope.$on('$stateChangeStart', function(e, toState, toParams, fromState, fromParams) {
      if (!toState.isLogin) {
        $state.go('page-A.page-C');
        e.preventDefault();
      }
    });
  }])

サンプル