Reputation: 2658
In my application, there are multiple links in which I have some links
with the same route
but with different query parameters
.
say, I have links like:
.../deposits-withdrawals
.../deposits-withdrawals?id=1
.../deposits-withdrawals?id=2&num=12321344
When I am in one of the above routes and native to the other route from above mentioned, the route is not changing. Not even any of the functions like ngOnInit
or ngOnChanges
being called.I have changed the parameters from queryParameters
to matrixParameters
but with no success. I have gone through many links and answers. But, none of them solved my problem. Help me how to solve this.
Thank you...
EDIT:
<button routerLink="/deposits-withdrawals" [queryParams]="{ type: 'deposit' ,'productId': selectedBalance.ProductId}" class="wallet-btns">DEPOSIT {{selectedBalance.ProductSymbol}}</button>
<button routerLink="/deposits-withdrawals" [queryParams]="{ type: 'withdrawal' ,'productId': selectedBalance.ProductId }" class="wallet-btns">WITHDRAW {{selectedBalance.ProductSymbol}}</button>
Upvotes: 14
Views: 20258
Reputation: 44336
I suppose you want to resolve a different set of data from the backend using those query parameters and then reinitialize the data inside your component using the new data set from the response.
If that's the case I would suggest to use the following approach:
Make a resolver for your data (implementing the Resolve
interface) and then subscribe to route.data
from your activated route which will re-emit with the new data when route and/or query parameters change if you configured the runGuardsAndResolvers
parameter inside your route config accordingly.
If you didn't use resolvers before read here more on them inside the Angular documentation.
An example on what your route config and your Module
could look like:
type Data = any;
const DATA = new InjectionToken<Resolve<Data>>('Data resolver for my component');
const routes: Routes = [
{
path: 'deposits-withdrawals',
component: MyComponent,
runGuardsAndResolvers: 'pathParamsOrQueryParamsChange',
data: {
// Your static data
},
resolve: {
// Resolvers for your dynamic data that should change on path or query params change
// In your case the source for deposits/withdrawals in your component
// Name your source as how you like it to be accessible as a property in data
source: DATA,
},
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
providers: [
{
provide: DATA,
useFactory: (apiService: ApiService): Resolve<Data> => {
return {
resolve: (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Data> => {
// Here you get your latest query params
const queryParams = route.queryParams;
return apiService.sendRequest(queryParams); // Should return Observable<Data>;
},
},
},
deps: [ApiService],
}
]
})
export class MyModule {}
Inside MyComponent
:
@Component({
selector: 'my-component',
})
class MyComponent {
private subscription: Subscription = new Subscription();
public ngOnInit(): void {
// Subscribe to route data changes
this.subscription.add(
this.route.data.subscribe((data: Data) => {
// The route data observable will re-emit when your resolver runs and returns new data
// Do something with the new data.source inside the subscriber/observer
const source = data.source;
}),
);
}
public ngOnDestroy(): void {
// Unsubscribe to prevent memory leaks.
this.subscription.unsubscribe();
}
}
This is mere a simple example on how you can go about. There are many ways on how to organize this nicely in your code. For example your ApiService
could of course also implement the Resolve
interface directly and you could in that case set the service as a resolver immediately (source: ApiService
).
Don't forget to set the runGuardsAndResolvers
exlicitly to run on query parameters change. By default it will only run on changing path parameters. Read more on that topic in the official Angular documentation here.
Upvotes: 0
Reputation: 2995
Changing parameters usually won't cause an ngOnInit.
If you are navigating to the same page with different parameter, you can listen to events such as NavigationEnd. based on that you will be able to trigger the functions that you want.
import { Router, NavigationEnd } from '@angular/router';
export class AppComponent {
...
constructor(public userService: UserService, router:Router) {
router.events.forEach((event) => {
if(event instanceof NavigationEnd) {
console.log(location.pathname);
}
//NavigationStart
// NavigationEnd
// NavigationCancel
// NavigationError
// RoutesRecognized
});
Upvotes: 0
Reputation: 507
I had this problem once. Can you put some code, or solutions you tried? I'll give you something working for me, but you better give me some more details so that I can help. Supposing we are here : some_url/deposits-withdrawals and we wish to navigate , changing only parameters.
let url = "id=2&num=12321344"
this.router.navigate(['../', url], { relativeTo: this.route });
Hope it helps :/
=================================== EDIT==================================
You have to detect that query parameters have changed. And for that, you may add a listener to queryParameters changings in the constructor of your component. This can be done using your router this way :
constructor(route:ActivatedRoute) {
route.queryParams.subscribe(val => {
// put the code from ngOnInit here
});
}
Adding this listener to detect query parameters changes, means you have to move your code from ngOnInit function to this listener. And every time, you navigate, it will be called.
For navigating, you may use html navigation, or ts navigation. If you want it to be in html, you may use :
<button routerLink="/deposits-withdrawals" [queryParams]="{ type: 'withdrawal' ,'productId': selectedBalance.ProductId }" class="wallet-btns">WITHDRAW {{selectedBalance.ProductSymbol}}</button>
Upvotes: 15
Reputation: 2035
I solved this problem like this.
Suppose you have a container news-list.component.ts
with ngOnInit
. It saves current queryParams
in currentFilters
and if there is not them makes simple GET request else it makes POST request.
ngOnInit() {
this.route.queryParams.subscribe(queryParams => {
if (!!queryParams) {
this.currentFilters = <NewsFilter>{...queryParams, offset: 0, size: 6};
this.news$ = this.newsPostsService.getNewsByFilter(this.currentFilters);
} else {
this.news$ = this.newsPostsService.getMainNews();
}
});
}
Then you create an component <news-rubric></news-rubric>
which has following view. You pass there currentFilters
and take rubricClick
which you process next.
news-list.component.html
<ml-news-rubrics [currentFilters]="currentFilters"
(rubricClicked)="onRubricFilter($event)"
></ml-news-rubrics>
news-list.component.ts
onRubricFilter(filters: NewsFilter) {
this.currentFilters = {...filters};
this.router.navigate([], {queryParams: filters, relativeTo: this.route});
}
And then inside news-rubric.component.ts
you do something like this:
onRubricClicked(rubricId: string) {
// check if filter exists and if not then put ID in filter
if (!this.currentFilters.filterByAnyRubricIds) {
this.putIdInFilter('filterByAnyRubricIds', rubricId, this.currentFilters.filterByAnyRubricIds);
} else {
// check if clicked ID is not in filter. put in filter
if (!this.currentFilters.filterByAnyRubricIds.includes(rubricId)) {
this.putIdInFilter('filterByAnyRubricIds', rubricId, this.currentFilters.filterByAnyRubricIds);
} else {
// if ID in filter remove it from filter
this.removeIdFromFilter('filterByAnyRubricIds', rubricId, this.currentFilters.filterByAnyRubricIds);
}
}
this.rubricClicked.emit(this.currentFilters);
}
There is most tricky code. It makes new filter by updating its key with filtered ID.
private putIdInFilter(key: string, value: any, list: any) {
if (!list || !(list instanceof Array)) {
if (!list) {
this.currentFilters = {...this.currentFilters, [key]: [value]};
} else {
this.currentFilters = {...this.currentFilters, [key]: [this.currentFilters[key], value]};
}
} else {
this.currentFilters = {...this.currentFilters, [key]: [...this.currentFilters[key], value]};
}
}
private removeIdFromFilter(key: string, value: any, list: any) {
if (!list || !(list instanceof Array)) {
this.currentFilters = <NewsFilter>{
...this.currentFilters, [key]: null
};
return;
}
const filteredValues = [...list.filter(i => i !== value)];
if (filteredValues.length > 0) {
this.currentFilters = <NewsFilter>{
...this.currentFilters, [key]: filteredValues
};
} else {
delete this.currentFilters[key];
}
}
And NewsFilter
it is merely interface like QueryParams
with keys which are required to be filtered.
Upvotes: 0
Reputation: 10849
The ngOnInit()
has to be re-invoked when query parameter is updated. This can be achieved as follow:
import { Router } from '@angular/router';
constructor(private router: Router) {
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
}
Upvotes: 11