Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS HTML5 mode degrade to full page reloads in lieu of hashbang

Tags:

angularjs

By enabling HTML5 mode in AngularJS, the $location service will rewrite URLs to remove the hashbang from them. This is a great feature that will help me with my application, but there is a problem with its fallback to hashbang mode. My service requires authentication, and I am forced to use an external authentication mechanism from my application. If a user attempts to go to a URL for my app with a hashbang in it, it will first redirect them to the authentication page (won't ever touch my service unless successfully authenticated), and then redirect them back to my application. Being that the hash tag is only seen from the client side, it will drop off whatever parts of the routes come after by the time they hit my server. Once they are authenticated, they may re-enter the URL and it will work, but its that one initial time that will cause a disruption to the user experience.

My question is then, is there any way to go from $location.html5Mode(true) to the fallback of full page reloads for un-supportive browsers, skipping the hashbang method of routing entirely in AngularJS?

The best comparison of available implementations of what I'm aiming for would be something such as browsing around folders on github.com. If the browser supports rewriting the URL without initiating a page refresh, the page will asynchronously load the necessary parts. If the browser does not support it, when a user clicks on a folder, a full-page refresh occurs. Can this be achieved with AngularJS in lieu of using the hashbang mode?

like image 525
Scott Avatar asked Feb 19 '13 22:02

Scott


4 Answers

DON'T overwrite the core functionality.

Use Modernizr, do feature detection, and then proceed accordingly.

check for history API support

if (Modernizr.history) {
  // history management works!
} else {
  // no history support :(
  // fall back to a scripted solution like History.js
}
like image 192
George Katsanos Avatar answered Dec 06 '22 14:12

George Katsanos


Try to wrap $location and $routeProvider configuration in browser's HTML5 History API checking, like this:

if (isBrowserSupportsHistoryAPI()) {
    $location.html5Mode(true)
    $routeProvider.when(...);
}

Also may be you need to create a wrapper to $location if you use it to change path. (Sorry for terrible english)

like image 29
ardentum-c Avatar answered Dec 06 '22 14:12

ardentum-c


Why not handle the un-authenticated redirect on the client side for this situation? I'd need to know a bit more about exactly how your app works to give you a more specific solution but essentially something like:

  1. User goes to a route handled by AngularJS, server serves up the AngularJS main template and javascript
  2. User is not authenticated, AngularJS detects this and redirects to the authentication page

You could have something in the module's run block for when the AngularJS application starts:

module('app',[])
  .configure(...yadda...yadda...yadda...)
  .run(['$location', 'authenticationService', function($location, auth) {
    if (!auth.isAuthenticated()) {
      $location.url(authenticationUrl) 
    }
  });

I've subbed in a service which would find out if you were authenticated somehow, up to you how, could be checking a session cookie, could be hitting your API to ask. Really depends on how you want to continue to check authentication as the client application runs.

like image 20
Chris Nicola Avatar answered Dec 06 '22 16:12

Chris Nicola


You can try and override the functionality of the $location service. The general idea would be to rewrite the URL according to whether someone is already authenticated or not, or just use a single approach (without hashbangs) for all URLs, regardless to whether html5mode is on or not.

I'm not sure that I fully understand the use-case so I can't write the exact code that you need. Here is a sample implementation of how to overrides/implements and registers the $location service, just making sure that hashbang is always eliminated:

app.service('$location', [function() {
    var DEFAULT_PORTS = {
        ftp: 21,
        http: 80,
        https: 443
    };

    angular.extend(this, {
        absUrl: function() {
            return location.href;
        },
        hash: function(hash) {
            return location.hash.substr(1);
        },
        host: function() {
            return location.host;
        },
        path: function(path) {
            if (!path) {
                return location.pathname;
            }
            location.pathname = path;
            return this;
        },
        port: function() {
            return location.port ? Number(location.port) : DEFAULT_PORTS[this.protocol()] || null;
        },
        protocol: function() {
            return location.protocol.substr(0, location.protocol.length - 1);
        },
        replace: function() {
            return this;
        },
        search: function(search, paramValue) {
            if (search || paramValue) {
                return this;
            }
            var query = {};
            location.search.substr(1).split("&").forEach(function(pair) {
                pair = pair.split("="); query[pair[0]] = decodeURIComponent(pair[1]);
            });
            return query;
        },
        url: function(url, replace) {
            return this.path();
        }
    });
}]);
like image 23
sagibb Avatar answered Dec 06 '22 15:12

sagibb