Click here to share this article on LinkedIn »

Ah the url, an often overlooked place to hold state in modern apps but one that is oh so powerful. Have you ever gone to a website, searched, set all your filters, refreshed and then it all goes away? This is a prime example of why well designed router/url state is important. While this article is about Angular specifically the main idea of storing state in the URL is applicable to all frameworks/apps.

Let’s take a look at an example URL for a search page. The requirements for our url is that it needs to say which page needs to be navigated to, hold the value the user searched for and hold several search filters.

/products?search=playstation?department=electronics&priceLimit=100

The above url tell’s our application several things about it’s state so let’s break it down.

products - The page we are viewing search=playstation - The search value department=electronics - the department filter priceLimit=100 - the price limit filter

Since Angular’s router is reactive it becomes very simple to use these values as the source for our search page.

import { Component } from '@anguar/core';

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

import { Observable } from 'rxjs/Observable';

import { switchMap } from 'rxjs/operators'; import { SearchService } from './search.service'; export interface SearchParams {

department: string;

priceLimit: string;

search: string;

} @Component({

template: `

{{searchParams | async | json}} {{results | async | json}}

`

})

class MyComponent {

searchParams: Observable<SearchParams> = this.route.queryParams results = this.searchParams.pipe(

switchMap(params => this.ss.findProducts(params))

); constructor(

private ss: SearchService,

private route: ActivatedRoute

) {} updateSearch(params: Partial<SearchParams>) {

this.router.navigate(['.'], {

queryParameters: params,

queryParamsHandling: 'merge'

})

}

}

Now if we look into the component definition above we notice 3 things.

The query parameters are an Observable, meaning that whenever a query parameter changes our subscribers (in this case our template via the “async” pipe) will be notified We can use the RxJs “switchMap” operator to trigger a new search whenever one of those parameters changes. All we have to do to update our view is change one of those parameters. (see the “updateSearch” method in the component. One can imagine it being called when someone selects a checkbox)

Following this pattern there is no need to manage local state manually or use a global app store, the URL IS the state of the view and will tell us when things need to change.

Now your route isn’t an ideal place to store EVERYTHING so let’s look at some example of what it is and isn’t good for.

GOOD:

Filters: Think an Amazon page or JIRA.

Anything that needs to be easily sharable by a user: Filters are again a good example or maintaing the correct “page” in a multi page table

Any state that needs to be maintained on refresh: Filters, Page, current tab

Simple Values: strings, numbers, booleans or arrays of these values which would look like this:

products?list=first?list=second&list=third

BAD:

Complex values: The url is great for storing simple primitives or arrays of primitives, (An array of primitives is represented by adding a query parameter with the same name multiple tiles) but bad at storing more complex structures like…

Objects: you may be tempted at times to just JSON.stringify an object and throw it in a query param, please don’t… please?

In closing think about yourself as the user, if there is a chance you would be annoyed having to navigate back to the correct tab or reselect filter or search parameters there is a greater then 0 change your users will too.

If you have any other interesting use cases or implementations using reactive query or route parameters let me know in the comments!