How to go back last page
Asked Answered
E

27

586

Is there a smart way to go back last page in Angular 2?

Something like

this._router.navigate(LASTPAGE);

For example, page C has a Go Back button,

  • Page A -> Page C, click it, back to page A.

  • Page B -> Page C, click it, back to page B.

Does router have this history information?

Edrisedrock answered 17/2, 2016 at 2:47 Comment(0)
M
1048

Actually you can take advantage of the built-in Location service, which owns a "Back" API.

Here (in TypeScript):

import {Component} from '@angular/core';
import {Location} from '@angular/common';

@Component({
  // component's declarations here
})
class SomeComponent {

  constructor(private _location: Location) 
  {}

  backClicked() {
    this._location.back();
  }
}

Edit: As mentioned by @charith.arumapperuma Location should be imported from @angular/common so the import {Location} from '@angular/common'; line is important.

Mahayana answered 7/4, 2016 at 8:29 Comment(18)
There is no built in back in the location serviceIntone
hi Justin, please refer to angular.io/docs/js/latest/api/router/… you would see the angular 2 documentation and see that there is a "Built in" back()Mahayana
The Location should be imported from "angular2/router" in older releases of Angular 2. In newer releases, it should be from "@angular/common".Tutankhamen
Is it correct to assert: never use "window.history.back();" in Angular 2?Escribe
If you have it built in in the framework, I dont see any reason to use the "native" "window.history.back();" which is an HTML5 feature (developer.mozilla.org/en-US/docs/Web/API/Window/history)Mahayana
For what it's worth, the official Angular2 API documentation for Location states: "Note: it's better to use Router service to trigger route changes. Use Location only if you need to interact with or create normalized URLs outside of routing." @Sasxa's answer does apparently show a way to use Router to do this. However, the Location method definitely is more convenient. Does anyone know why the Router method might be more correct than the Location method?Motteo
@Andrew: I've encountered the problem, that you cannot go back two times, if you use this.location.back(). You will jump back to the initial site.Escribe
How would I do a conditional navigation? let's say I am in an article details page... but an error happens... and I have a buttons saying "error happened, cklick to go back to article list" now, if the previous URL was article list I want to use this._location.back().... if not, I want to use router.navigate(['articleList']);Deflection
@DS_web_developer, i think there is not other way but to pragmatically implement what you just described in the Button click handler. like: if(error) then navigate to X, otherwise location.back(). will this be good for you?Mahayana
not really... this button is displaying JUST on error.... and I need on the click: if(this.router.previous.has('articleList')){location.back();}else{router.navigate(['/articleList']);} ... The point being, that the previous URL (if coming from article list) might have URL parameters (like filters, pagination, sorting, etc)... and I want the page to go back to that if it's set.... but if a user just navigates to the article from outside source and not via the list (or maybe from a homepage widget) than redirect him to the root of the articleListDeflection
Is there any disadvantege of this solution instead of preparing routerLink?Sixtyfour
@yt61, not sure, maybe re-usability? or if you can get to a specified page from various routes, so you dont know in advance the route to go back to.Mahayana
There is some bloat/unneeded code in this answer, Hinrich's answer below is the same yet more elegantCompost
When I use this._location.back() I get some component null error, which don't happen on using window.history.back(). So maybe it's better to use history.back instead of Location.back?Undrape
Using the router method would run the resolvers again @AndrewWillemsChess
@Escribe Not having this issue on v4.4.4. Was this on an older version?Saree
If you use an anchor instead of a button you will need make it a passive link like this <a href="javascript:void(0)" (click)="goBack()">Back</a> ... as you can see in this response.Alveolus
how is backClicked() tied to the event?, this function will not run for me.Chlo
N
147

In the final version of Angular 2.x / 4.x - here's the docs https://angular.io/api/common/Location

/* typescript */

import { Location } from '@angular/common';
// import stuff here

@Component({
// declare component here
})
export class MyComponent {

  // inject location into component constructor
  constructor(private location: Location) { }

  cancel() {
    this.location.back(); // <-- go back to previous location on cancel
  }
}
Nicholenicholl answered 31/1, 2017 at 9:34 Comment(2)
While navigating back to previous screen can we retain input entered values without using a object in service.Morissa
How to show back animation while location.back() is executing?Mesotron
S
105

<button backButton>BACK</button>

You can put this into a directive, that can be attached to any clickable element:

import { Directive, HostListener } from '@angular/core';
import { Location } from '@angular/common';

@Directive({
    selector: '[backButton]'
})
export class BackButtonDirective {
    constructor(private location: Location) { }

    @HostListener('click')
    onClick() {
        this.location.back();
    }
}

Usage:

<button backButton>BACK</button>
Sacristy answered 10/10, 2018 at 8:7 Comment(4)
If you refresh on this page, and click on the button that triggers "this.location.back()", it will just trigger a page refresh. Is there any way that the Location module can detect if previous path exists?Dalhousie
Keep in mind if a user went directly to a page where Back button exists and if he clicks on a button.. then he will be throw out of the app to the previous page according to the browser(platform) history.Sinhalese
For future readers, look at API docsSinhalese
I had to add BackButtonDirective in the declarations list in app.module.ts to make it work.Beagle
A
31

Tested with Angular 5.2.9

If you use an anchor instead of a button you must make it a passive link with href="javascript:void(0)" to make Angular Location work.

app.component.ts

import { Component } from '@angular/core';
import { Location } from '@angular/common';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent {

  constructor( private location: Location ) { 
  }

  goBack() {
    // window.history.back();
    this.location.back();

    console.log( 'goBack()...' );
  }
}

app.component.html

<!-- anchor must be a passive link -->
<a href="javascript:void(0)" (click)="goBack()">
  <-Back
</a>
Alveolus answered 1/4, 2018 at 9:46 Comment(2)
I would suggest creating a 'clickPreventDefault' directive rather than using javascript:void(0). Something like... @Directive({ selector: '[clickPreventDefault]' }) export class ClickPreventDefaultDirective { @HostListener("click", ["$event"]) onClick($event: Event) { $event.preventDefault(); } }Centillion
Thank you @Centillion , it's a more elaborate way but it works too. Another working solution is don't use herf: <a (click)="goBack()"> although this way don't pass HTML Validators.Alveolus
P
25

Maybe you'd like to check if the previous point of history is within your app. For example, if you enter directly to your app and do location.back() (by pressing a <- back button in a toolbar for example), you'd be back to your browser's main page, instead of going somewhere else within your app.

This is how I check for this:

import { Component } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Location } from '@angular/common';

@Component({
  selector: 'app-foo',
  template: ''
})
export class FooComponent {

  private readonly canGoBack: boolean;

  constructor(
    private readonly route: ActivatedRoute,
    private readonly router: Router,
    private readonly location: Location
  ) {
    // This is where the check is done. Make sure to do this
    // here in the constructor, otherwise `getCurrentNavigation()`
    // will return null. 
    this.canGoBack = !!(this.router.getCurrentNavigation()?.previousNavigation);
  }

  goBack(): void {
    if (this.canGoBack) {
      // We can safely go back to the previous location as
      // we know it's within our app.
      this.location.back();
    } else {
      // There's no previous navigation.
      // Here we decide where to go. For example, let's say the
      // upper level is the index page, so we go up one level.
      this.router.navigate(['..'], {relativeTo: this.route});
    }
  }

}

We check if the navigation that loaded the current route has a previous sibling. This has to be done in the constructor, while the navigation process is still active.

This doesn't come without caveats though:

  • canGoBack will be false even if the previous location is actually within our app but the page was refreshed.
  • The user might want to "go back" to the previous page (where the goBack() ocurred) by clicking the browser's back button, but since the app went back on history instead of pushing a new location, the user will be going back even further and might get confused.
Phenoxide answered 19/2, 2021 at 14:43 Comment(0)
P
20

You can implement routerOnActivate() method on your route class, it will provide information about previous route.

routerOnActivate(nextInstruction: ComponentInstruction, prevInstruction: ComponentInstruction) : any

Then you can use router.navigateByUrl() and pass data generated from ComponentInstruction. For example:

this._router.navigateByUrl(prevInstruction.urlPath);
Pittman answered 17/2, 2016 at 2:54 Comment(3)
Is this still valid for Angular 2.1.0?Rockbottom
@Rockbottom I don't think so, there is documentation for routerOnActivateBeady
The routerOnActivate() link in this answer is broken. Seems this is not the way to do it in release version.Papeterie
B
19

After all these awesome answers, I hope my answer finds someone and helps them out. I wrote a small service to keep track of route history. Here it goes.

import { Injectable } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import { filter } from 'rxjs/operators';

@Injectable()
export class RouteInterceptorService {
  private _previousUrl: string;
  private _currentUrl: string;
  private _routeHistory: string[];

  constructor(router: Router) {
    this._routeHistory = [];
    router.events
      .pipe(filter(event => event instanceof NavigationEnd))
      .subscribe((event: NavigationEnd) => {
        this._setURLs(event);
      });
  }

  private _setURLs(event: NavigationEnd): void {
    const tempUrl = this._currentUrl;
    this._previousUrl = tempUrl;
    this._currentUrl = event.urlAfterRedirects;
    this._routeHistory.push(event.urlAfterRedirects);
  }

  get previousUrl(): string {
    return this._previousUrl;
  }

  get currentUrl(): string {
    return this._currentUrl;
  }

  get routeHistory(): string[] {
    return this._routeHistory;
  }
}
Bertelli answered 17/1, 2019 at 21:20 Comment(6)
After trying more or less all solutions, I find this one is the more consistent way to do thatDearden
What if I open the page on particular link and I want that it will go back to page that in page tree?Jodoin
Iḿ getting error Type string has no call signatures when calling preiousUrl(). Can you help me out? :DMorelock
@SzokeLori Sounds like your "this" is pointing to String type. You should post a question with the code.Bertelli
@AnjilDhamala well... i just injected the thing as private to constructor and wanted to console log it. Mind me, im a beginnerMorelock
@SzokeLori: I understand. However, it's hard to tell what exactly is going wrong in your context. Did you register this injectable in a Module? If possible, post your question in this website and put a link in this comment section. I'll try to help you out if someone hasn't already.Bertelli
A
17

Also work for me when I need to move back as in file system. P.S. @angular: "^5.0.0"

<button type="button" class="btn btn-primary" routerLink="../">Back</button>
Aftermath answered 4/1, 2018 at 16:28 Comment(4)
I was hopeful this would work, but this moves back to the next Route above it - not to the route you were on before navigating to the page. Good to know this exists, but if you have multiple entry points for your component, this method will only ever go back to the route above it, not where you originated from.Flipflop
As I write "when I need to move back as in file system" :) For me, this behavior was also unexpected.Aftermath
You're confusing back (e.g. cd -) with up (cd ..). Nevertheless, it's handy to know this works.Accession
This returns to the parent view (it does not always equate to the previous view)Floyfloyd
E
16

I made a button I can reuse anywhere on my app.

Create this component

import { Location } from '@angular/common';
import { Component, Input } from '@angular/core';

@Component({
    selector: 'back-button',
    template: `<button mat-button (click)="goBack()" [color]="color">Back</button>`,
})
export class BackButtonComponent {
    @Input()color: string;

  constructor(private location: Location) { }

  goBack() {
    this.location.back();
  }
}

Then add it to any template when you need a back button.

<back-button color="primary"></back-button>

Note: This is using Angular Material, if you aren't using that library then remove the mat-button and color.

Eisenstark answered 30/1, 2018 at 1:16 Comment(4)
Does this approach work with named router outlets though? Say I have several on the page and only want to go back on one of them, would this work?Mimi
You'll have to use a different approach for that situation. If you had the same back button in two different router outlets, they will probably both do the same thing and go back on the last router outlet that was changed.Eisenstark
For named outlets, I found that this approach worked: this.router.navigate(['../'], {relativeTo: this.route})Mimi
How to use this component inside another component?Ironworks
R
13

Simply use Location , An Angular service that applications can use to interact with a browser's URL.

Import it :

import { Location } from '@angular/common';

Inject it :

constructor(private location: Location) { }

Simply Use it :

goBack() {
    this.location.back(); // Navigates back in the platform's history
}
Rifle answered 22/6, 2022 at 11:14 Comment(1)
Beware that there's an edge case with this scenario such that if user opens a new tab there won't be an entry in the history to go back to. This can throw user out of the Angular application as well as introduce security issues as there's no API for directly inspecting the browser history.Wilmington
W
11

The way I did it while navigating to different page add a query param by passing current location

this.router.navigate(["user/edit"], { queryParams: { returnUrl: this.router.url }

Read this query param in your component

this.router.queryParams.subscribe((params) => {
    this.returnUrl = params.returnUrl;
});

If returnUrl is present enable the back button and when user clicks the back button

this.router.navigateByUrl(this.returnUrl); // Hint taken from Sasxa

This should able to navigate to previous page. Instead of using location.back I feel the above method is more safe consider the case where user directly lands to your page and if he presses the back button with location.back it will redirects user to previous page which will not be your web page.

Whew answered 14/3, 2017 at 5:10 Comment(3)
Need to import ActivatedRoute and use that instead of Router on the queryParams subscription (e.g., this.route.queryParams.subscribe), but otherwise, seems to work!Headachy
for me it is working fine with router itself even in angular 4Whew
Best answer, but in Angular 5 (up to x?) you need to inject an object of "ActivatedRoute" and use queryParams on this object, as Stephen Kaiser already stated.Hilliard
P
6

In RC4:

import {Location} from '@angular/common';
Ponce answered 21/7, 2016 at 11:32 Comment(1)
This is a complement to an answer but not an answer itselfEmpiric
B
4

Please make sure you explicitly import if you are using latest Angular/TypeScript

import { Location } from '@angular/common';

and

 onCancel() {
    this.location.back();
  }
Buenrostro answered 13/11, 2021 at 15:54 Comment(3)
just unfortunate that the answer was already given 5 years earlier, and even misses the part where you define location in the constructorRexfourd
Yes, don't forget to define in your constructor() the: private location: LocationDermato
As noted above for one of the proposed solutions, beware that there's an edge case with this scenario such that if user opens a new tab there won't be an entry in the history to go back to. This can throw user out of the Angular application as well as introduce security issues as there's no API for directly inspecting the browser history.Wilmington
B
3

Since beta 18:

import {Location} from 'angular2/platform/common';

Birdlime answered 2/6, 2016 at 13:59 Comment(0)
M
3

To go back without refreshing the page, We can do in html like below javascript:history.back()

<a class="btn btn-danger" href="javascript:history.back()">Go Back</a>
Meter answered 29/11, 2019 at 7:25 Comment(1)
I would recommend using Location service instead. official APISinhalese
R
3

2022
Utilize your app routing - more of an "angular approach" rather than accessing the browser's location object for navigation history. Think of why you need the user to go 'back', and what 'back' means in the broader context of your application and its routes.

for example, returning to a parent route from its child

  this.router.navigate(['..'], {relativeTo: this.route});

You can also read about previous navigation

previousNavigation : The previously successful Navigation object. Only one previous navigation is available, therefore this previous Navigation object has a null value for its own previousNavigation.

Romeo answered 17/4, 2022 at 18:21 Comment(1)
This seems to take you up the route path, not back to previous location.Unrighteous
D
3

Imports:

import { Location } from '@angular/common';
import { Router } from '@angular/router';

Constructor:

constructor(private readonly router: Router, private readonly location: Location) {
  location.onUrlChange(() => this.canGoBack = !!this.router.getCurrentNavigation()?.previousNavigation);
}

Optional, avoiding to go back outside the application:

private canGoBack: boolean = false;

constructor(private router:Router,private location:Location){
  this.canGoBack = !!(this.router.getCurrentNavigation()?.previousNavigation);
}

Go back:

goBack(): void {
  if (this.canGoBack) {
    this.location.back();
  }
}

Html:

<button (click)="goBack()"></button>
Doyon answered 25/12, 2022 at 19:26 Comment(0)
G
2

in angular 4 use preserveQueryParams, ex:

url: /list?page=1

<a [routerLink]="['edit',id]" [preserveQueryParams]="true"></a>

When clicking the link, you are redirected edit/10?page=1, preserving params

ref: https://angular.io/docs/ts/latest/guide/router.html#!#link-parameters-array

Gone answered 12/5, 2017 at 19:28 Comment(0)
F
2

im using this way:

import { Location } from '@angular/common'
import { Component, Input } from '@angular/core'

@Component({
    selector: 'Back_page',
    template: `<button  (click)="onBack()">Back</button>`,
})
export class BackPageComponent {
  constructor(private location: Location) { }

  onBack() {
    this.location.back();// <-- go back to previous location
  }
}
Fermat answered 23/3, 2021 at 19:31 Comment(0)
F
2

I came up with this, you can also check if there is a previous page. Make sure to use the service in your appComponent.

import { Injectable } from '@angular/core';
import { Location } from '@angular/common';
import { NavigationEnd, Router } from '@angular/router';

interface HistoryData {
  previousPage: string | null,
  currentPage: string | null,
}

@Injectable({ providedIn: 'root' })
export class GoBackService {

  private historyData: HistoryData = { previousPage: null, currentPage: null };

  constructor(private router: Router, private location: Location) {
    this.router.events.subscribe((event) => {
      if (event instanceof NavigationEnd) {
        this.historyData.previousPage = this.historyData.currentPage;
        this.historyData.currentPage = event.urlAfterRedirects;
      }
    });
  }

  public goBack(): void {
    if (this.historyData.previousPage) this.location.back();
  }

  public canGoBack(): boolean {
    return Boolean(this.historyData.previousPage);
  }

}
Frederiksberg answered 23/5, 2022 at 13:13 Comment(0)
A
2

If you wanna go back to the previous Window, You can use History API.

window.history.back();

Note: This API is only available on the main thread (Window). It cannot be accessed in Worker or Worklet contexts.

Abbreviated answered 12/6, 2023 at 10:50 Comment(0)
B
1

yes you can do it. write this code on your typescript component and enjoy!

import { Location } from '@angular/common'
import { Component, Input } from '@angular/core'

@Component({
    selector: 'return_page',
    template: `<button mat-button (click)="onReturn()">Back</button>`,
})
export class ReturnPageComponent {
  constructor(private location: Location) { }

  onReturn() {
    this.location.back();
  }
}
Blond answered 11/11, 2020 at 7:7 Comment(0)
I
1

try routerLink="../LASTPAGE"

Indecorum answered 15/1, 2022 at 10:5 Comment(0)
S
0

Also you can use this service with fallback feature in case history is empty

url-back.service.ts

import { Injectable } from '@angular/core';
import { Location } from '@angular/common';

import { Router } from '@angular/router';

const EMPTY_HISTORY_LENGTH = 2;

/**
 * This service helps to Navigate back to the prev page, and if no prev page,
 * will redirect to the fallback url.
 */
@Injectable()
export class UrlBackService {
  constructor(private router: Router, private location: Location) {}

  /**
   * This method will back you to the previous page,
   * if no previous page exists, will redirect you to the fallback url.
   * @param href - url, if tryNativeFirst is provided, this is fallback url
   * @param tryNativeFirst - try to go back natively using browser history state.
   */
  back(href: string, tryNativeFirst: boolean = false) {
    if (tryNativeFirst) {
      if (history.length === EMPTY_HISTORY_LENGTH) {
        this.router.navigate(UrlBackService.urlToArray(href));
      } else {
        this.location.back();
      }
    } else {
      this.router.navigate(UrlBackService.urlToArray(href));
    }
  }

  /**
   * In case that router.navigate method tries to escape all '/' in the string,
   * was decided to split string to array, and if URL starts or ends with slash - remove them, eg:
   * /my/url will be split to ['', 'my', 'url'], so we need to remove empty spaces use filter function.
   * @param href
   * @private
   */
  private static urlToArray(href: string) {
    return href.split('/').filter((notEmpty) => notEmpty);
  }
}

url-back.service.spec.ts

import { TestBed } from '@angular/core/testing';

import { UrlBackService } from './url-back.service';
import { Router } from '@angular/router';
import { Location } from '@angular/common';
import { RouterTestingModule } from '@angular/router/testing';

describe('UrlBackService', () => {
  let service: UrlBackService;
  let router: Router;
  let location: Location;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [RouterTestingModule],
      providers: [UrlBackService],
    });
    service = TestBed.inject(UrlBackService);
    router = TestBed.inject(Router);
    location = TestBed.inject(Location);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('no meter what history state is, it should be redirected to the /my/url', () => {
    spyOn(router, 'navigate');
    service.back('/my/url');
    expect(router.navigate).toHaveBeenCalledWith(['my', 'url']);
  });

  it('in case history is empty push to /my/url', () => {
    spyOn(router, 'navigate');
    service.back('/my/url', true);
    expect(router.navigate).toHaveBeenCalledWith(['my', 'url']);
  });

  it('in case history is NOT empty push to url1', () => {
    spyOn(location, 'back');
    window.history.pushState(null, '', 'url1');

    service.back('/my/url', true);
    expect(location.back).toHaveBeenCalled();
  });
});
Scholastic answered 29/10, 2021 at 13:8 Comment(0)
F
0

Version of @Parziphal answer for non-change detected components:

  import { Location } from '@angular/common';
  import { Router } from '@angular/router';

  constructor(private readonly router: Router, private readonly location: Location) {
    location.onUrlChange(() => this.canGoBack = !!this.router.getCurrentNavigation()?.previousNavigation);
  }

  goBack(): void {
    if (this.canGoBack) {
      this.location.back();
    }
  }

Fourthclass answered 17/11, 2022 at 11:53 Comment(0)
P
0

The latest update from Angular 14. If previousUrl is undefined we will route to the predefined previous URL.

import { Location } from '@angular/common';
import { filter, Subject, takeUntil } from 'rxjs';
import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';

private previousUrl: string;
private ngUnsubscribe: Subject<any> = new Subject<any>();

constructor(
    private router: Router,
    private activatedRoute: ActivatedRoute,
    private location: Location
) {
    router.events
        .pipe(
            takeUntil(this.ngUnsubscribe),
            filter((event) => event instanceof NavigationEnd)
        )
        .subscribe((event: NavigationEnd) => {
            this.previousUrl = event.url;
        });
}

public async goBack() : Promise<void> {
   if (this.previousUrl) {
     this.location.back();
   } else {
     await this.router.navigate(['..'], {
         relativeTo: this.activatedRoute,
     });
   }
}

ngOnDestroy(): void {
    this.ngUnsubscribe.next(true);
    this.ngUnsubscribe.complete();
}
Prophylaxis answered 18/4, 2023 at 7:57 Comment(0)
G
0

As many of the answers don't take into account all the aspects of back navigation,
I've created a little library that does the trick.

It handles

  1. Browser history
  2. Fallback when clicking on the back button when not routed yet
  3. Customized fallback

Installation

npm install ngx-back-button

import { NgxBackButtonModule, NgxBackButtonService } from 'ngx-back-button'

  imports: [
    NgxBackButtonModule.forRoot(), // Default rootUrl === '/'

    // Or
    NgxBackButtonModule.forRoot({
      rootUrl: '/custom', // Or any custom root url
      fallbackPrefix: '/tabs' // For libraries users
    }),
  ],
  providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: () => () => null,
      deps: [NgxBackButtonService],
      multi: true,
    },
  ]

Then where you need to navigate back

// foo.component.ts
import { NgxBackButtonService } from 'ngx-back-button'

// ...
 constructor(public ngxBackButtonService: NgxBackButtonService) {}

 navigateBack() {
  this.ngxBackButtonService.back()
 }

Also work with a directive

<button ngxBackButton>
  Back
</button>
Gaddy answered 10/8, 2023 at 13:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.