ChatGPT解决这个技术问题 Extra ChatGPT

How to go back last page

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?


K
Kishan Vaishnav

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.


The Location should be imported from "angular2/router" in older releases of Angular 2. In newer releases, it should be from "@angular/common".
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)
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?
@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.
@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.
D
Daniel Gray

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

While navigating back to previous screen can we retain input entered values without using a object in service.
How to show back animation while location.back() is executing?
N
Nikita Fedyashev

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>

that's awesome!
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?
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.
For future readers, look at API docs
awesome solution thanks.
J
JavierFuentes

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>

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(); } }
Thank you @bmd , it's a more elaborate way but it works too. Another working solution is don't use herf: although this way don't pass HTML Validators.
B
Bhargav Rao

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

Is this still valid for Angular 2.1.0?
@smartmouse I don't think so, there is documentation for routerOnActivate
The routerOnActivate() link in this answer is broken. Seems this is not the way to do it in release version.
A
Anjil Dhamala

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

After trying more or less all solutions, I find this one is the more consistent way to do that
What if I open the page on particular link and I want that it will go back to page that in page tree?
Iḿ getting error Type string has no call signatures when calling preiousUrl(). Can you help me out? :D
@SzokeLori Sounds like your "this" is pointing to String type. You should post a question with the code.
@AnjilDhamala well... i just injected the thing as private to constructor and wanted to console log it. Mind me, im a beginner
S
Shevtsiv Andriy

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>

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.
As I write "when I need to move back as in file system" :) For me, this behavior was also unexpected.
You're confusing back (e.g. cd -) with up (cd ..). Nevertheless, it's handy to know this works.
This returns to the parent view (it does not always equate to the previous view)
T
Todd Skelton

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.


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?
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.
For named outlets, I found that this approach worked: this.router.navigate(['../'], {relativeTo: this.route})
How to use this component inside another component?
P
Parziphal

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.


P
Puneeth Rai

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.


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!
for me it is working fine with router itself even in angular 4
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.
D
Derlin

In RC4:

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

This is a complement to an answer but not an answer itself
A
Albert

Since beta 18:

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


D
Džan Operta

Another solution

window.history.back();


Works for me location.back() also works but not I compile with --prod
N
Naeem Bashir

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

j
jprism

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

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

and

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

just unfortunate that the answer was already given 5 years earlier, and even misses the part where you define location in the constructor
Yes, don't forget to define in your constructor() the: private location: Location
N
Niels Prins

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);
  }

}

G
Gal Margalit

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.


S
Sangwin Gawande

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
}

M
Mohammad Reza

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();
  }
}

Y
Yuriy Gyerts

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();
  });
});

R
Rojin Gharooni

try routerLink="../LASTPAGE"