[angularjs] $location / switching between html5 and hashbang mode / link rewriting

I was under the impression that Angular would rewrite URLs that appear in href attributes of anchor tags within tempaltes, such that they would work whether in html5 mode or hashbang mode. The documentation for the location service seems to say that HTML Link Rewriting takes care of the hashbang situation. I would thus expect that when not in HTML5 mode, hashes would be inserted, and in HTML5 mode, they would not.

However, it seems that no rewriting is taking place. The following example does not allow me to just change the mode. All links in the application would need to be rewritten by hand (or derived from a variable at runtime. Am I required to manually rewrite all URLs depending on the mode?

I don't see any client-side url rewriting going on in Angular 1.0.6, 1.1.4 or 1.1.3. It seems that all href values need to be prepended with #/ for hashbang mode and / for html5 mode.

Is there some configuration necessary to cause rewriting? Am I misreading the docs? Doing something else silly?

Here's a small example:

<head>
    <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.3/angular.js"></script>
</head>

<body>
    <div ng-view></div>
    <script>
        angular.module('sample', [])
            .config(
        ['$routeProvider', '$locationProvider',
            function ($routeProvider, $locationProvider) {

                //commenting out this line (switching to hashbang mode) breaks the app
                //-- unless # is added to the templates
                $locationProvider.html5Mode(true);

                $routeProvider.when('/', {
                    template: 'this is home. go to <a href="/about"/>about</a>'
                });
                $routeProvider.when('/about', {
                    template: 'this is about. go to <a href="/"/>home</a'
                });
            }
        ])
            .run();
    </script>
</body>

Addendum: in re-reading my question, I see that I used the term "rewriting" without an abundance of clarity as to who and when I wanted to do the rewriting. The question is about how to get Angular to rewrite the URLs when it renders paths and how to get it to interpret paths in the JS code uniformly across the two modes. It is not about how to cause a web server to do HTML5-compatible rewriting of requests.

This question is related to angularjs angular-routing

The answer is


This took me a while to figure out so this is how I got it working - Angular WebAPI ASP Routing without the # for SEO

  1. add to Index.html - base href="/">
  2. Add $locationProvider.html5Mode(true); to app.config

  3. I needed a certain controller (which was in the home controller) to be ignored for uploading images so I added that rule to RouteConfig

         routes.MapRoute(
            name: "Default2",
            url: "Home/{*.}",
            defaults: new { controller = "Home", action = "SaveImage" }
        );
    
  4. In Global.asax add the following - making sure to ignore api and image upload paths let them function as normal otherwise reroute everything else.

     private const string ROOT_DOCUMENT = "/Index.html";
    
    protected void Application_BeginRequest(Object sender, EventArgs e)
    {
        var path = Request.Url.AbsolutePath;
        var isApi = path.StartsWith("/api", StringComparison.InvariantCultureIgnoreCase);
        var isImageUpload = path.StartsWith("/home", StringComparison.InvariantCultureIgnoreCase);
    
        if (isApi || isImageUpload)
            return;
    
        string url = Request.Url.LocalPath;
        if (!System.IO.File.Exists(Context.Server.MapPath(url)))
            Context.RewritePath(ROOT_DOCUMENT);
    }
    
  5. Make sure to use $location.url('/XXX') and not window.location ... to redirect

  6. Reference the CSS files with absolute path

and not

<link href="app/content/bootstrapwc.css" rel="stylesheet" />

Final note - doing it this way gave me full control and I did not need to do anything to the web config.

Hope this helps as this took me a while to figure out.


I wanted to be able to access my application with the HTML5 mode and a fixed token and then switch to the hashbang method (to keep the token so the user can refresh his page).

URL for accessing my app:

http://myapp.com/amazing_url?token=super_token

Then when the user loads the page:

http://myapp.com/amazing_url?token=super_token#/amazing_url

Then when the user navigates:

http://myapp.com/amazing_url?token=super_token#/another_url

With this I keep the token in the URL and keep the state when the user is browsing. I lost a bit of visibility of the URL, but there is no perfect way of doing it.

So don't enable the HTML5 mode and then add this controller:

.config ($stateProvider)->
    $stateProvider.state('home-loading', {
         url: '/',
         controller: 'homeController'
    })
.controller 'homeController', ($state, $location)->
    if window.location.pathname != '/'
        $location.url(window.location.pathname+window.location.search).replace()
    else
        $state.go('home', {}, { location: 'replace' })

Fur future readers, if you are using Angular 1.6, you also need to change the hashPrefix:

appModule.config(['$locationProvider', function($locationProvider) {
    $locationProvider.html5Mode(true);
    $locationProvider.hashPrefix('');
}]);

Don't forget to set the base in your HTML <head>:

<head>
    <base href="/">
    ...
</head>

More info about the changelog here.