Migrating from Polymer 3 to Angular

Nick Felker
5 min readApr 18, 2023

Why am I doing this? Well, several years ago I created my own website for recipes. This was fun but I recently returned to it. I purchased an air fryer and wanted to document the things I’ve air-fried like air-fried ravioli.

Recently, as I’ve been getting into building PWAs, I thought I could just add the manifest and service worker and have it work. However, I quickly realized that wouldn’t be possible. The main problem was that PWAs need to be secure, hosting through HTTPS. My current architecture wouldn’t work and I didn’t want to pay my domain provider, Namecheap, the extra cost of SSL certificates.

I figured the second option would be to use a free static host provider like GitHub Pages or Firebase Hosting. There’s a bit of a backend that I can keep while the hosting was independent. Could I just migrate my frontend over? No.

The git repo has been built and rebuilt a few times. Most recently it was set up using the now deprecated Polymer 3 with an Express backend for hosting. How do you get these two things to work together? It was a very confusing build system that I cannot today make sense of.

The easiest option was a full rewrite of the site. Building it with Angular made sense as it’s a well-supported and non-deprecated frontend framework. The backend would be simply extended to providing a data API rather than manage the entire hosting.

So in this post, I go through the work I did to migrate Polymer 3 to Angular. You may find this useful, or you may not, but it could be a useful reference.

Getting started

The actual frontend is fairly simple, with a handful of pages and a few smaller widgets. The implementation was a little wonky admittedly, and past me did a few things that worked but not well.

I created a new Angular project with the Angular CLI and then started to build all my views.

This gist shows the original index and the new index. It’s fairly simple, with a few links and standard widgets.

Using ng serve I was able to load the webpages in a separate window, which would show a variety of errors as I copied from Polymer and massaged into something different.

Differences

Polymer 3 puts everything into one file whereas Angular recommends putting the layout, presentation, and logic into separate files. I prefer the latter as it makes me feel better about not breaking the code. Angular also uses TypeScript, which was generally much stricter. I realized my original code was loose in a lot of places.

The original page for loading individual recipes is quite complicated in the original Polymer version. I threw a lot of things in one file and removed a few things to make it easier to read for this post.

The Angular one now looks cleaner thanks to splitting up things into separate files. But there are some differences I want to point to specifically:

  • <template is="dom-if" if="{{condition}}"> <div>...</div> </template> is now easily written as <div *ngIf="condition">...</div>
  • <template is="dom-repeat" items="{{items}}"> <div>...</div> </template> is now easily written as <div *ngFor="let item of items">...</div>
  • Data binding works easily without needing to do any arrayItem hacks
  • reflectToAttribute is now replaced by @Input() and types are more idiomatic
  • My servings input element no longer needs an explicit oninput event defined in code. I can add (input)="reserve() to run events. Values are also bound with [(ngModel)]="data.servings
  • Icons cannot be pulled from iron-icons but the same set are accessible through Material Icons.

I migrated away from Express and my complex build system to Angular Routing. You can see that the various path params of the URL, such as https://dishout.recipes/g/fleker/personal-cookbook/crepe, are parsed and used to make the API call and get the data.

Angular’s data binding works much better and reliably.

When I first started this, I copied recipe.js into recipe.component.html and a lot of errors came up. Top to bottom I resolved them until everything loaded.

TypeScript and Angular together are really particular about undefined variables and led me to use a bunch of optional chaining, which was barely around when I wrote this the first time. Instead of defining a lot of specific types and fields, taking advantage of the JSON schema I defined a while back, I opted to do with any as the type a lot. It made it slightly faster to get through the work, since I was reasonably sure the original code worked.

I ran both the old and new versions of the frontend side-by-side so that as my Angular implementation iterated, I could verify that the design was roughly the same.

Finally I added my manifest.json and sent up my built website to Firebase. It took up to an hour for all the Namecheap rerouting to be complete.

Managing technical debt

The migration works, but isn’t truly “complete”. There’s now a lot of unused code in the backend. The repo is now split, with two branches for each side and two separate deployment workflows. To call it truly complete I would need to do a lot of cleaning up. I also would like to better separate out the layout of various widgets and define more types rather than using any.

But I’m relatively happy with how everything is situated now. My cookbook and all its recipes load in a PWA. I can easily look things up on my phone. As long as I don’t change anything, it’ll be reliable.

--

--

Nick Felker

Social Media Expert -- Rowan University 2017 -- IoT & Assistant @ Google