路由导航到同一页面时,ngOnInit不会被调用

146

我正在同一页上使用一些查询字符串参数调用router.navigate。在这种情况下,ngOnInit()不会被调用。这是默认行为还是我需要添加其他内容?

16个回答

195

您可以注入 ActivatedRoute 并订阅 params

constructor(route:ActivatedRoute) {
  route.params.subscribe(val => {
    // put the code from `ngOnInit` here
  });
}

仅当路由导航到不同的路由时,路由器才会销毁并重新创建组件。当仅更新路由参数或查询参数但路由相同时,组件不会被销毁和重新创建。

强制重新创建组件的另一种方法是使用自定义重用策略。另请参见Angular2 router 2.0.0 not reloading components when same url loaded with different parameters?(目前似乎没有太多可用的实现信息)


2
此订阅不起作用,它只会在初始化时触发。 - Osanda Wedamulla
@OsandaWedamulla 那就是针对你的代码特定情况了。不过如果没有更多细节,很难判断。 - Günter Zöchbauer
1
你不需要取消订阅吗?还是这个会自动完成? - rain01
1
@rain01 通常情况下,当你明确地订阅时,你应该在代码中明确地取消订阅。如果你的根组件中有以上代码,则取消订阅是多余的,因为根组件的生命周期通常与整个 Angular 应用程序的生命周期相同。 - Günter Zöchbauer
当与 https://dev59.com/OlgR5IYBdhLWcg3wJqi7#47827668(或全局设置 onSameUrlNavigation)相结合时,此解决方案有效。 - S. Roose
@S.Roose 对我来说不起作用.. :( - Raphael St

138

您可以在路由器上调整reuseStrategy。

constructor(private router: Router) {
    // override the route reuse strategy
    this.router.routeReuseStrategy.shouldReuseRoute = function() {
        return false;
    };
}

7
这将启动页面上每个组件的ngInit。 - jforjs
3
好的,@Pascal,你让我不得不登录SO来给你的答案点赞。我想补充一下,在Angular 6中,你还必须像这样向配置对象添加onSameUrlNavigation: 'reload'RouterModule.forRoot(appRoutes, {onSameUrlNavigation: 'reload'})。不过我不能保证其他版本的Angular也是如此。 - Hildy
2
@Hildy,我也被迫这样做了 :) 感谢@Pascal!如果你喜欢lambda,你可以这样做:this.router.routeReuseStrategy.shouldReuseRoute = () => false; - nick
1
@Swaprks 我创建了一个 routing.module.ts 文件,并将代码放在了构造函数中。 - Pascal
2
@Swaprks.. 我有类似的问题,想尝试您上面的答案。但对于这个领域的初学者来说,它并没有告诉我太多。我应该把这段代码放在代码的哪个位置?我需要更改代码片段中的任何内容(例如 function())吗?如果您创建了一个名为 routing.module.ts 的新文件,它应该如何与其他文件交互?还有一个名为 app-routing.module.ts 的文件是自动创建的。 - edn
显示剩余5条评论

41

Angular 9

我使用过以下内容,它有效。

onButtonClick() {
    this.router.routeReuseStrategy.shouldReuseRoute = function () {
        return false;
    }
    this.router.onSameUrlNavigation = 'reload';
    this.router.navigate('/myroute', { queryParams: { index: 1 } });
}

2
此外,还可以使用lambda表达式替代上述代码:this.router.routeReuseStrategy.shouldReuseRoute = () => false; - Dhwanil Patel
适用于Angular 8。 - Kishan Vaishnav
17
这会改变应用程序中的路由器行为,还是只会针对此特定的 navigate() 触发一次? - Halfist
适用于 Angular 9。 - Ilija Iličić
2
@Halfist 我观察到的是,设置 shouldReuseRoute 后,它将为整个应用程序设置。我通过在页面 1 上使用 console.log(this.router.routeReuseStrategy),将策略设置为页面 2 上的该函数,然后导航回页面 1 来检查这一点。在初始加载页面 1 时,报告没有函数,但在导航到页面 2 并返回后,该函数仍然存在。如果您只想在一个组件中使用它,也许可以在 NgOnDestroy 中重新设置策略? - S. ten Brinke
5
小心!当该代码执行时,它将永久性地更改路由器的行为。这意味着,例如,如果您使用router.navigate()导航到相同的URL,它将再次触发ngOnInit - Caveman

11

你是否需要重新加载页面?这是我的解决方案:我改变了@NgModule(在我的情况下是app-routing.module.ts文件):

@NgModule({
  imports: [RouterModule.forRoot(routes, {onSameUrlNavigation: 'reload'})] })

这里的“routes”是什么? - Dhwanil Patel
@DhwanilPatel,你的Angular应用程序路由。例如:"const routes: Routes = [ { path: 'crisis-center', component: CrisisListComponent }, { path: 'heroes', component: HeroListComponent }, ]" https://angular.io/guide/router#register-router-and-routes - Dionis Oros

7

在您的导航方法中,

this.router.routeReuseStrategy.shouldReuseRoute = () => false;
this.router.onSameUrlNavigation = 'reload';
this.router.navigate(['/document'], {queryParams: {"search": currentSearch}});

2
这会破坏其余的导航模型。 - Shaybc
2
小心!当该代码执行时,它将永久性地更改路由器的行为。这意味着,例如,如果您使用router.navigate()到相同的URL,它将再次触发ngOnInit - Caveman
是的,开发人员必须相应地管理相关事项。 - Dhwanil Patel

5

这是一个包含更多信息的最佳想法集合

解决方案1 - 使用params订阅:

教程:https://angular-2-training-book.rangle.io/routing/routeparams#reading-route-parameters

文档:https://angular.io/api/router/ActivatedRoute#params

在使用参数变量的每个路由组件中,都应该包含以下内容:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs';

// ...

@Component({
    // ...
})
export class MyComponent implements OnInit, OnDestroy {
    paramsSub: Subscription;

    // ...

    constructor(activeRoute: ActivatedRoute) {

    }

    public ngOnInit(): void {
        // ...
        this.paramsSub = this.activeRoute.params.subscribe(val => {
            // Handle param values here
        });

        // ...
    }

    // ...

    public ngOnDestroy(): void {
        // Prevent memory leaks
        this.paramsSub.unsubscribe();
    }
}

这段代码常见的问题是订阅是异步的,可能会更加棘手。此外,在 ngOnDestroy 中不能忘记取消订阅,否则会发生糟糕的事情。

好消息是,这是处理此问题的最常见和最有文档记录的方法。使用此方法还有一个性能提升,因为您可以重用模板,而不是每次访问页面时销毁并重新创建。

解决方案2 - shouldReuseRoute / onSameUrlNavigation:

文档: https://angular.io/api/router/ExtraOptions#onSameUrlNavigation

文档: https://angular.io/api/router/RouteReuseStrategy#shouldReuseRoute

文档: https://angular.io/api/router/ActivatedRouteSnapshot#params

找到项目中 RouterModule.forRoot 的位置(通常在 app-routing.module.ts 或 app.module.ts 中):

const routes: Routes = [
   // ...
];

// ...

@NgModule({
    imports: [RouterModule.forRoot(routes, {
        onSameUrlNavigation: 'reload'
    })],
    exports: [RouterModule]
})

然后在AppComponent中添加以下内容:

import { Component, OnInit} from '@angular/core';
import { Router } from '@angular/router';

// ...
@Component({
    // ...
})
export class AppComponent implements OnInit {
    constructor(private router: Router) {
    }

    ngOnInit() {
        // Allows for ngOnInit to be called on routing to the same routing Component since we will never reuse a route
        this.router.routeReuseStrategy.shouldReuseRoute = function() {
            return false;
        };

        // ...
    }

    // ...
}


最后,在您的路由组件中,您现在可以像这样处理参数变量:
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

// ...

@Component({
    // ...
})
export class MyComponent implements OnInit {
    // ...

    constructor(activeRoute: ActivatedRoute) {

    }

    public ngOnInit(): void {
        // Handle params
        const params = +this.activeRoute.snapshot.params;

        // ...
    }

    // ...
}

这种解决方案的常见问题是它并不常见。另外,您正在改变 Angular 框架的默认行为,因此可能会遇到其他人通常不会遇到的问题。

好处在于所有代码都是同步的,更容易理解。


1

NgOnInit 会在实例创建时被调用一次。对于同一个实例,NgOnInit 不会再次被调用。如果想要调用它,需要销毁已创建的实例。


1

我曾经遇到过同样的问题,此外,我还收到了警告:

did you forget to call `ngZone.run()`

这个 网站提供了最好的解决方案:

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

...

  constructor(
    private ngZone:NgZone,
    private _router: Router
  ){ }

  redirect(to) {
    // call with ngZone, so that ngOnOnit of component is called
    this.ngZone.run(()=>this._router.navigate([to]));
  }

关于这一点,我想知道如何处理当您使用新路由刷新页面时出现的问题。 在这种情况下,NgZone警告仍将存在。 - Siddhant

1

解决方案可能是订阅您的路由器事件。

导入RouterNavigationEnd

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

在构造函数中初始化路由器。

constructor(router: Router) {

在构造函数中订阅路由器事件。
        this.router.events.subscribe((ev) => {
          if (ev instanceof NavigationEnd) {
            //do something
          }
        });

我发现开发人员有时需要每个router.navigate的通用代码,如果在app.component.ts或任何共享组件中,这可能是答案。

0
创建路由数组中同一组件的不同路径。
const routes: Routes = [{ path: "app", component: MyComponent }, { path: "app-reload", component: MyComponent } ];
如果当前URL是“app”,则导航到“app-reload”,反之亦然。

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