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?
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.
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
}
}
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>
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>
javascript:void(0)
. Something like... @Directive({ selector: '[clickPreventDefault]' }) export class ClickPreventDefaultDirective { @HostListener("click", ["$event"]) onClick($event: Event) { $event.preventDefault(); } }
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);
routerOnActivate
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;
}
}
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>
cd -
) with up (cd ..
). Nevertheless, it's handy to know this works.
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
.
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.
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.
In RC4:
import {Location} from '@angular/common';
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>
Location
service instead. official API
Since beta 18:
import {Location} from 'angular2/platform/common';
Another solution
window.history.back();
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
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
}
}
Please make sure you explicitly import if you are using latest Angular/TypeScript
import { Location } from '@angular/common';
and
onCancel() {
this.location.back();
}
location
in the constructor
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);
}
}
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.
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
}
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();
}
}
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();
});
});
try routerLink="../LASTPAGE"
Success story sharing
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 useRouter
to do this. However, theLocation
method definitely is more convenient. Does anyone know why theRouter
method might be more correct than theLocation
method?