For a client we needed a dialog that contains some complex user experience flows. It basically boiled down to a wizard with multiple paths. This meant that we didn’t want the entire dialog in one huge component, but rather use separate ones to keep our components as small, atomic, reusable, and maintainable as possible. Since the application uses Angular Material, it made sense to also use their dialogs to stay within the same UI guidelines.
Since I love developing, and am always looking forward to the next Angular release, I sometimes get lost in solutions, because I want to utilize the next new shiny feature! I try to find ways to use it in projects, but that sometimes leads me down the rabbit-hole and shows me that I just have to KISS… Keep it simple, stupid! This is one of those times…
Make it lazy! 🛌
A requirement of the dialog was that it should only be shown to new users. Because of this, my team and I didn’t want the dialog to be packed into the main bundle since it decreases performance for existing users. It had to be lazy loaded! To do so we came up with two solutions. In the first one, we leveraged the new functionality of Angular 9 to load modules on demand. It looked something like this:
dialogComponent: Type<object>; ngOnInit(): void { if (conditionToLoad) { this.loadDialog(); } } private loadDialog(): void { import('./dialog/dialog.module') .then(mod => mod.DialogModule) .then(dialogModule => { this.dialogComponent = dialogModule.entryComponent; return this.compiler.compileModuleAsync(dialogModule); }) .then(factory => { factory.create(this.injector); }); } <ng-container *ngIf="dialogComponent"> <ng-container *ngComponentOutlet="dialogComponent"></ng-container> </ng-container>
However, we simply weren’t happy with how this worked. We chose to KISS, and went with a simpler and more elegant solution. We created a lazy loaded route, as we’re used to, and will open a dialog when navigating there. If really needed, we could block the route with a guard. Simple enough! We neatly separated the module from the bundle, and didn’t need a bunch of code and template-logic to glue it together. It also kept the main application more clean.
Keep it maintainable! 🛠️
As mentioned earlier, we wanted these complex flows to be maintainable. This basically means that we want to split each view, with its own logic, into its own component. Pretty sure you have heard of it: separation of concerns.
We didn’t want to write complex logic to get references to components and use a single ngComponentOutlet
, because we’d end up with cyclic dependencies (if you have a back-button for instance) when two components reference each other. Again we ended up with a simple solution: the Angular router. Such a powerful tool! Our lazy loaded DialogModule would have its own router-outlet
and therefore child routes.
The main route will render the wrapper component, which will open the dialog from a template, containing the outlet, and navigate to the first screen from there. Since I don’t want to clutter this article with a bunch of code snippets that take away from the story, here’s a simple StackBlitz.
This is a solution we were pretty happy with. It performed well, it is maintainable, components could be used in different flows and thus reusable, and we didn’t need to reinvent the wheel.
Conclusion
So what is this article really about? About how we can lazy load modules? How we can split a complex dialog into multiple components? Or how you should always consider to keep your logic simple? It’s all three actually!
As tech lead and developer it’s always fun to use the next big thing, but sometimes it’s better to stick to what you already know and use techniques that have already been present. That being said… I can’t wait for the next major Angular release and see what shiny new features they will roll out!
Who am I?
My name is Arjen Brandenburgh. I’m a senior frontend engineer and consultant working as Technical Lead for Techspire in the Netherlands. Working in the frontend field for almost 10 years and still loving it every single day. My main expertise lies with Angular. As a true geek I love to expand my knowledge and am proficient with Python, Ruby on Rails and AWS.
See all Techspire blogs?
See all Techspire news?
Follow our LinkedIn page: Techspire LinkedIn