参数变化时角2重载路径

我目前正在写我的第一个角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>

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

以上所有的工作完全没问题。如果我现在点击其中一个列表项目的细节显示,但当我选择另一个列表元素的视图不会改变到新的细节。URL 确实发生了变化,但是内容没有重新加载。如何实现 DetailComponent 的重新初始化?

116558 次浏览

This is currently not directly supported. See also https://github.com/angular/angular/issues/9811

What you can do is something like

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

(not tested)

I don't know whether there is an answer to the problem similar to the one I'm going to propose right here, so I'll do it anyway:

I managed to achieve a 'fake' reload the following way.

What I basically did is creating a Component which redirects me to the 'real' component I want to use:

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


}

So by selecting a list item the router will navigate to /fake/:id which just extracts the id from the URL and navigates to the 'real' component.

I know there might be an easier, or more fancy way, but I think this solution works pretty well since the fake doesn't really attract attention. Just the 'flashing' when the page reloads is a negative aspect but as far as my css knowledge reaches there might be some transition to cover that.

I solved it by using event, if the child component send a new link, and emit an event, then the parent can find the change and call to some reload function, which will reload the necessary data. Another option is to subscribe to the route parameters, and found when it change But I do think that the guys from angular2 should think about adding parameters to the router.navigate function, which can force the reload. (forceReload=true)

As per the first final release, this is resolved.

Just pay much attention to correctly reset the state of the component when the parameter changes

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

Is possible to detect any change in the parameters received, in my case I load the information using a Resolve so I don't need the parameter (only detect if it change). This is my final solution:

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

Another alternative that should be added here is to provide a RouteReuseStrategy to your module.

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

The default behaviour of the router is to reuse the route if the configuration is the same (which is the case when only changing the :id param in this question). By changing the strategy to not reuse the route, the component will be reloaded, and you do not have to subscribe to route changes in the component.

An implementation of the RouteReuseStrategy could be like this:

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

I've written some about it here too:

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

You can change the routeReuseStrategy directly at the component level:

constructor(private router: Router) {


// force route reload whenever params change;
this.router.routeReuseStrategy.shouldReuseRoute = () => false;


}

Likewise, the reuse strategy can be changed globally.

This does not necessarily address your problem directly but seeing how this question is the first search result for "angular 2 reload url if query params change" it might save the next person from digging through the github issues.

this.route.paramMap.subscribe(params => {
//fetch your new parameters here, on which you are switching the routes and call ngOnInit()
this.ngOnInit();
});

You just need to call ngOnInit() from inside the paramMap and it will initialise the whole page with newly loaded data.

hope this will help.

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


}

Import Router in your Angular 7 project.

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

Create an object of the Router

constructor(private router: Router) {


}

Use routeReuseStrategy to detect router parameter change

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


return false;
}
}

Use this on your constructor()

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

I didn't find that any of these was a good and thorough solution for Angular 8. Some suggestions ended up creating infinite loops that resulted in, ahem, a stack overflow. Others are just too hacky for my tastes. I found a good solution online but since I can't post just a link here, I'll do the best to summarize what I did based on the link and why I think it's a solid solution. It allows you to just affect certain routes that need the behavior and not others and you don't need to roll any custom classes to make it work.

From Simon McClive's solution at https://medium.com/engineering-on-the-incline/reloading-current-route-on-click-angular-5-1a1bfc740ab2

First, modify your app-routing module configuration:

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

Next, modify the routes you want to affect. If you aren't using authentication you can omit the canActivate parameter:

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.
}
]

Lastly, update your class to listen for the navigation event and act accordingly (being sure to unregister the listener on the way out):

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

An idiomatic approach could be to use Observables and the | asyc pipe in your template.

(taken from https://medium.com/@juliapassynkova/angular-2-component-reuse-strategy-9f3ddfab23f5 - read more for details )

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


If you are fetching further details from a REST api, you can use switchMap for example:

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


The | async pipe will automatically subscribe and the id$ or items$ observable will update when the route parameter changes triggering an API data fetch (in the items$ case) and updating the view.

Put this on the constructor was by far the best solution

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

However as it update the behavior of the reuse strategy for the whole app ! I tried several other approach to make a clean fix for this without having to reinvent the wheel.

The conclusion is below your eyes you just need to set the value to true when destroying the component

ngOnDestroy() {
this.router.routeReuseStrategy.shouldReuseRoute = () => true;
}