【问题标题】:Angular 2 reload route on param change参数更改时的Angular 2重新加载路线
【发布时间】:2016-12-22 15:17:54
【问题描述】:

我目前正在编写我的第一个 Angular 2 应用程序。 我有一个具有以下简单模板的 OverviewComponent:

<div class="row">
  <div class="col-lg-8">
    <router-outlet></router-outlet>
  </div>
  <div class="col-lg-4">
    <app-list></app-list>
  </div>
</div>

当访问 url / 时,我的路由器将我重定向到 /overview,然后在路由器插座中加载地图。 &lt;app-list&gt; 有一个可点击项目列表,触发 &lt;app-detail&gt; 显示而不是应用程序组件。因此,我在 url 中传递引用 json 文件的 id,如下所示:/details/:id(在我的路线中)。

上述所有工作都很好。如果我现在单击其中一个列表项,则会显示详细信息,但是当我选择另一个列表元素时,视图不会更改为新的详细信息。 URL 确实发生了变化,但内容没有重新加载。如何实现 DetailComponent 的重新初始化?

【问题讨论】:

  • @Peter Salomonsen 的答案似乎完全符合需要检查一下!

标签: angular typescript angular2-routing


【解决方案1】:

目前不直接支持。另见https://github.com/angular/angular/issues/9811

你可以做的事情是这样的

<div *ngIf="doShow" class="row">
  <div class="col-lg-8">
    <router-outlet></router-outlet>
  </div>
  <div class="col-lg-4">
    <app-list></app-list>
  </div>
</div>
doShow:boolean: true;

constructor(private _activatedRoute: ActivatedRoute, private _router:Router, private cdRef:ChangeDetectorRef) {
  _router.routerState.queryParams.subscribe(
    data => {
      console.log('queryParams', data['st']); 
      this.doShow = false;
      this.cdRef.detectChanges();
      this.doShow = true;
  });
}

(未测试)

【讨论】:

  • 感谢您的快速回复,但我以不同的方式解决了问题,请参阅我的回答。
【解决方案2】:

我不知道这个问题是否有类似于我将在这里提出的问题的答案,所以我还是会这样做:

我设法通过以下方式实现了“假”重新加载。

我所做的基本上是创建一个组件,它将我重定向到我想要使用的“真实”组件:

@Component({
  selector: 'camps-fake',
  template: ''
})
export class FakeComponent implements OnInit {

  constructor(private _router:Router,
              private _route:ActivatedRoute)
  { }

  ngOnInit() {
    let id:number = -1;
    this._route.params.forEach((params:Params) => {
      id = +params['id'];
    });

    let link:any[] = ['/details', id];
    this._router.navigate(link);
  }

}

因此,通过选择一个列表项,路由器将导航到 /fake/:id,它只是从 URL 中提取 id 并导航到“真实”组件。

我知道可能有一种更简单或更奇特的方法,但我认为这种解决方案效果很好,因为假货并没有真正引起注意。只是页面重新加载时的“闪烁”是一个负面方面,但就我的 css 知识而言,可能会有一些过渡来涵盖这一点。

【讨论】:

  • 嗯,非常感谢这个提示!很遗憾 Angular 在某些事件中默认不支持这一点。
  • 已经6个月了,有什么消息吗?我目前正在使用您的方法,但它对 seo 不友好。
  • @RaymondtheDeveloper 我不知道。我今天又遇到了这个问题,又给了谷歌一个完全相同的结果。
  • @YoannFleuryDev Angular 是开源的,这意味着我们都感到羞耻 :)
  • 这适用于前几个问题集,但不是很棱角分明。看看@gpanagopoulos 的建议,这将是现在正式接受的解决方案。
【解决方案3】:

我通过事件解决了,如果子组件发送一个新链接,并发出一个事件,那么父组件可以找到变化并调用一些重新加载函数,这将重新加载必要的数据。 另一种选择是订阅route parameters, and found when it change 但我确实认为来自 angular2 的人应该考虑向 router.navigate 函数添加参数,这可以强制重新加载。 (forceReload=true)

【讨论】:

    【解决方案4】:

    根据第一个最终版本,此问题已得到解决。

    只要注意参数变化时正确重置组件的状态

    this.route.params.subscribe(params => {
        this.param = params['yourParam'];
        this.initialiseState(); // reset and set based on new parameter this time
    });
    

    【讨论】:

    • 虽然这是一个建议的解决方案,但我遇到的问题是我的组件(实现了上述代码)在路由更改后被加载,因此这个承诺中的代码没有被解雇
    • 这是迄今为止更优雅和棱角分明的解决方案,尽管我宁愿将resetComponentState() 替换为更通用的initialize 函数,该函数在创建组件或更改参数时被调用。
    • 有效点@hGen。我将其更改为初始化以表明它也是第一次加载时调用的那个。
    • 不,这是为给定参数设置组件状态的自定义方法
    • 'route' 指的是ActivatedRoute的一个实例
    【解决方案5】:

    可以检测接收到的参数的任何变化,在我的情况下,我使用 Resolve 加载信息,所以我不需要参数(仅检测它是否发生变化)。这是我的最终解决方案:

    public product: Product;
    private parametersObservable: any;
    
    constructor(private route: ActivatedRoute) {
    }
    
    ngOnInit() {
      this.parametersObservable = this.route.params.subscribe(params => {
        //"product" is obtained from 'ProductResolver'
        this.product = this.route.snapshot.data['product'];
      });
    }
    
    //Don't forget to unsubscribe from the Observable
    ngOnDestroy() {
      if(this.parametersObservable != null) {
        this.parametersObservable.unsubscribe();
      }
    }
    

    【讨论】:

    • 我认为不需要取消订阅,因为路由器管理它提供的可观察对象并本地化订阅。当组件被销毁时,它们会被清理。见:angular.io/tutorial/toh-pt5#do-you-need-to-unsubscribe
    • 我同意,但不退订任何订阅总是让我感到紧张。
    【解决方案6】:

    此处应添加的另一个替代方法是为您的模块提供RouteReuseStrategy

    providers: [
      {
        provide: RouteReuseStrategy,
        useClass: AARouteReuseStrategy
      }
    ]
    

    如果配置相同,路由器的默认行为是重用路由(在此问题中仅更改 :id 参数时就是这种情况)。通过将策略更改为不重用路由,组件将被重新加载,您不必订阅组件中的路由更改。

    RouteReuseStrategy 的实现可能是这样的:

    export class AARouteReuseStrategy extends RouteReuseStrategy {
      shouldDetach(route: ActivatedRouteSnapshot): boolean {
        return false;
      }
      store(route: ActivatedRouteSnapshot, handle: {}): void {
    
      }
      shouldAttach(route: ActivatedRouteSnapshot): boolean {
        return false;
      }
      retrieve(route: ActivatedRouteSnapshot): {} {
         return null;
     }
     shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
       return false; // default is true if configuration of current and future route are the same
     }
    }
    

    我也在这里写过一些:

    https://pjsjava.blogspot.no/2018/01/angular-components-not-reloading-on.html

    【讨论】:

    • 这应该是一个可以接受的答案,因为它不需要更改组件行为并且可以全局定义。
    【解决方案7】:

    您可以直接在组件级别更改 routeReuseStrategy:

    constructor(private router: Router) {
    
          // force route reload whenever params change;
          this.router.routeReuseStrategy.shouldReuseRoute = () => false;
    
    }
    

    同样,重用策略可以全局更改。

    这不一定直接解决您的问题,但看看这个问题是如何成为“angular 2 reload url if query params change”的第一个搜索结果,它可能会避免下一个人在 github 上进行挖掘问题。

    【讨论】:

    • 对此表示感谢。我将补充一点,在全局添加此更改后,您应该真的检查您的所有路线是否都正常,因为我的一条路线不再起作用了。
    • 在搜索了一段时间的修复后对我使用 angular 7 有效。谢谢。
    • 小心,这种方法破坏了 routerLinkActive 指令。
    • 是的,我担心这会造成什么影响?我们是否只为这个组件覆盖它?还是我们为其他所有事情都这样做?
    • 这是覆盖全局的路由重用策略,而不仅仅是针对当前组件 - 小心这种方法。
    【解决方案8】:
    this.route.paramMap.subscribe(params => {
      //fetch your new parameters here, on which you are switching the routes and call ngOnInit()
      this.ngOnInit();
     });
    

    您只需要从 paramMap 内部调用 ngOnInit(),它就会使用新加载的数据初始化整个页面。

    【讨论】:

    • 最简单、最不容易出错且最可靠的答案。无需额外管理新订阅并考虑(并可能重构)您可能与路由器/模块挂钩的其他机制(在我的情况下这实际上可能是一件大事)。谢谢!附言routeReuseStrategy.shouldReuseRoute -&gt; false 对我不起作用
    【解决方案9】:

    希望这会有所帮助。

    constructor(private router: Router){
     // override the route reuse strategy
    
     this.router.routeReuseStrategy.shouldReuseRoute = function(){
        return false;
     }
    
     this.router.events.subscribe((evt) => {
        if (evt instanceof NavigationEnd) {
           // trick the Router into believing it's last link wasn't previously loaded
           this.router.navigated = false;
           // if you need to scroll back to top, here is the right place
           window.scrollTo(0, 0);
        }
    });
    
    }
    

    【讨论】:

      【解决方案10】:

      在您的 Angular 7 项目中导入路由器。

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

      创建路由器对象

      constructor(private router: Router) {
      
      }
      

      使用routeReuseStrategy检测路由器参数变化

      ngOnInit() {
          this.router.routeReuseStrategy.shouldReuseRoute = () => {
            // do your task for before route
      
            return false;
          }
      }
      

      【讨论】:

        【解决方案11】:

        在你的constructor()上使用这个

        this.router.routeReuseStrategy.shouldReuseRoute = () => false;
        

        【讨论】:

          【解决方案12】:

          我没有发现这些对于 Angular 8 来说是一个好的和彻底的解决方案。一些建议最终创建了无限循环,导致,咳咳,堆栈溢出。其他人对我的口味来说太老套了。我在网上找到了一个很好的解决方案,但是由于我不能在这里只发布一个链接,所以我会尽力根据链接总结我所做的事情以及为什么我认为这是一个可靠的解决方案。它允许您只影响需要行为的某些路由而不影响其他路由,并且您不需要滚动任何自定义类来使其工作。

          来自 Simon McClive 的解决方案 https://medium.com/engineering-on-the-incline/reloading-current-route-on-click-angular-5-1a1bfc740ab2

          首先,修改你的应用路由模块配置:

          @ngModule({ imports: [RouterModule.forRoot(routes, {onSameUrlNavigation: ‘reload’})],
          exports: [RouterModule] })
          

          接下来,修改您想要影响的路线。如果您不使用身份验证,则可以省略 canActivate 参数:

          export const routes: Routes = [
           {
             path: ‘invites’,
             component: InviteComponent,
             children: [
               {
                 path: ‘’,
                 loadChildren: ‘./pages/invites/invites.module#InvitesModule’,
               },
             ],
             canActivate: [AuthenticationGuard],
             runGuardsAndResolvers: ‘always’, //there are three options for this - see Simon's post. 'Always' is the heaviest-handed and maybe more than you need.
           }
          ]
          

          最后,更新你的类以监听导航事件并采取相应的行动(确保在退出时取消注册监听器):

          export class AwesomeComponent implements OnInit, OnDestroy{
          
           // ... your class variables here
           navigationSubscription;
          
           constructor( private router: Router ) {
          
             // subscribe to the router events and store the subscription so
             // we can unsubscribe later
          
             this.navigationSubscription = this.router.events.subscribe((e: any) => {
               // If it is a NavigationEnd event, re-initalize the component
               if (e instanceof NavigationEnd) {
                 this.myInitFn();
               }
             });
           }
          
           myInitFn() {
             // Reset anything affected by a change in route params
             // Fetch data, call services, etc.
           }
          
           ngOnDestroy() {
              // avoid memory leaks here by cleaning up
              if (this.navigationSubscription) {  
                 this.navigationSubscription.unsubscribe();
              }
            }
          }
          

          【讨论】:

            【解决方案13】:

            一种惯用方法是在模板中使用 Observables 和 | asyc 管道。

            (取自https://medium.com/@juliapassynkova/angular-2-component-reuse-strategy-9f3ddfab23f5 - 阅读更多了解详情 )

            import {Component, OnInit} from '@angular/core';
            import {ActivatedRoute} from '@angular/router';
            import {Observable} from 'rxjs/Observable';
            import 'rxjs/add/operator/pluck';
            
            @Component({
              selector: 'app-detail-reusable',
              template: `<p>detail reusable for {{id$| async}} param </p>`
            })
            export class DetailReusableComponent implements OnInit {
              id$: Observable<string>;
            
              constructor(private route: ActivatedRoute) {
              }
            
              ngOnInit() {
                this.id$ = this.route.params.pluck('id');
              }
            }
            
            

            如果您要从 REST api 获取更多详细信息,您可以使用 switchMap 例如:

            import {Component, OnInit} from '@angular/core';
            import {ActivatedRoute} from '@angular/router';
            import {Observable} from 'rxjs/Observable';
            import 'rxjs/add/operator/pluck';
            
            @Component({
              selector: 'app-detail-reusable',
              template: `<ul><li *ngFor="let item of items$ | async">{{ item.name }}</li></ul>`
            })
            export class DetailReusableComponent implements OnInit {
              items$: Observable<string[]>;
            
              constructor(private route: ActivatedRoute) {
              }
            
              ngOnInit() {
                this.items$ = this.route.params.pipe(
                  pluck("id"),
                  switchMap(id => this.http.get<string[]>(`api/items/${id}`))  // or whatever the actual object type is
                );
              }
            }
            
            

            | async 管道将自动订阅,id$items$ observable 将在路由参数更改触发 API 数据获取(在 items$ 情况下)并更新视图时更新。

            【讨论】:

              猜你喜欢
              • 2017-05-14
              • 2016-06-07
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-01-17
              • 1970-01-01
              • 2018-10-14
              相关资源
              最近更新 更多