如何返回上一页

581

在Angular 2中,有没有一种聪明的方法返回上一页?

类似于

this._router.navigate(LASTPAGE);
例如,页面C有一个返回按钮,
  • 从页面A跳转到页面C,点击按钮,返回页面A。

  • 从页面B跳转到页面C,点击按钮,返回页面B。

路由器是否具有此历史信息?
29个回答

1032

实际上,您可以利用内置的位置服务,它拥有一个"后退"API。

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

编辑:正如@charith.arumapperuma所提到的,Location 应该从 @angular/common 导入,因此 import {Location} from '@angular/common'; 这一行很重要。


96
在旧版 Angular 2 中,应该从 "angular2/router" 导入 Location。在新版中,则应该从 "@angular/common" 导入。 - charith.arumapperuma
3
如果框架中已经内置了它,我看不出使用“本地”的“window.history.back();”(这是HTML5的功能)有任何理由。 - Amir Sasson
10
官方的Angular2 API文档中指出:“注意:最好使用Router服务来触发路由更改。仅在需要与路由之外的规范化URL进行交互或创建时使用Location。” @Sasxa的回答 显然展示了使用Router方法实现此功能的方式。但是,使用Location方法肯定更方便。有人知道为什么Router方法可能比Location方法更正确吗? - Andrew Willems
2
@Andrew:我遇到了一个问题,如果你使用this.location.back(),你不能后退两次。你会跳回到最初的那个网站。 - Johannes
1
@yt61,不确定,也许是可重用性?或者如果可以从各种路径到达指定页面,那么你事先不知道返回的路径。 - Amir Sasson
显示剩余13条评论

147

在 Angular 2.x / 4.x 的最终版本中 - 这里是文档 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
  }
}

1
我们是否可以在返回到上一个屏幕时保留输入的值,而不使用服务中的对象? - Vignesh
2
如何在执行location.back()时显示后退动画? - Snowbases

103

<button backButton>返回</button>

你可以将这个代码放在指令里,然后把指令附加到任何可点击元素上:

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

使用方法:

<button backButton>BACK</button>

太棒了! - Rafael de Castro
4
如果您在此页面上进行刷新,并单击触发“this.location.back()”的按钮,它只会触发页面刷新。是否有任何方法可以使Location模块检测到先前的路径是否存在? - Henry
1
请注意,如果用户直接进入一个包含“返回”按钮的页面,并且点击了一个按钮,则根据浏览器(平台)历史记录,他将被扔出应用程序返回到上一页。 - hastrb
对于未来的读者,请查看API文档 - hastrb
太棒了,解决方案非常感谢。 - Caner
我不得不在app.module.ts的声明列表中添加BackButtonDirective才能使其正常工作。 - Asaf M

31

已测试通过 Angular 5.2.9

如果您使用锚点而不是按钮,您必须将其设置为被动链接,并使用href="javascript:void(0)"使Angular Location工作。

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>

我建议创建一个“clickPreventDefault”指令,而不是使用javascript:void(0)。类似这样... `@Directive({ selector: '[clickPreventDefault]' })export class ClickPreventDefaultDirective { @HostListener("click", ["$event"]) onClick($event: Event) { $event.preventDefault(); } }` - bmd
谢谢@bmd,这是更详细的方法,但它也可以工作。另一个可行的解决方案是不使用herf:<a (click)="goBack()">,尽管这种方式不通过HTML验证器。 - JavierFuentes

23

也许您想检查一下历史记录的上一个点是否在您的应用程序内。例如,如果您直接进入您的应用程序并执行location.back()(例如通过工具栏中的<- back按钮),您将回到浏览器的主页,而不是去往您的应用程序中的其他地方。这是我检查这个问题的方法:

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

}

我们检查加载当前路由的导航是否有前一个同级元素。这必须在构造函数中完成,而导航过程仍在活动状态。

但是,这样做并不是没有注意事项:

  • canGoBack将为false,即使前一个位置实际上在我们的应用程序内,但页面已刷新。
  • 用户可能希望通过单击浏览器的后退按钮“返回”到先前的页面(goBack()发生的位置),但由于应用程序回退历史记录而不是推送新位置,因此用户将会更加困惑。

20

您可以在路由类上实现routerOnActivate()方法,它会提供有关先前路由的信息。

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

然后您可以使用router.navigateByUrl()并传递从 ComponentInstruction 生成的数据。例如:

this._router.navigateByUrl(prevInstruction.urlPath);

这对于Angular 2.1.0仍然有效吗? - smartmouse
1
@smartmouse 我不这么认为,routerOnActivate有文档说明。 - Bojan Kogoj
4
这个答案中的routerOnActivate()链接已经失效了。看起来在发布版本中不是这样做的方法。 - rmcsharry

19

在所有这些精彩的答案之后,我希望我的答案能找到某个人并帮助他们解决问题。我编写了一个小服务来跟踪路由历史记录。以下是它。

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

在尝试了几乎所有的解决方案之后,我发现这是更一致的方法。 - A. D'Alfonso
如果我在特定链接上打开页面,我想让它返回到页面树中的页面怎么办? - Ivan
当我调用previousUrl()时,出现了“Type string has no call signatures”错误。你能帮我解决一下吗? :D - Lorand
@SzokeLori 听起来你的“this”指向了String类型。你应该发布一个带有代码的问题。 - Anjil Dhamala
@AnjilDhamala 嗯...我只是将东西注入到构造函数中并想要将其输出到控制台。请注意,我是初学者。 - Lorand
@SzokeLori:我理解你的问题。但是,在你的情境中很难确定出现了什么问题。你是否在模块中注册了这个可注入对象?如果可能的话,请在这个网站上发布你的问题,并将链接放在评论区。如果还没有人帮助你,我会尽力帮助你的。 - Anjil Dhamala

17

当我需要在文件系统中后退时,这也对我有用。 P.S. @angular: "^5.0.0"

<button type="button" class="btn btn-primary" routerLink="../">Back</button>

10
我很希望这能够奏效,但它会返回到上一个路由而不是你在导航到该页面之前所在的路由。知道这个很好,但如果你的组件有多个入口点,那么这种方法只会返回到它上面的路由,而不是你最初来自哪里的路由。 - Scott Byers
当我需要像在文件系统中后退时,我写的是“when I need to move back as in file system” :) 对我来说,这种行为也是意料之外的。 - Shevtsiv Andriy
1
你把“返回”(例如cd -)和“上一级”(cd ..)搞混了。不过,知道这个也很方便。 - devios1
这将返回到父视图(它并不总是等同于上一个视图) - morrigannn

16

我创建了一个按钮,可以在我的应用程序中重复使用。

创建这个组件

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

需要返回按钮时,将其添加到任何模板中即可。

<back-button color="primary"></back-button>
注意:这里使用了Angular Material,如果你没有使用该库,请移除mat-buttoncolor

注:若您未使用 Angular Material 库,则需删除 mat-buttoncolor


这种方法适用于命名路由出口吗?比如说我在页面上有几个出口,只想回到其中一个,这样行得通吗? - rrd
你需要针对那种情况采用不同的方法。如果你在两个不同的路由器出口中都有相同的返回按钮,它们可能会执行相同的操作并返回到最后更改的路由器出口。 - Todd Skelton
对于命名的出口,我发现这种方法可行:this.router.navigate(['../'],{relativeTo:this.route}) - rrd
如何在另一个组件中使用此组件? - RN Kushwaha

11

只需使用 Location ,一个 Angular 服务,应用程序可以使用它与浏览器的 URL 进行交互。

导入它:

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

注入它:
constructor(private location: Location) { }

简单使用它:

goBack() {
    this.location.back(); // Navigates back in the platform's history
}

2
请注意,这种情况存在一个边缘案例,即如果用户打开一个新选项卡,历史记录中就不会有条目可返回。这可能会将用户从Angular应用程序中排除,并引入安全问题,因为没有API可以直接检查浏览器历史记录。 - Ali Celebi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接