192

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().

pb2q
  • 58,613
  • 19
  • 146
  • 147
baba-dev
  • 2,582
  • 6
  • 27
  • 32
  • 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. –  Sep 20 '13 at 00: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. – CJ Dennis Nov 10 '16 at 00:42

10 Answers10

265

You need to use a link function in your directive:

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

See jsFiddle.

asgoth
  • 35,552
  • 12
  • 89
  • 98
  • 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) – baba-dev Dec 28 '12 at 14:10
  • 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. – asgoth Dec 28 '12 at 14:24
  • 8
    This code is untestable, you should really use the '$window' object instead of 'window.' – Arbiter Apr 25 '13 at 18:19
  • Does this work with IE8? I don't believe it has history – Neil Apr 09 '14 at 08:56
  • 5
    @Neil, Angular **proudly** does not support IE8. So, no. – Rap Aug 30 '15 at 20:33
  • See my answer further below for a simple reusable directive – Darren Nov 02 '17 at 22:18
  • will this go back to same state with previous params? – VijayVishnu Sep 17 '19 at 13:17
135

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()).

Sampath
  • 63,341
  • 64
  • 307
  • 441
Andrew Joslin
  • 43,033
  • 21
  • 100
  • 75
  • 10
    I'd recommend against putting anything in a "global function". [There are a great many things that can happen to global state](http://programmers.stackexchange.com/a/148109/26025). 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 – Ben Lesh Jan 29 '14 at 17:05
  • 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. – Ben Lesh Jan 29 '14 at 17:06
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>
Darren
  • 9,014
  • 2
  • 39
  • 50
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>
Luca
  • 9,259
  • 5
  • 46
  • 59
pleerock
  • 18,322
  • 16
  • 103
  • 128
  • 1
    Seems to work once you reorder the closing curlys and rename the directive and element attr to match each other. – remarsh Dec 13 '14 at 00:36
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);
Rob Bygrave
  • 3,861
  • 28
  • 28
  • Other answers make use of plain `window`. You seem to be using the `$window` Angular service. Maybe this is the root cause? – superjos Mar 06 '13 at 19:11
  • 7
    I got the same error under IE9 and this solved the problem. See https://github.com/angular/angular.js/issues/1417 He is right about using $window, all other answers are "wrong" on this point, see http://docs.angularjs.org/api/ng.$window – tanguy_k Mar 10 '13 at 17:07
  • Any idea on why 100ms? That's quite a delay, does it work with less? – Kevin Mar 18 '14 at 00:28
  • @Kevin The 100ms was just a duration that I thought was reasonable and imo not noticeable. Indeed a smaller delay might work. – Rob Bygrave Apr 02 '14 at 07:37
  • 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](http://stackoverflow.com/questions/18611214/turn-off-url-manipulation-in-angularjs) helped. – Domi May 31 '14 at 08:49
  • Works for me but with delay = 500. – alehro Sep 09 '14 at 12:56
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>
Barnee
  • 3,212
  • 8
  • 41
  • 53
Arvind Kushwaha
  • 759
  • 2
  • 10
  • 18
  • 1
    this looks wrong on so many levels, cf. [this blog post](http://crisp.tweakblogs.net/blog/the-useless-javascript-pseudo-protocol.html) from almost 10 years ago – Rocco Apr 13 '16 at 20:08
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();
            });
        }
    }
});
phemt.latd
  • 1,775
  • 21
  • 33
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
  }
}
krmld
  • 1,248
  • 14
  • 9
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.

deb2fast
  • 990
  • 11
  • 18
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

Paul Leclerc
  • 1,117
  • 1
  • 14
  • 18