Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Successfully Call history.pushState() from Angular Without Inifinite Digest?

Is there a way to call history.pushState() without angular going into an infinite digest loop?

I'm trying to migrate my app from backend routing to frontend routing and all the stackoverflow posts/google resources don't seem to have an answer.

like image 239
Blaskovicz Avatar asked Apr 07 '14 13:04

Blaskovicz


People also ask

What is history pushState in angular?

In an HTML document, the history. pushState() method adds an entry to the browser's session history stack.

Does history pushState reload page?

But this function is not intended to reload the browser. All the function does, is to add (push) a new "state" onto the browser history, so that in future, the user will be able to return to this state that the web-page is now in.

What is the use of history pushState () method in HTML?

In an HTML document, the history.pushState () method adds an entry to the browser's session history stack.

Does pushState () cause a hashchange event?

Note that pushState () never causes a hashchange event to be fired, even if the new URL differs from the old URL only in its hash. This creates a new browser history entry setting the state, title , and url . Found a problem with this page?

What is pushState () and how does it work?

In a sense, calling pushState () is similar to setting window.location = "#foo", in that both will also create and activate another history entry associated with the current document. But pushState () has a few advantages:

Why does the pushState () method throw an exception?

If you pass a state object whose serialized representation is larger than this to pushState (), the method will throw an exception. If you need more space than this, you're encouraged to use sessionStorage and/or localStorage . Most browsers currently ignore this parameter, although they may use it in the future.


4 Answers

I recently encountered a variation of this problem myself and none of the answers here worked for me in isolation. history.pushState was defined, but only worked if called from the console. I was able to come up with a solution by consolidating a few of the answers here. Here is the code I used to solve this problem:

// Configure $locationProvider html5Mode
app.config(['$locationProvider', function($locationProvider) {
    $locationProvider.html5Mode({ enabled: true, requireBase: false, rewriteLinks: false });
}]);

// In controller
$window.history.pushState(null, 'Page Title', '/some_new_url');

I'm running Angular 1.5.5.

like image 166
Daniel Bonnell Avatar answered Nov 10 '22 21:11

Daniel Bonnell


This is what we do in an Angular 1.2.16 app based on this github comment: https://github.com/angular/angular.js/issues/3924#issuecomment-48592773

$location.url(myNewUrl);
$location.replace();
$window.history.pushState(null, 'any', $location.absUrl());

This code is part of a controller. I'm using it to add a query param to the url without wanting to reload the page. It works in our case.

like image 23
Torsten Uhlmann Avatar answered Nov 10 '22 21:11

Torsten Uhlmann


Use the rewriteLinks option on the $locationProvider html5Mode object:

    pageModule.config(function($locationProvider) {
        $locationProvider.html5Mode({
            enabled: true,
            requireBase: false,
            rewriteLinks: false
        });
    });

Afterwards, you should be able to use the $location service properly, such as:

    this.$location.path("/mynewpath");
like image 42
clint Avatar answered Nov 10 '22 21:11

clint


I was able to come up with a solution that works with angular 1.2.15.

The gist of it is to add a target attribute to each link and then use the $location service to change the location.

Based on the current angular code, anchor tags that have a target attribute are ignored (note that this solution may eventually break).

To do this, ensure this javascript runs before angular:

// prevent angular from taking over link clicks until we have frontend routing
$(document.documentElement).on("click","a",function(event) {
  var elem = $(this), target = elem.attr("target");
  if(!target) {
    elem.attr("target","_self");
    setTimeout(function() {
      elem.removeAttr("target");
    });
  }
});

After that, configure your location provider to use html5mode:

  angular.module("App", ["App.controllers"]).config([
    "$locationProvider", function($locationProvider) {
      if (history.pushState) {
        $locationProvider.html5Mode(true);
      }
    }
  ]);

Now, in your controller, inject the location service and use it normally:

angular.module("App.controllers",[]).controllers("SomeController", ["$scope", "$location", function($scope,$location){
    $scope.updateLocation = function() {
      if (history.pushState) {
        $location.path("/new/path/here");
      }
    };
  }]);
like image 44
Blaskovicz Avatar answered Nov 10 '22 23:11

Blaskovicz