Angular Route & Query Parameters Tutorial
In this Angular tutorial we learn how to send query data or dynamic data through an app's Routes with Query & Route parameters
We also cover how to use observables to fetch the data we sent.
What are Route Parameters
Route parameters allow us to send dynamic data through a route by appending it to the URL as a segment.
For example, if we want to send a user’s ID to another component, we could do it with a route parameter. The URL would look something like the following.
- /user/1
- /user/2
- etc.
Our app may have many users so it’s impractical to hardcode their IDs in separate router links. Instead, we use a placeholder, like ID, for the actual data.
1. Before we go any further, let’s set up an ‘employee’ component to help with demonstration.
ng generate component employee
2. Next, set up a route to the new component.
- Import Routes and RouterModule from ‘@angular/router’.
- Set up a constant array of type Routes with the path and component name.
- Specify the array for Angular to use in the RouterModule.forRoot() method in the imports.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { EmployeeComponent } from './employee/employee.component';
import { Routes, RouterModule } from '@angular/router';
const appRoutes: Routes = [
{ path: 'employee', component: EmployeeComponent }
];
@NgModule({
declarations: [
AppComponent,
EmployeeComponent
],
imports: [
BrowserModule,
RouterModule.forRoot(appRoutes)
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
3. In the main ‘app’ component’s View, let’s add a router outlet and some menu items.
<ul>
<li><a routerLink="/">Home</a></li>
<li><a routerLink="/employee">Employee</a></li>
</ul>
<hr>
<router-outlet></router-outlet>
How to setup a Route Parameter
To define a parameter we add a slash, followed by a colon and the placeholder name in our route definition.
{ path: 'path_to_component/:placeholder_name', component: component_name }
If we want to send data to the current component, we simply remove its path before the placeholder.
{ path: '/:placeholder_name', component: component_name }
As an example, let’s say we want to send the employee’s ID to the ‘employee’ component.
...
const appRoutes: Routes = [
{ path: 'employee', component: EmployeeComponent },
{ path: 'employee/:id', component: EmployeeComponent }
];
...
We can now use an ID number in the URL, like: http://localhost:4200/employee/1
How to send a Route Parameter
We don’t want users to have to type parameters manually in the URL bar, we want to send the parameters through code with links, images or buttons.
To do this we use the routerLink directive with the Link Parameters array.
<a [routerLink]="['/path', 'parameter']">Link</a>
Let’s create a normal link as well as a button in the main ‘app’ component’s View. These should go to the ‘employee’ component with a number as the ID.
<ul>
<li><a routerLink="/">Home</a></li>
<li><a routerLink="/employee">Employee</a></li>
</ul>
<hr>
<a [routerLink]="['/employee', '1']">Employee 1</a><br><br>
<button [routerLink]="['/employee', '2']">Employee 2</button>
<router-outlet></router-outlet>
Clicking on the links will show the ID number in the URL.
We don’t need to include a slash operator. If we do, it will be converted to a “safe” ASCII url.
<ul>
<li><a routerLink="/">Home</a></li>
<li><a routerLink="/employee">Employee</a></li>
</ul>
<hr>
<a [routerLink]="['/employee', '/1']">Employee 1</a><br><br>
<button [routerLink]="['/employee', '2']">Employee 2</button>
<router-outlet></router-outlet>
If we click on the ‘Employee 1’ link, the URL will be encoded into /employee/%2F1 , which is not what we want.
How to access a Route Parameter
Now that we’ve sent data through the route, we need to access the parameter to be able use the data.
We do this with the ActivatedRoute service, which keeps track of the currently activated route associated with the loaded component.
To use the ActivatedRoute service we need to import and inject it into the component where we will be accessing the data. In our case that’s the ‘employee’ component.
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-employee',
templateUrl: './employee.component.html',
styleUrls: ['./employee.component.css']
})
export class EmployeeComponent implements OnInit {
constructor(private activatedRoute : ActivatedRoute) { }
ngOnInit(): void { }
}
Angular adds all the route parameters in the ParamMap object, which makes it easier to work with parameters.
The older Params array is now deprecated and is replaced by the ParamMap.
There are two ways to get the parameter value from the ParamMap object.
- Snapshot
- Observable
Using the snapshot property to access a Route Parameter
The snapshot property returns the initial value of a route. We can then access the paramMap array and retrieve the value of the parameter with the get() method.
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-employee',
templateUrl: './employee.component.html',
styleUrls: ['./employee.component.css']
})
export class EmployeeComponent implements OnInit {
userID: any;
constructor(private activatedRoute : ActivatedRoute) { }
ngOnInit(): void {
this.userID = this.activatedRoute.snapshot.paramMap.get("id");
}
}
In the example above we get the value of the id parameter and store it in userID .
For demonstration, let’s show the ID in the component’s view with property binding.
<p>User ID: {{ userID }}</p>
Now when we click on the link or button, the parameter will show in the paragraph.
The snapshot property is typically only used when we need the initial value because it doesn’t update after changes.
Using an Observable to access the Route Parameter
We can retrieve the value of the parameter by subscribing to the paramMap observable property of the ActivatedRoute .
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-employee',
templateUrl: './employee.component.html',
styleUrls: ['./employee.component.css']
})
export class EmployeeComponent implements OnInit {
userID: any;
constructor(private activatedRoute : ActivatedRoute) { }
ngOnInit(): void {
this.activatedRoute.paramMap.subscribe(params => {
this.userID = params.get('id');
});
}
}
An observable is typically used when the value is expected to change over time.
We cover Observables in more detail in the Observables lesson.
Why should we use an Observable
We retrieve parameter values in the ngOnInit life cycle hook, when the component is initialized.
If a user navigates away from, then back to a component, Angular will reuse the existing instance of it instead of creating a new one.
This means that the ngOnInit hook is not called again, so any different value for the parameter will not show.
By using an observable, we will always retrieve the latest value of the parameter.
What are Query Parameters
Query Parameters are optional parameters that we can pass through a route. They are added to the end of a URL with a question mark.
/listings?page=4
Where page=4 is the parameter.
Query parameters are common in searches, pagination etc.
Route parameters are part of the route definition and is used by Angular to determine the route. Query parameters are optional and doesn’t stop Angular from navigating to a route.
How to send a Query Parameter
Because query parameters aren’t part of the route, we can send them using the queryParams directive or with the router.navigate() method.
When using the queryParams directive, we have to specify a Javascript object with key:value notation.
<a [routerLink]="['path']" [queryParams]="{ key:value }">Page 2</a>
This will resolve into key=value .
<ul>
<li><a routerLink="/">Home</a></li>
<li><a routerLink="/employee">Employee</a></li>
</ul>
<hr>
<a [routerLink]="['/employee', '1']" [queryParams]="{ page:1 }">Employee 1</a><br><br>
<button [routerLink]="['/employee', '2']">Employee 2</button>
<router-outlet></router-outlet>
If we run the example above and click on the ‘Employee 1’ link, it will add page=1 to the end of the URL.
When navigating programmatically, we use the router.navigate() method with the queryParams Javascript object as the second argument. Again, we have to use key:value notation.
this.router_object.navigate(['/path'], { queryParams: { key: value } });
Remember that the Router class must be imported from ‘@angular/router’ and injected in the component’s constructor before we can use the navigate() method.
import { Component } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
constructor(private r: Router) { }
navToPage(pageNum: number) {
this.r.navigate(['/employee', '2'], { queryParams: { page: pageNum } });
}
}
In the example above we wrap our link in a method that will allow us to easily specify a value for the query parameter.
<ul>
<li><a routerLink="/">Home</a></li>
<li><a routerLink="/employee">Employee</a></li>
</ul>
<hr>
<a [routerLink]="['/employee', '1']" [queryParams]="{ page:1 }">Employee 1</a><br><br>
<button (click)="navToPage(4)">Employee 2</button>
<router-outlet></router-outlet>
Because we’re using a method, we changed the button from routerLink to a click event.
If we run the example above and click on the ‘Employee 2’ button, it will add page=4 to the end of the URL.
How to access a Query Parameter
We access query parameters in the same way as route parameters, with the ActivatedRoute, but instead of the ParamMap , we use the queryParamsMap .
The queryParams array has been deprecated and is replaced with queryParamsMap .
Using the snapshot property to access a Query Parameter
The snapshot property returns the initial value of a route. We can then access the queryParamMap array and retrieve the value of the parameter with the get() method.
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-employee',
templateUrl: './employee.component.html',
styleUrls: ['./employee.component.css']
})
export class EmployeeComponent implements OnInit {
pageNum: any;
constructor(private activatedRoute : ActivatedRoute) { }
ngOnInit(): void {
this.pageNum = this.activatedRoute.snapshot.queryParamMap.get("page");
}
}
Let’s also output the value to the page.
<p>Page Number: {{ pageNum }}</p>
When we run the example above and click on the ‘Employee 1’ link, the page number 1 will show in both the URL and on the page.
But, if we click on the ‘Employee 2’ button, it will still show the page number as 1 on the page instead of 4. Like with the Route parameter, the snapshot property only holds the value when the component is initialized, it doesn’t hold any updates to the value from our navToPage() method.
Luckily, we can also use an observable to access the query parameter and get the latest value.
The snapshot property is typically only used when we need the initial value because it doesn’t update after changes.
Using an Observable to access the Query Parameter
We can retrieve the value of the query parameter by subscribing to the queryParamMap observable property of the ActivatedRoute .
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-employee',
templateUrl: './employee.component.html',
styleUrls: ['./employee.component.css']
})
export class EmployeeComponent implements OnInit {
pageNum: any;
constructor(private activatedRoute : ActivatedRoute) { }
ngOnInit(): void {
this.activatedRoute.queryParamMap.subscribe(params => {
this.pageNum = params.get('page');
});
}
}
This time we get the latest value so both the link and the button will show the correct values on the page.
An observable is typically used when the value is expected to change over time.