I have routes defined like:
[
{
path: '',
component: MyComponent,
},
{
path ':id',
component: MyComponent,
},
...
So I can go to /foo or /foo/123
The component knows to fetch that specific id or to fetch a collection of all records whether or not the id parameter exists..
The problem is, I have a link that does:
<a routerLink="/foo">foo!</a>
If I type into the url bar /foo I see the list of records, and if I type /foo/123 it also works correctly and loads that specific record. However, if I am on /foo/123 and then click that link I see the url change to /foo but it does nothing, does not re-render the component, so the it is still showing the 123 record, rather than the list of records as it should...
How can I force it to do a reload?
I tried putting runGuardsAndResolvers: 'always' in the route, I also tried {onSameUrlNavigation: ‘reload’} on the app level forRoot route config options, and also tried the incredibly dumb hack I've seen multiple people talk about with adding a queryParam of a timestamp on the <a> tag... None of those things worked.
You can do it by subscribing to the route params, and whenever it emits the new value, you can re-initialize your component data.
import { ActivatedRoute } from '#angular/router';
import { Subscription } from 'rxjs';
...
private subs: Subscription[] = [];
constructor(private route: ActivatedRoute) {}
ngOnInit(): void {
this.subs.push(
this.route.params.subscribe({
next: (response) => {
const id = response['id'];
this.initializeData();
},
error: (errorResponse) => {
console.log('ERROR: ', errorResponse);
},
})
);
}
ngOnDestroy() {
this.subs.forEach((s) => s?.unsubscribe());
}
initializeData() {
// Do something
}
I am trying to update (add, remove) queryParams from a component. In angularJS, it used to be possible thanks to :
$location.search('f', 'filters[]'); // setter
$location.search()['filters[]']; // getter
I have an app with a list that the user can filter, order, etc and I would like to set in the queryParams of the url all the filters activated so he can copy/paste the url or share it with someone else.
However, I don't want my page to be reloaded each time a filter is selected.
Is this doable with the new router?
You can navigate to the current route with new query params, which will not reload your page, but will update query params.
Something like (in the component):
import {ActivatedRoute, Router} from '#angular/router';
constructor(
private router: Router,
private activatedRoute: ActivatedRoute,
) { }
public myMethodChangingQueryParams() {
const queryParams: Params = { myParam: 'myNewValue' };
this.router.navigate(
[],
{
relativeTo: activatedRoute,
queryParams: queryParams,
queryParamsHandling: 'merge', // remove to replace all query params by provided
});
}
Note, that whereas it won't reload the page, it will push a new entry to the browser's history. If you want to replace it in the history instead of adding new value there, you could use { queryParams: queryParams, replaceUrl: true }.
EDIT:
As already pointed out in the comments, [] and the relativeTo property was missing in my original example, so it could have changed the route as well, not just query params. The proper this.router.navigate usage will be in this case:
this.router.navigate(
[],
{
relativeTo: this.activatedRoute,
queryParams: { myParam: 'myNewValue' },
queryParamsHandling: 'merge'
});
Setting the new parameter value to null will remove the param from the URL.
#Radosław Roszkowiak's answer is almost right except that relativeTo: this.route is required as below:
constructor(
private router: Router,
private route: ActivatedRoute,
) {}
changeQuery() {
this.router.navigate(['.'], { relativeTo: this.route, queryParams: { ... }});
}
In Angular 5 you can easily obtain and modify a copy of the urlTree by parsing the current url. This will include query params and fragments.
let urlTree = this.router.parseUrl(this.router.url);
urlTree.queryParams['newParamKey'] = 'newValue';
this.router.navigateByUrl(urlTree);
The "correct way" to modify a query parameter is probably with the
createUrlTree like below which creates a new UrlTree from the current while letting us modify it using NavigationExtras.
import { Router } from '#angular/router';
constructor(private router: Router) { }
appendAQueryParam() {
const urlTree = this.router.createUrlTree([], {
queryParams: { newParamKey: 'newValue' },
queryParamsHandling: "merge",
preserveFragment: true });
this.router.navigateByUrl(urlTree);
}
In order to remove a query parameter this way you can set it to undefined or null.
The answer with most vote partially worked for me. The browser url stayed the same but my routerLinkActive was not longer working after navigation.
My solution was to use lotation.go:
import { Component } from "#angular/core";
import { Location } from "#angular/common";
import { HttpParams } from "#angular/common/http";
export class whateverComponent {
constructor(private readonly location: Location, private readonly router: Router) {}
addQueryString() {
const params = new HttpParams();
params.append("param1", "value1");
params.append("param2", "value2");
this.location.go(this.router.url.split("?")[0], params.toString());
}
}
I used HttpParams to build the query string since I was already using it to send information with httpClient. but you can just build it yourself.
and the this._router.url.split("?")[0], is to remove all previous query string from current url.
Try
this.router.navigate([], {
queryParams: {
query: value
}
});
will work for same route navigation other than single quotes.
If you want to change query params without change the route. see below
example might help you:
current route is : /search
& Target route is(without reload page) : /search?query=love
submit(value: string) {
this.router.navigate( ['.'], { queryParams: { query: value } })
.then(_ => this.search(q));
}
search(keyword:any) {
//do some activity using }
please note : you can use this.router.navigate( ['search'] instead of this.router.navigate( ['.']
I ended up combining urlTree with location.go
const urlTree = this.router.createUrlTree([], {
relativeTo: this.route,
queryParams: {
newParam: myNewParam,
},
queryParamsHandling: 'merge',
});
this.location.go(urlTree.toString());
Not sure if toString can cause problems, but unfortunately location.go, seems to be string based.
Better yet - just HTML:
<a [routerLink]="[]" [queryParams]="{key: 'value'}">Your Query Params Link</a>
Note the empty array instead of just doing routerLink="" or [routerLink]="''"
First, we need to import the router module from angular router and declare its alias name
import { Router } from '#angular/router'; ---> import
class AbcComponent implements OnInit(){
constructor(
private router: Router ---> decalre alias name
) { }
}
1. You can change query params by using "router.navigate" function and pass the query parameters
this.router.navigate([], { queryParams: {_id: "abc", day: "1", name: "dfd"}
});
It will update query params in the current i.e activated route
The below will redirect to abc page with _id, day
and name as query params
this.router.navigate(['/abc'], { queryParams: {_id: "abc", day: "1", name:
"dfd"}
});
It will update query params in the "abc" route along with three query paramters
For fetching query params:-
import { ActivatedRoute } from '#angular/router'; //import activated routed
export class ABC implements OnInit {
constructor(
private route: ActivatedRoute //declare its alias name
) {}
ngOnInit(){
console.log(this.route.snapshot.queryParamMap.get('_id')); //this will fetch the query params
}
Angular's Location service should be used when interacting with the browser's URL and not for routing. Thats why we want to use Location service.
Angulars HttpParams are used to create query params. Remember HttpParams are immutable, meaning it has to be chained when creating the values.
At last, using this._location.replaceState to change to URL without reloading the page/route and native js location.path to get the url without params to reset the params every time.
constructor(
private _location: Location,
) {}
...
updateURLWithNewParamsWithoutReloading() {
const params = new HttpParams().appendAll({
price: 100,
product: 'bag'
});
this._location.replaceState(
location.pathname,
params.toString()
);
}
I've had an interesting situation where we used only one component for all routes we had. This is what routes looked like:
const routes: Routes = [
{
path: '',
component: HomeComponent,
children: [
{ path: 'companies', component: HomeComponent },
{ path: 'pipeline', component: HomeComponent },
// ...
]
},
// ...
];
So, basically, paths /, /companies and /pipeline were all having the same component that had to be loaded. And, since Angular prevents reloading of the components if they were previously loaded in the DOM, Router's navigate method returned a Promise that always resolved with null.
To avoid this, I had to use onSameUrlNavigation. By setting this value to 'reload', I managed to make the router navigate to the same URL with the updated query string parameters:
#NgModule({
imports: [RouterModule.forRoot(routes, { onSameUrlNavigation: 'reload' })],
exports: [RouterModule]
})
Also you can add BehaviorSubject like:
refresher$ = new BehaviorSubject(null);
I changed my code from that:
this.route.queryParamMap.subscribe(some code)
to:
combineLatest([
this.route.queryParamMap,
this.refresher$
])
.pipe(
map((data) => data[0])
)
.subscribe(here is your the same code)
And when you need refresh your subscription, you need call this:
this.refresher$.next(null);
Also don't forget add unsubscribe from that to ngOnDestroy
I configured routing module as following:
const routes: Routes = [
{
path: "engineering/:branches",
component: BranchesTabsComponent
},
{
path: "humanities/:branches",
component: BranchesTabsComponent
},
];
and in the main-continer.component.ts:
titlesOfEngineeringTabs: string[] = ['E1','E2','E3'];
titlesOfHumanitiesTabs: string[] = ['H1','H2'];
constructor(private router: Router) {}
handleEnginTabs():void{
this.router.navigate(['/engineering', this.titlesOfEngineeringTabs]);
}
handleHumanTabs():void{
this.router.navigate(['/humanities', this.titlesOfHumanitiesTabs]);
}
and also main-continer.component.html contains:
<router-outlet></router-outlet>
and then in branches-tabs.component.ts have:
tabsLable: string[] = [''];
ngOnInit(): void {
this.route.params.subscribe(param => this.tabsLable = param["branches"]);
}
till here, it is obvious that we want to replace <router-outlet> with branches-tabs component in which deferent tab labels are shown Depending on selected menu...
but I get this error:
*core.mjs:6485 ERROR Error: Uncaught (in promise): Error: Cannot match any routes. URL Segment: 'engineering;0=%DA%A9%D8%A7%D9%85%D9%BE%DB%8C%D9%88%D8%AA%D8%B1;1=%D8%B5%D9%86%D8%A7%DB%8C%D8%B9;2=%D9%85%D8%AA%D8%A7%D9%84%D9%88%D8%B1%DA%98%DB%8C'
Error: Cannot match any routes. URL Segment: 'engineering;0=%DA%A9%D8%A7%D9%85%D9%BE%DB%8C%D9%88%D8%AA%D8%B1;1=%D8%B5%D9%86%D8%A7%DB%8C%D8%B9;2=%D9%85%D8%AA%D8%A7%D9%84%D9%88%D8%B1%DA%98%DB%8C'*
how can pass a string array as parameter and fix above error?
best regards
Angular docs on navigate()
An array of URL fragments with which to construct the target URL. If
the path is static, can be the literal URL string. For a dynamic
path, pass an array of path segments, followed by the parameters for
each segment. The fragments are applied to the current URL or the
one provided in the relativeTo property of the options object, if
supplied.
If your intent is to have the url look like /engineering/E1/E2/E3 then you should apply the spread operator ... to the arrays and it would look like:
this.router.navigate(['/engineering', ...this.titlesOfEngineeringTabs]);
If you want to put the array in the url, it needs to be a string. So you can use JSON.stringify() and JSON.parse()
handleEnginTabs():void{
this.router.navigate(['/engineering', JSON.stringify(this.titlesOfEngineeringTabs)]);
}
ngOnInit(): void {
this.route.params.subscribe(param => this.tabsLable = JSON.parse(param["branches"]));
}
But queryParams is probably more appropriate.
handleEnginTabs(): void {
this.router.navigate(['/engineering'], {
queryParams: { branches: this.titlesOfEngineeringTabs },
});
}
ngOnInit(): void {
this.tabsLable = this.route.snapshot.queryParams['branches'];
}
and removing the parameter from the path
{
path: "engineering",
component: BranchesTabsComponent
},
Keep in mind the user can change the values of your array by editing the url. If that's something you don't want to happen, you'll have to use other means of passing the data.
This thread goes over some other ways of passing data to routes:
How do I pass data to Angular routed components?
However, a lot of them will not persist on refresh. So if you want that, you'll have to write to / read from localStorage as well.
In my angular 7 code I'm trying to fetch the clientId passed in the URL. clientId will be dynamic.
localhost:4200/client/xyz
app.routing.ts
{
path: 'client/:clientId',
component: AppComponent
},
app.component.ts
constructor(private route: ActivatedRoute) { }
ngOnInit() {
console.log(this.route.snapshot.paramMap.get('clientId'));
});
It prints null in the console
You can get the route params using this method
this.route.params.subsribe(params => {
console.log(params['clientId'])
});
or
this.activateRoute.snapshot.params['clientId']
PLease let me know if you still have problem
Use params instead of paramMap,
console.log(this.route.snapshot.params['clientId']);
Currently, I have a modal material dialog window that asks the user to input a number and then hit search. On search, it fetches data from api call and gets back a response object. I want to use the response object to populate a new page (edit form).
My question is, how can I past the data, particularly the number the user entered on the material dialog component to another component, so that it can fetch the api call results or how can I pass my response object to my edit from from dialog?
E.g.
Here's my search function:
search(searchNumber) {
if (this.selectedOption === 'Bill Number') {
this._transactionService.findExistingTransactionByBillNumber('U001', searchNumber)
.subscribe(data => this.transactionResponse = data);
console.log(JSON.stringify(this.transactionResponse));
this.router.navigate(['/edit-transaction-portal']);
} else {
this._transactionService.findExistingTransactionByTransactionNumber('U001', searchNumber)
.subscribe(data => this.transactionResponse = data);
console.log(JSON.stringify(this.transactionResponse));
this.router.navigate(['/edit-transaction-portal']);
}
}
I want to be able to either 1) pass the response object I get here or pass the searchNumber the user entered, so that I can do a lookup within my edit form component. I need to pass in either one from this component to my new component that I navigate to.
EDIT: Accepted solution shows how to add query params to this.router.navigate() and how to retrieve it by subscribing to activateRoute, a different approach than the one identified in the other SO post.
You can pass the number (bill/transaction)
this.router.navigate(['/edit-transaction-portal'], { queryParams: { bill: 'U001' } });
this.router.navigate(['/edit-transaction-portal'], { queryParams: { transaction: 'U001' } });
then in your component(edit-transaction-portal) hit the api to get the data. In component you should include ActivatedRoute in constructor. It will be something like:
isBill: boolean;
isTransaction: boolean;
number: string;
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.sub = this.route
.queryParams
.subscribe(params => {
this.isBill = params['bill'] != undefined;
this.isTransaction = params['transaction'] != undefined;
this.number = this.isBill ? params['bill'] : params['transaction'];
// Call API here
});
}
My question is, how can I past the data, particularly the number the
user entered on the material dialog component to another component
You can pass it throw material dialog component. Inject dialogRef to you component which opened in the dialog:
constructor(
public dialogRef: MatDialogRef<SomeComponent>,
#Inject(MAT_DIALOG_DATA) public data: any,
) { }
After the submitting data, you can pass any data to component which opened this dialog, by closing the dialog:
onSubmit() {
this.service.postProduct(this.contract, this.product)
.subscribe(resp => {
this.dialogRef.close(resp);
});
}
And in your Parent component, who opened this dialog can get this passed data by subscribing to afterClosed() observable:
Parent.component.ts:
openDialog(id) {
const dialogRef = this.dialog.open(SomeComponent, {
data: { id: anyData}
});
dialogRef.afterClosed().subscribe(result => {
if (result) {
// do something...
}
});
}
Would I pass the data object in dialog.open()? How would I retrieve it
from there?
Look at openDialog() above. It has data property, that you can send to dialog components. And in the opened component inject MAT_DIALOG_DATA as this:
#Inject(MAT_DIALOG_DATA) public data: any,
to access passed data object as shown code above
Official docs[sharing-data-with-the-dialog-component]
if you want to pass data which the help of routing you have to define route which takes value as part of rout like as below
const appRoutes: Routes = [
{ path: 'hero/:id', component: HeroDetailComponent },];
it will from code side
gotoHeroes(hero: Hero) {
let heroId = hero ? hero.id : null;
// Pass along the hero id if available
// so that the HeroList component can select that hero.
// Include a junk 'foo' property for fun.
this.router.navigate(['/heroes', { id: heroId, foo: 'foo' }]);
}
Read : https://angular.io/guide/router#router-imports
If you want to pass data between two component then there is #Input and #Output property concept in angular which allows you to pass data between components.
#Input() - this type of property allows you to pass data from parent to child component.
Output() - this type of property allows you to pass data from child to parent component.
Other way to do it is make use of Service as use the same instance of service between component.
Read : 3 ways to communicate between Angular components