Como implementar history.back () no angular.js

190

Eu tenho uma diretiva que é o cabeçalho do site com o botão Voltar e quero clicar no botão para voltar à página anterior. Como faço isso da maneira angular?

Eu tentei:

<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>

e esta é a diretiva js:

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

mas nada acontece. Procurei na API angular.js cerca de $ location, mas não encontrei nada sobre o botão voltar ou history.back().

baba-dev
fonte
1
Você mencionou que isso funcionou para você. Leva você a páginas diferentes em seu aplicativo ou apenas o navegador volta? Parece que o navegador volta para mim.
Chookoos
Se você configurou o AngularJS para usar o modo HTML5, acessar qualquer página que já esteja no histórico do navegador utilizará a versão em cache e não a recarregará. O projeto em que estou trabalhando usa uma mistura de código antigo e novo, e a página anterior muda depois que os dados são salvos com o AngularJS. Não é uma opção para atualizar a primeira página para usar o AngularJS, então tive que carregar uma terceira página que não seja AngularJS para redirecionar de volta para a primeira. Não é uma solução agradável, mas funciona.
CJ Dennis

Respostas:

262

Você precisa usar uma função de link em sua diretiva:

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

Veja jsFiddle .

asgoth
fonte
Mas eu tenho 2 botões no meu cabeçalho, um para casa e outro para trás, se eu entender o seu código, o elemento na função de link é o elemento que foi clicado nesse histórico de caso. Back também se aplica ao botão home? (O que não é bom )
baba-dev
Eu mudei um pouco o exemplo. Agora existem dois botões (para frente e para trás). Ele usa jQuery agora, o que significa escopo. $ Apply () é necessário ao clicar.
asgoth
8
Esse código não pode ser testado; você realmente deve usar o objeto '$ window' em vez de 'window'.
Arbiter
Isso funciona com o IE8? Não acredito que tenha história
Neil
5
@ Neil, a Angular orgulhosamente não suporta o IE8. Então não.
Rap
133

As rotas angulares observam a localização do navegador; portanto, basta window.history.back()clicar em algo que funcione.

HTML:

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

JS:

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

Normalmente, crio uma função global chamada '$ back' no meu controlador de aplicativo, que geralmente coloco na etiqueta do corpo.

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

Então, em qualquer lugar do meu aplicativo, eu posso fazer <a ng-click="$back()">Back</a>

(Se você quiser que seja mais testável, injete o serviço $ window no seu controlador e use $window.history.back()).

Andrew Joslin
fonte
9
Eu recomendaria não colocar nada em uma "função global". Há muitas coisas que podem acontecer ao estado global . Nesse caso, é principalmente a volatilidade disso. Código de terceiros (ou outro desenvolvedor, se você estiver em uma equipe grande), digamos, uma diretiva ou um serviço pode modificar facilmente o $ scope. $ Back para seus filhos. O que pode ser difícil de depurar. Definitivamente, é uma prática melhor injetar um serviço em cada componente e expor a funcionalidade sempre que necessário. O exemplo é apenas "global para o aplicativo", mas há riscos
Ben Lesh
Fonte: tive coisas que eu coloquei no escopo de um aplicativo "global" e me mordi muitas vezes para contar, e parei de usar essa técnica em favor dos serviços. Que ainda pode ser pisoteado, mas é muito mais fácil entender.
Ben Lesh
65

Idealmente, use uma diretiva simples para manter os controladores livres de janelas $ redundantes

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

Use assim:

<button back>Back</button>
Darren
fonte
Que bom que você mostrou a dependência de $ window - isso é importante.
22718 Chris Smith
20

Outra solução agradável e reutilizável é criar uma diretiva como esta :

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

então use-o assim:

<a href back-button>back</a>
pleerock
fonte
1
Parece funcionar depois de reordenar as curvas de fechamento e renomear a diretiva e o elemento attr para coincidirem.
Remarsh
18

Caso isso seja útil ... eu estava atingindo as "10 $ digerências () alcançadas. Interrompendo!" erro ao usar $ window.history.back (); com o IE9 (funciona bem em outros navegadores, é claro).

Eu consegui trabalhar usando:

setTimeout(function() {
  $window.history.back();
},100);
Rob Bygrave
fonte
Outras respostas fazem uso de planície window. Você parece estar usando o $windowserviço Angular. Talvez esta seja a causa raiz?
Superjo
7
Eu recebi o mesmo erro no IE9 e isso resolveu o problema. Veja github.com/angular/angular.js/issues/1417 Ele está certo sobre o uso da janela $, todas as outras respostas estão "erradas" nesse ponto, consulte docs.angularjs.org/api/ng.$window
tanguy_k
Alguma idéia de por que 100ms? Isso é um atraso, funciona com menos?
Kevin
@ Kevin Os 100ms foram apenas uma duração que eu pensei que era razoável e que nem percebi. De fato, um atraso menor pode funcionar.
Rob Bygrave
Sinto problemas semelhantes no Chrome mais recente ao usar a navegação padrão Angular no meu aplicativo, e o atraso também não ajuda. Desativar apenas o gerenciamento de navegação da Angular ajudou.
Domi
3

Ou você pode simplesmente usar código:

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

Gostar:

<a class="back" ng-class="icons">
   <img src="../media/icons/right_circular.png" onClick="javascript:history.go(-1);" />
</a>
Arvind Kushwaha
fonte
1
isso parece errado em muitos níveis, cf. este post de quase 10 anos atrás
Rocco
1

Ocorreu um erro de sintaxe. Tente isso e deve funcionar:

directives.directive('backButton', function(){
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            element.bind('click', function () {
                history.back();
                scope.$apply();
            });
        }
    }
});
Ripan Kumar
fonte
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
fonte
0

Para mim, meu problema era precisar navegar de volta e fazer a transição para outro estado. Portanto, o uso $window.history.back()não funcionou porque, por algum motivo, a transição ocorreu antes do início do history.back (), então tive que agrupar minha transição em uma função de tempo limite dessa maneira.

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

Isso corrigiu meu problema.

deb2fast
fonte
0

No AngularJS2 eu encontrei uma nova maneira, talvez seja a mesma coisa, mas nesta nova versão:

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

(...)

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

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

Após minha função, posso ver que meu aplicativo está indo para a página anterior usando a localização do angular2 / router.

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

Paul Leclerc
fonte
Funciona, você precisa importá-lo de @ angular / common, não de @ angular / router.
plexo