HTML Local Storage can be a nice tool to preserve transient data over several page visits, e.g. when reloading the page or coming back from a necessary user login after session timeout. It’s part of good usability to let the user continue where he or she left. But don’t let me count the times that I had to solve bugs in combination with wrong local storage handling. If you add and use local storage as another source of truth regarding your data, inconsistency can be a big issue. Also you perhaps want to have entry points into your application that don’t load the local storage? Let’s take a look at a possible solution…
The requirements: entering our application using „normal“ routes should reset the local storage, so that the user gets a „fresh“ start. On every subsequent page reload he or she should see previous data by not clearing the local storage. This can be realized by using a special query parameter in the URL (let’s name it usePreviousData
) that will be set automatically when the user enters the application.
To have a central place and not pollute our components, we want to use a reusable Angular Route Guard to handle this logic for us. And here’s the implementation:
@Injectable() export class ManageLocalStorageGuard implements CanActivate { constructor(private readonly router: Router) {} canActivate(route: ActivatedRouteSnapshot, routerState: RouterStateSnapshot): boolean | UrlTree { const usePreviousDataParam = 'usePreviousData'; const usePreviousData = route.queryParamMap.has(usePreviousDataParam); if (!usePreviousData) { localStorage.clear(); const urlTree = this.router.parseUrl(routerState.url); urlTree.queryParams[usePreviousDataParam] = '✓'; return urlTree; } return true; } }
The code is not so hard to understand (I hope ;)). First we are checking if the usePreviousData
query param is set. If not, first we clear the local storage completely (of course you can abstract this functionality in a separate service). Then, we fetch the current URL as parsed UrlTree and append the usePreviousData
param (with an arbitrary value) in order to keep the local storage on page reload.
As last step we return the modified UrlTree
. This is allowed since Angular 7.1 and will navigate to the extended URL. Note that this will call the current route again, leading to a re-execution of the guard. On this second call, nothing will happen since usePreviousData
is now set in the URL. Hence this call doesn’t bother us. But be aware of it! Other guards will also be re-executed, which can lead to unwanted behavior. To tackle this problem you can define the ManageLocalStorageGuard
on your main route and then use child routes for other guards and logic.
In the easiest case, now just include the guard in your routing module configuration and you are done:
const routes: Routes = [ { path: '', canActivate: [ManageLocalStorageGuard], component: MyAwesomeComponent } ... ];