193

I have directive which is site header with back button and I want on click to go back to the previous page. How do I do it in the angular way?

I have tried:

<header class="title">
<a class="back" ng-class="icons"><img src="../media/icons/right_circular.png" ng-click="history.back()" /></a>
<h1>{{title}}</h1>
<a href="/home" class="home" ng-class="icons"><img src="../media/icons/53-house.png" /></a>   
</header>

and this is the directive js:

myApp.directive('siteHeader', function () {
    return {
        restrict: 'E',
        templateUrl: 'partials/siteHeader.html',
        scope: {
            title: '@title',
            icons: '@icons'
        }
    };
});

but nothing happens. I looked in the angular.js API about $location but didn't find anything about back button or history.back().

2
  • 1
    You mentioned that this worked for you. Does it take you to different pages within your app or just does the browser back? It looks like it does browser back to me. Commented Sep 20, 2013 at 0:00
  • If you have set AngularJS to use HTML5 mode, going to any page already in the browser's history will used the cached version and not reload it. The project I am working on uses a mixture of old and new code, and the previous page changes after the data is saved with AngularJS. It's not an option to upgrade the first page to use AngularJS so I had to load a third, non-AngularJS page to redirect back to the first one. Not a nice solution, but it works. Commented Nov 10, 2016 at 0:42

10 Answers 10

266

You need to use a link function in your directive:

link: function(scope, element, attrs) {
     element.on('click', function() {
         $window.history.back();
     });
 }

See jsFiddle.

Sign up to request clarification or add additional context in comments.

7 Comments

But i have 2 buttons in my header one for home and one for back if i understand your code the element in the link function is the element that was clicked in that case history.back will apply also for home button?(which is not good)
I've changed the example a bit. Now there are two buttons (back and forward). It uses jQuery now, which means scope.$apply() is needed on click.
This code is untestable, you should really use the '$window' object instead of 'window.'
Does this work with IE8? I don't believe it has history
@Neil, Angular proudly does not support IE8. So, no.
|
134

Angular routes watch the browser's location, so simply using window.history.back() on clicking something would work.

HTML:

<div class="nav-header" ng-click="doTheBack()">Reverse!</div>

JS:

$scope.doTheBack = function() {
  window.history.back();
};

I usually create a global function called '$back' on my app controller, which I usually put on the body tag.

angular.module('myApp').controller('AppCtrl', ['$scope', function($scope) {
  $scope.$back = function() { 
    window.history.back();
  };
}]);

Then anywhere in my app I can just do <a ng-click="$back()">Back</a>

(If you want it to be more testable, inject the $window service into your controller and use $window.history.back()).

2 Comments

I'd recommend against putting anything in a "global function". There are a great many things that can happen to global state. In this case it's mostly the volatility of it. Code from third party (or another developer, if you're on a large team) in say, a directive, or a service could easily modify the $scope.$back for it's children. Which could be hard to debug. It's definitely better practice to inject a service into each component, and expose the functionality where needed. The example is only "global to the app", but there's risk
Source: I've had things I stuck in a "global" app $scope bite me too many times to count, and I've stopped using that technique in favor of services. Which can still be tromped on, but it's way easier to suss out.
66

Ideally use a simple directive to keep controllers free from redundant $window

app.directive('back', ['$window', function($window) {
        return {
            restrict: 'A',
            link: function (scope, elem, attrs) {
                elem.bind('click', function () {
                    $window.history.back();
                });
            }
        };
    }]);

Use like this:

<button back>Back</button>

1 Comment

Glad you showed the $window dependency - that's important.
20

Another nice and reusable solution is to create a directive like this:

app.directive( 'backButton', function() {
    return {
        restrict: 'A',
        link: function( scope, element, attrs ) {
            element.on( 'click', function () {
                history.back();
                scope.$apply();
            } );
        }
    };
} );

then just use it like this:

<a href back-button>back</a>

1 Comment

Seems to work once you reorder the closing curlys and rename the directive and element attr to match each other.
18

In case it is useful... I was hitting the "10 $digest() iterations reached. Aborting!" error when using $window.history.back(); with IE9 (works fine in other browsers of course).

I got it to work by using:

setTimeout(function() {
  $window.history.back();
},100);

6 Comments

Other answers make use of plain window. You seem to be using the $window Angular service. Maybe this is the root cause?
I got the same error under IE9 and this solved the problem. See github.com/angular/angular.js/issues/1417 He is right about using $window, all other answers are "wrong" on this point, see docs.angularjs.org/api/ng.$window
Any idea on why 100ms? That's quite a delay, does it work with less?
@Kevin The 100ms was just a duration that I thought was reasonable and imo not noticeable. Indeed a smaller delay might work.
I get similar issues in latest Chrome when using Angular default navigation in my app, and the delay won't help either. Only disabling Angular's navigation management helped.
|
3

Or you can simply use code :

onClick="javascript:history.go(-1);"

Like:

<a class="back" ng-class="icons">
   <img src="../media/icons/right_circular.png" onClick="javascript:history.go(-1);" />
</a>

1 Comment

this looks wrong on so many levels, cf. this blog post from almost 10 years ago
1

There was a syntax error. Try this and it should work:

directives.directive('backButton', function(){
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            element.bind('click', function () {
                history.back();
                scope.$apply();
            });
        }
    }
});

Comments

1

Angular 4:

/* typescript */
import { Location } from '@angular/common';
// ...

@Component({
  // ...
})
export class MyComponent {

  constructor(private location: Location) { } 

  goBack() {
    this.location.back(); // go back to previous location
  }
}

Comments

0

For me, my problem was I needed to navigate back and then transition to another state. So using $window.history.back() didn't work because for some reason the transition happened before history.back() occured so I had to wrap my transition in a timeout function like so.

$window.history.back();
setTimeout(function() {
  $state.transitionTo("tab.jobs"); }, 100);

This fixed my issue.

Comments

0

In AngularJS2 I found a new way, maybe is just the same thing but in this new version :

import {Router, RouteConfig, ROUTER_DIRECTIVES, Location} from 'angular2/router'; 

(...)

constructor(private _router: Router, private _location: Location) {}

onSubmit() {
    (...)
    self._location.back();
}

After my function, I can see that my application is going to the previous page usgin location from angular2/router.

https://angular.io/docs/ts/latest/api/common/index/Location-class.html

1 Comment

It works, you have to import it from @angular/common, not @angular/router.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.