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('!');
ってすれば、#!
になる。