Site icon Ryadel

Angular 5 - How to access Window, Document and other browser types in Angular Universal

Angular 5 - How to use LocalStorage, Window, Document and other browser types in Angular Universal

A short while ago I wrote this post explaining how to properly access browser types - such as localStorage - from an Angular Universal web application using a situational approach built upon the PLATFORM_ID  token and the isPlatformBrowser  & isPlatformServer  methods. Although that workaround might definitely point us in the right direction to write isomorphic JavaScript code, it won't automatically make us write good Angular code... unless we understand something else.

The first thing we need to consider is that accessing global variables within an Angular component is never the proper way of doing things, regardless of the executing context. As a matter of fact it's always a bad practice, as it goes against the Angular DI pattern, even if we do that when these variables are available.

To better understand that, let's pick the following code:

Is this code isomorphic? YesWill it work fine in an Universal web app? YesCan it be considered a good piece of Angular code? No.

Writing isomorphic code and writing decent Angular code are two completely different things. In the above code we're not injecting anything that contains the localStorage  object, we're just trying to access it directly as a global variable: as a general rule we could say that, when we're looking at an Angular component code, any global access means that there's something wrong. There are no exceptions here: it could work fine, it could get us where we want to, it could even be the only way to get the job done, yet it's still wrong for a number of reasons, including the following:

  • It could cause issues with some TypeScript compiler, expecially if they are configured to perform strict type checks with no browser and/or generic types support.
  • It will cause issues on some AoT compilers, including those who don't exist yet but we might choose (o be required) to adopt in future.
  • It will not give us testable code, thus making it harder to understand what's going on.

What's the proper way to deal with these types then? If we want to stick to the Angular DI-based pattern, the best thing we can do is inject an adapter for such types that will work for both the server-side and client-side executing contexts with specific behaviours. That adapter can either be a function, a custom-made class or anything else, depending on our scenario. For the localStorage  object a function returning an untyped option should be more than enough:

We can then put this function in our AppModule file and then use it as a factory for a LOCALSTORAGE provider in the following way:

and then use it as a factory for a LOCALSTORAGE provider in the following way:

If we have different AppModule files for the browser and for the server, we can safely use this function in the   app.module.shared.ts  file, unless we want to enforce completely different behaviours for the server and client contexts. If that's the case, it could be wiser to implement a custom class factory with a getValue()  method that will conditionally operate and return something depending on the executing platform type, which you can determine checking the PLATFORM_ID  token against the   isPlatformBrowser  method like already explained here.

Once done, we can inject the LOCALSTORAGE generic object in any of our Angular components and check for the platform type before using it:

It's worth noting that, since our getLocalStorage()  function will return null if the window object isn't available, we could just check for this.localStorage  nullability and entirely skip the platform type check. However, the above one is the recommended approach: we can't take for granted that function return value, as its implementation might be subject to change in the future; conversely, the isPlatformBrowser  / isPlatformServer  return values are something that we can always trust by design.

If you want to know more about .NET Core and Angular check out the ASP.NET Core 2 and Angular 5 book, available in paperback and/or digital format. Promo Code: ASPCA50 to get it with a 50% discount! The book's latest edition, updated to ASP.NET Core 5 and Angular 11, is available here.

That's it, at least for now: happy coding!

 

Exit mobile version