Search code examples
angularangular2-routing

Angular: how to only go back if inside application


Going back to the previous page in Angular is fairly simple:

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

This is the equivalent of hitting your browser's "back" button. But how can this code be modified such that if this._location.back() is going to take you to a url outside of the app, that it instead redirects you to a route inside the app.

For example, say you're at Google.com and then you paste in my-app.com/page-foo and navigate that way. this._location.back() will take you back to Google.com, but I want it to instead navigate to my-app.com/page-bar.


Solution

  • Found the answer at https://nils-mehlhorn.de/posts/angular-navigate-back-previous-page.

    1. Make a new navigation-service:

    import { Injectable } from '@angular/core';
    import { NavigationEnd, Router } from '@angular/router';
    import { Location } from '@angular/common';
    
    @Injectable({
      providedIn: 'root'
    })
    export class NavigationService {
      private MAX_HISTORY_LEN = 10; // prevent history from growing indefinitely
      private history: string[] = [];
    
      constructor(private router: Router, private location: Location) {
        this.router.events.subscribe((event) => {
          if (event instanceof NavigationEnd) {
            this.history.push(event.urlAfterRedirects);
            if (this.history.length > this.MAX_HISTORY_LEN) {
              this.history.shift();
            }
          }
        });
      }
    
      back(): void {
        this.history.pop();
        if (this.history.length > 0) {
          this.location.back();
        } else {
          this.router.navigateByUrl('/');
        }
      }
    }
    

    2. Inject the service into app.component.ts so that it tracks history throughout the application

    export class AppComponent implements AfterViewInit {
    
      constructor(private navigationService: NavigationService) {
      }
      ...
    

    3. Then update the (click) function wherever you want to use this. Using the original example:

    @Component({
      // component's declarations here
    })
    class SomeComponent {
    
      constructor(private navigationService: NavigationService) 
      {}
    
      backClicked() {
        this.navigationService.back();
      }
    }
    

    I made a few adjustments from the blogpost:

    • Added a MAX_HISTORY_LEN to prevent the history array from growing indefinitely throughout app use
    • Injected the navigation-service in app.component.ts so that it's always tracking history. If you only inject the service in the component that's calling .back() then it might not have a history the first time you call it.