のらねこの気まま暮らし

技術系だけど、Qiita向きではないポエムとかを書くめったに更新されないヤツ

AngularJS x UI-RouterでpushStateを使う

何も考えずにAngularJSを使うと、遷移のURLはハッシュフラグメント(#)を使うことになる。 しかし、ngRouterの$locationProviderにはhtml5Modeという機能があり、こいつを有効にすると、pushStateを使ってURLを構築することができる。

それをUI-Routerで使うよという話。

UI-Routerも$locationProviderをそのまま利用可能

ほんとうにまんま一緒に有効化できる。

ngRouterの場合:

angular.module("myApp").config(["$locationProvider", function ($locationProvider) {
  $locationProvider.html5Mode(true);
}]);

UI-Routerの場合:

angular.module("myApp").config(["$locationProvider", function ($locationProvider) {
  $locationProvider.htmlMode(true);
}]);

ui-srefの扱い

UI-Routerを使っていて感動したのは、ui-srefの扱い。 ui-srefはstateを指定し、そのstateで定義したURLをhrefの属性に変換してくれるDirectiveですが、html5Modeを有効にすると、ハッシュ(#)を取り除いてくれる。

つまり、URLの差し替えとかせずにhtml5Modeに移行できる。 なので、UI-Routerを使っているなら、hrefよりもui-srefを使ったほうが可用性が高いです。

grunt-contrib-connectと併せて使いたい

変更を最小限にhtml5Modeを利用できるのは素晴らしいが、pushStateを使う以上は当然、サーバ側にも手を入れなければならない。

基本的には/のアクセスは/index.htmlにリライトされているだけなので、/hogeとかのURLを(内部の遷移ではなく)叩いてしまうと、当然Not Foundになる。

わざわざテンプレートをRouterにまかせて管理しているのに、同じようなHTMLを大量に生産するのも無駄過ぎるし、一般的なプロラマなら違う方法があると考える。

connect-rewrite

grunt-pluginではないが、URLに対して特定コンテンツを返す様に設定できるnode-connectのプラグインがある。これをgrunt-contrib-connectのmiddlewareとして利用してやればいい。

/api, /css, /js, /tmpl, /fonts, /imgのHTTP-PATHを除くリクエストをすべて/index.htmlへリライトしたい場合は以下の様に書く。

connect: {
  server: {
    options: {
      host: "localhost",
      port: 8000,
      base: "www",
      middleware: function (connect, options) {
        if (!Array.isArray(options.base)) options.base =[options.base];
        directory = options.directory || options.base[options.base.length - 1];

        // node-moduleを読み込み
        modRewrite = require("connect-modrewrite");

        middlewares = [];
        middlewares.push(connect.directory(directory));
        // modRewriteの設定
        middlewares.push(modRewrite([["!/api|/css|/js|/tmpl|/fonts|/img /index.html"]);
      }
    }
  }
}

備忘録

pushState対応してて気づいた余談。

RouterのtemplateUrlは絶対パスで記述しよう

相対パスで指定してて、/hogeなURLでアクセスした時にテンプレートが404になって困った。 templateUrlとしてはhttp://localhost:8000/tmpl/fuga.htmlを期待してても、相対パスで書かれていると、/hogeから/fugaに遷移しようとしたときに読みに行くURLはhttp://localhost:8000/hoge/tmpl/fuga.htmlになる。

WEB屋としてはあまりに当たり前なコトなのだけど、サンプルで書かれてた相対パスをそのまま使っていた。まあ、ハッシュフラグメントでやっている限りは困らないけど。

URLの指定にHashfragment(#)ではなくHashbang(#!)を使いたい

$locationProvider.hashPrefix('!');ってすれば、#!になる。