Angular Universal didn't go easy on me

#angular 

Story by gioweeargao

April 18, 2020

laptop with angry octopus stuff toy

So I recently implemented a server side dynamic prerender in this blog, which was just rushed because it was also my current task at work (ssr with dynamic preview links) and my growing anxiety keeps on making it harder to sleep at night πŸ’†πŸ»β€β™€οΈ. Now that it works (here and at work) it feels very fulfilling, and that was only because it was more complicated than I think it should be.

What is Angular Universal?

As we all know, Angular is a client side framework. Everything is rendered in the browser and for most projects, the only "node" thing we know to do is host it (and with Angular CLI everything is already built for us). It is also a single page app, which means that our meta tags and titles are really just one in reality. We can change them via client side but they are not really there in the server. (Try: navigate around an Angular app and go to the page source to see if the title / meta tags change)

Why do we need them in the server?

Because that's what google and other social media crawls. They need the info saved in the server to fetch an image, a title, a description and others. Also, other important SEO stuff.

That's where Angular Universal comes in to save the day! And it wasn't easy. The last time I've felt this stupid was when I needed to work on CI builds using Bamboo Specs when I'm not a devops πŸ™…πŸΌβ€β™€οΈ. 

So here's a blog post dedicated to how Angular Universal gave me a hard time:

Not much documentation online

Most blog posts about Angular Universal is long and has a lot of explanations (which apparently is not enough still πŸ€·πŸ»β€β™€οΈ). Most also has identical content inside. What if you get stuck somewhere in the middle? Somewhere near the end? I've spent a lot of times googling stuff to only be redirected to the same pages.

There's not much specific topics about it that you'll find online. I feel like there's probably only 1 to 3 three ways to make it work and that's it. And available tools will each require a lot of change in packages used. It is a lot of mess when they don't work (I tried ng-toolkit/universal). I was so close to trying to upgrade to Angular 9 (which is not the right time to do as we are close to a release).

Now what?

I followed the tutorials online about creating a server and sending the requested HTML pages to that server, now what? How do I deploy this live? 

It was all a big question mark to me.

For this blog, as of today, it is still hosted for free (which I plan to change this year), and I have no idea what the server is for in a world that's outside my local. Most blogs will ask you to create a server using node express and nobody explains how to use that after trying the output for http://localhost:4444 πŸ€·πŸ»β€β™€οΈ

As it turns out that is SSR and what I can do was Prerender.

Prerender VS SSR

For a long time I thought I understood their difference. As it turns out I really don't despite reading multiple blog posts about it.

prerender.ts:

  • Has a manual list of pages to run through in the server 
  • Creates multiple index.html files and saves them in the dist folder named after the route.
    • e.g /blog/a-day-in-bangkok-temples.html/index.html
  • This new added HTML then are added in the dist folder which are the files we are gonna use to deploy

server.ts:

  • Does not need a manual list of pages
  • Once anyone opens a specific route, the server renders the html and saves it there, somewhere
    • Please don't ask me where or my brain will break again 🐣

The biggest difference I see now is that there's more work needed for prerender after (I followed this tutorial). If I publish a new blogpost (e.g this one), I will need to prerender the file for it first locally and then manually deploy the specific file πŸ‘ŽπŸΌ

I guess it works for now. Will probably be forced to figure out how to automate this process when it starts to annoy me.

On the other hand, for the server side rendering (SSR), it is possible that our requests are doubled. And if there are unnecessary console logs added in the code, it will also be seen in the server which is not ideal.

Expected preview not set in the server

For this issue, I blamed Angular Universal. I thought it didn't work. My configuration might be incomplete. The versions of (way too many) dependencies could be mismatched. Well, nope.

For this blog the only reason it was just partially working was because I used an incorrect HTML meta tag attribute. It should be property instead of name for the Facebook meta tags πŸ™ƒ. 

And then at work it took me a long time before realizing we already have Angular Universal implemented in our code. I didn't notice that the only issue is that the updated meta tags and title are not being included in the content saved in the server. ...and yes it took me awkwardly way too long to figure it out πŸ‘».

Way too many errors in the server logs

Since it's in the server, it does not know any of the browser global variables (window, document, localStorage, navigator). So it logs a lot of error not knowing these. And there's a lot! 

A few days after finishing this task and the website had an issue and was not loading, the devops found these error logs and automatically assumed that's the issue. Long story short it's not πŸ™ƒ. (Let's talk about best practices and why they are not properly followed some other time ✌🏼)

I initially thought everything will be ok if I fix this. I even attempted to do it. I was wrong.

All requests with relative URL will not work

One more thing that took me too long to notice. Okay this one might be my fault for not reading the docs completely (because it is way too long πŸ’€). So there's a lot of code out there to fix this issue using an interceptor that you can add in the app.server.module.ts so that it does not affect client side code. 

Also encountered an issue once it was deployed (AKA outside my local) that the request still does not work. That one was an nginx problem where the server does not know where to find the url.

What they don't tell you (in easy to understand english) is that it's not like you're gonna want all your requests to work in the server because they're unnecessary. In my case I only needed one to work. 

1 bug down and more introduced.

Infinite loop on error

What made me almost give up was the server crashing. 

Apparently when it encounters an error that's not handled, it will be in an infinite loop and we all know what happens after that. I've spent days looking for answers. Some say it's an Angular issue while others say it is caused by the Express server.

In a perfect world, all request errors should be handled but I have started working on this project already looking like this, there's not much I can do about that. Also, it might not even be the real cause of the infinite loop.

What I did to fix this was to move code I didn't need to only work in the browser. A simple if statement. That's it πŸ˜….


Looking back, I find it crazy now that all the solution to the issues I encountered were simple. An if statement. incorrect attribute, an interceptor. I want to say to anyone having the same issues to just read to docs more, but I feel like I did more than that to make this work.

I still believe we'll live in a world where adding Angular Universal is easy and can be configured with just one command (although there's one blog post saying it is just like that in Angular v9). For now, I am glad all this mess is in the past and I can finally sleep better πŸ’†πŸ»β€β™€οΈ.


PS:

Totally unrelated but I feel like this blog post does not sit well with the other posts next to it (travels, personal stuff, relationships). When I started this website I didn't think I'd write a lot about dev stuff because I was still just a newbie junior developer back then. Now there's so much I want to share frontend related. Specially those tiny issues that stressed the hell out of me 🐣.

So maybe if I have the time I'll find a way to make it (blog categories) work, for now I apologize for the random unrelated content that's all over this blog βœŒπŸΌπŸ’€.

loading

loading

Hello, your viewport is too small. ☹️