Bundling and injecting components in Ionic 2 / Angular 2

Posted on

In Ionic 2, one of the nifty concepts is a ‘navigation stack’, where your screens/pages (actually Angular 2 components) simply push and pop from the stack as the user navigates from the root page. Explained briefly in this video, it’s logically better than doing it through UI router in Angular 1 – but also heaps, heaps easier to code as well.

Import hell

A particular topic of consternation for the abstraction nerds lies in the need to import the component every time you need to do navigation between non-root components. You have to do something like this (in TypeScript – also see Random thoughts on having to use TypeScript):

This allows you to write a [navPush]="userPage" binding in your template file:

or perform controller-side navigation through an event-bound method, which lets you code more logic into the method before navigating:

BUT. Coming back to the abstraction nerd thing, you have to import the component (usually from a far-away corner of your file structure) and then create a property to store the reference.

Injecting components through an Injectable

Surely we can do this better?

What if we chucked all the templates into an @Injectable() …? I went down this path – about 20 minutes of refactoring later, it didn’t work. Spoiler alert: And it can’t work, because of circular imports. Let me demonstrate:

Let’s say we created an injectable (aka Angular service, if it helps with thinking) where we mass-import all the components. I shall simply call it ScreenShack:

Therefore I only have to go through the pain of really long component import locations just once – in this file. And all you’d need to do is inject via the constructor, and make it public and the template will be able to pick it up.

But here’s what happens when you ionic serve:

It’s because of a circular import that occurs – AccountComponent imports ScreenShack which in turn imports AccountComponent, and so on. The Angular 2 injector probably detects circular imports and says nope, not instantiating that… and so your app won’t work till you remove that.

So there’s no better way

So unfortunately, I don’t really know of a better way to consolidate the components. If you have a technique or a solution, let us know!

The @SkipSelf() decorator is a way to avoid cyclic dependencies for injectables, but it wouldn’t do the same for components.