Subscribed unsubscribe Subscribe Subscribe

AngularJSにおける#と$locationProvider.html5Modeとreload時のNotFoundについて

最近、AngularJSを使っていて感じるのが、URLに#がついているなということです。
公式サイトを見てみます。

Hashbang mode (default mode)

In this mode, $location uses Hashbang URLs in all browsers. Angular also does not intercept and rewrite links in this mode. I.e. links work as expected and also perform full page reloads when other parts of the url than the hash fragment was changed.

※AngularJSは#移行のパスが変わった場合はリロードするようになっているのですね。

HTML5 mode

In HTML5 mode, the $location service getters and setters interact with the browser URL address through the HTML5 history API. This allows for use of regular URL path and search segments, instead of their hashbang equivalents. If the HTML5 History API is not supported by a browser, the $location service will fall back to using the hashbang URLs automatically. This frees you from having to worry about whether the browser displaying your app supports the history API or not; the $location service transparently uses the best available option.

Opening a regular URL in a legacy browser -> redirects to a hashbang URL Opening hashbang URL in a modern browser -> rewrites to a regular URL Note that in this mode, Angular intercepts all links (subject to the "Html link rewriting" rules below) and updates the url in a way that never performs a full page reload.

※これによってリロードが行われなくなるようです。
※この後、reloadした場合にトップ画面以外はcannot GETになるのはそのためだったのですね。

HTML5 modeにする

  • src/app/index.js
  .config(function ($routeProvider, $locationProvider) {
    $locationProvider.html5Mode(true);
    $routeProvider
      .when('/', {
        templateUrl: 'app/main/main.html',
        controller: 'MainCtrl'
      })
      .when('/about/', {
        templateUrl: 'app/about/about.html',
        controller: 'AboutCtrl'
      })
      .when('/skill/:kind/', {
        templateUrl: 'app/skill/skill.html',
        controller: 'SkillCtrl'
      })
      .otherwise({
        redirectTo: '/'
      });
  })

※$locationProviderをfunctionの引数に加えて、$locationProvider.html5Mode(true);とします。

  • src/index.html
<base href="/">

相対パスのベースとなるパスを指定
※つまり相対パスのスタートはsrcディレクトリということになります
※これでHTML5 modeになりましたが、トップ画面以外をリロードするとcannot getになってしまいますので、以下の手順でrewrite設定をします。
※#がなくなったことによる#以降が変わったらリロードが走るのが効かなくなるためです

リロードの際のrewrite設定

  • connect-modrewriteのインストール
$ sudo npm install connect-modrewrite --save

connect-modrewrite

  • gulp/server.jsの編集(connectタスクにmodRewrite設定を入れる)
'use strict';

var gulp = require('gulp');

var paths = gulp.paths;

var util = require('util');

var browserSync = require('browser-sync');

var modRewrite  = require('connect-modrewrite');

function browserSyncInit(baseDir, files, browser) {
  browser = browser === undefined ? 'default' : browser;

  var routes = null;
  if(baseDir === paths.src || (util.isArray(baseDir) && baseDir.indexOf(paths.src) !== -1)) {
    routes = {
      '/bower_components': 'bower_components'
    };
  }

  browserSync.instance = browserSync.init(files, {
    startPath: '/',
    server: {
      baseDir: baseDir,
      middleware: [
        modRewrite([
          '!\\.\\w+$ /index.html [L]'
        ])
      ],
      routes: routes
    },
    browser: browser
  });
}

enable reload while HTML5 mode · 8aa046e · keiwt/skillcracy · GitHub

※これでトップ画面以外でリロードした際にmodRewriteが走るようになります

ついでにコメントしておきます。

AngularJS HTML5 mode reloading the page gives wrong GET request - Stack Overflow

nginxへの設定追加

先ほどのgulpへの設定だとローカルのみですので、デプロイ先のnginxにも設定を加えます * /etc/nginx/conf.d/default.conf

rewrite ^/.*/$ /index.html last;

※ちなみにELBのヘルスチェックよりも下に書きます
※ちなみにlastはその後に他のlocationも見にいきます ※breakは ヘルスチェックなど、そのlocationだけで完結し、他のlocationは見にいきません

  • nginxの設定の確認と再起動
$ nginx -t
$ sudo nginx -s reload

ついでにgulpを見てみる

  • gulpfile.js
'use strict';

var gulp = require('gulp');

gulp.paths = {
  src: 'src',
  dist: 'dist',
  tmp: '.tmp',
  e2e: 'e2e'
};

require('require-dir')('./gulp');

gulp.task('default', ['clean'], function () {
    gulp.start('build');
});

※src,dist,tmp,e2eを使い、gulp/build.jsを実行するようです
※gulp/*.jsはそれぞれ、taskを定義しているようです