AngularJSでrouteProviderで遊んでる時に、ファイルのダウンロードやら画像の直リンクやら 意図しないリンクまでルーティングに含まれてしまう問題の対処方法。

header

これで3時間位詰まった。。。~~おそらくBackboneJSでも通用する。~~しませんでした。


問題

下記のようなルーティングを設定した時、 ng-app内の同一ドメイン内のすべてのリンクに対してルーティング処理が行われ、 PJAXに似た動作をするようになる。

angular.module("app", ["ngRoute"])
.config([
  "$routeProvider",
  "$locationProvider",
  function($routeProvider, $locationProvider) {
    $locationProvider.html5Mode(true);

    return $routeProvider.when("/", {
      templateUrl: "/template/index.html"
    })
    .when("/page01", {
      templateUrl: "/template/page01.html"
    })
    .when("/page02", {
      templateUrl: "/template/page02.html"
    })
    .otherwise({
      redirectTo: "/"
    });

  }
]);

上記の指定では //page01/page02 へのアクセスを行った際に 所定の場所からテンプレートを取得し、ng-viewへ反映する。

ルーティングに該当しなかった場合は /へのリクエストとしてリダイレクトして処理する。

ここで問題となるのが、公開したアプリに下記のようなファイルをダウンロードさせるためのリンクや 画像を直リンクで表示したいといったような意図で設置したリンク。

<a href="/download/hoge.zip">ダウンロードはこちら!</a>

残念ながらこの状態でリンクを設定すると、 AngularJSはリクエストをルーティングに組み込んで処理する。

実際どのように動作するかというと、 /download/hoge.zip をクエリとしてルーティングのマッチングを行う。 当然 router.js の内容に該当するルートはないので、/と同じ内容が表示されてしまう。

かと言ってroute.jsのotherwiseを外したところで内容の取得ができなくなるだけなので リンクをクリックするとng-viewは空になる。

どうすりゃええのん(´・ω・`)

解決方法

このような場合には下記のように、ルーティングをしてほしくないURLに対して targetを付加することで解決することができる。

例えばファイルのダウンロードのリンクは下記のようになる。

<a href="/download/hoge.zip" target="_self">ダウンロードはこちら!</a>

また応用として、ルーティングに定義済みのURLでもtargetを付加すると AngularJSのルーティング処理を意図して解除することができる。

<!-- こっちはルーティングされる -->
<a href="/page01">PAGE01へ</a>

<!-- こっちはルーティングされずにページの更新がかかる -->
<a href="/page02" target="_self">PAGE02へ</a>

今回は同一ページ内で処理したかったのでtarget="_self"を指定したが もちろん新規ウィンドウで表示したいときはtarget="_blank"などを使うこともできる。

参考


関連リンク