Do I understand the routing wrongly?

Hello there,

I’m an Angular developer that recently got into Ionic and I have some topics about the Ionic lifecycle implementation in combination with Angular.

The problems I’m describing here are not really noticed by the community, so I’m just wondering if I’m doing something horribly wrong or if the described points are valid to some degree.

Angular async pipes

Async pipes are only unsubscribed when the page is completely destroyed (e.g. with a navigate to root). Following this, they will only subscribe again when creating a completely new instance of the component. This could make them obsolete when trying to get new values each time a component gets initiated.

One “solution” I came around is to initialize the observable objects anew in the ionic lifecycle hooks to force the default Angular behavior. The disadvantage is that this will generate many waiting subscriptions in the background, even though they only wait for destruction.

OnDestroy in child components

Let’s say we have an Ionic page with a child component. The child component is a qr-code scanner with camera access from an external library. The camera stream is opened when navigating to the component. It is supposed to close again when navigating further.

The problem is that due to the Ionic route caching, the parent page is cached. Which leads to the OnDestroy lifecycle hook of the child not being called. The child now doesn’t know if it’s being used or not and doesn’t deactivate the camera stream when navigating to the next page. What follows is an always open camera stream.

A workaround here would be to use a condition that is only active when the parent page is open and put a *ngIf around the child component.

OnInit in child components

When having a child component that relies on values provided by the parent page, the lifecycle execution order can get difficult.

Default order : Parent ngOnInitChild ngOnInitParent ionViewWillEnterParent ionViewDidEnter

If the parent sets something like resourceId in it’s ionViewWillEnter and provides it for their children, we can’t rely on that value in the children as it may not be set when the children’s ngOnInit is executed. I would also assume this could get even worse as the initialization of the child is executed before the parent is initialized.

Please note that the ionic lifecycle hooks in the child are not executed at all, as described here.

EDIT: I also asked this question on StackOverflow.

The phrase “horribly wrong” seems a bit strong, but I do think there is one fundamental change you could make to your world view that would melt all of these concerns away (with one potential exception):

Consider lifecycle events strictly a view layer concept. Never try to do data layer things in them.

If you haven’t seen this post before, it’s the first thing I remember writing on this topic, so some of the syntax may be obsolete at this point, but I believe the basic concept still stands up: manage your business object data flow independently of how the framework chooses to present the view layer.

The idioms in that post should take care of your async pipe issue, and the last one involving OnInit, because you will no longer be using ionViewWillEnter to set resourceId.

The middle one with the camera stream is the potential exception. Ordinarily, views shouldn’t care whether or not they’re being cached out of sight, but I can see how something like an external camera input would be an exception there. I think your idea of getting the element that provides the camera stream out of the DOM when it shouldn’t be there sounds like a reasonable approach.

The fundamental problem, as I see it, is that bare scalar properties have no way of communicating when their values are reliable. Trying to achieve that ability (to convey the notion of “freshness”) is arguably the #1 reason that Angular is so tightly coupled with RxJS in the first place.

Every time you share bare scalar properties (like your resourceId) that can change, you’ve laid a trap for yourself. You have to come up with some out-of-band way to signal changes, lifecycle events seem close enough, you hitch your wagon to them, the wheels come off, and before you know it, you’re dead of dysentery on the Oregon Trail.

When these data are no longer shared nakedly, but instead as Observables, the problem goes away. You can push updates (with next on a Subject, for example) whenever needed, and all clients receive updates seamlessly.

For technical reasons, there are things you can’t do in component constructors. Everything that can be done, I would suggest doing. That leaves things like dependencies on bound input properties that haven’t been bound yet for lifecycle events, and in that instance, Angular will manage the data freshness for you.

2 Likes

Thank you very much for the comprehensive answer!

I never thought about it that way for now.

Consider lifecycle events strictly a view layer concept. Never try to do data layer things in them.

really made it for me.

Currently, most of my app’s components are requesting initial data when the view is opened, mostly via observables. But the initiator is the view itself that requests the data when its visible and forces execution in the observable.

Thinking this the other way around makes much more sense in terms of an app.

Just one little follow up question:
Do you think an async pipe that continues the subscription when a view is cached in the background could be a problem? I mean, doesn’t the UI may change in the back even when a user is not actively viewing it using this approach?

Ionic’s router does not work like angular’s router. Angular’s router destroys the component when navigated to a new component. Ionic pages are still active as long as you navigate forward. If you navigate backwards, then page is destroyed.

Page A → Page B → Page C

When you navigate to page C from page B, A and B are still active and the subscriptions they use are also still listening for events. So, you can send a message from C to A that can change UI.

2 Likes

Yeah, I’m just thinking if this could be a problem in terms of performance. But a well designed app should not get to its limits due to the low depth of the navigation tree I guess.

I have not found it to be so, no.

I’m pretty sure the browser is smart enough to optimize this away if you’re concerned about performance implications. If you’re worried about surprising the user, again not a problem. In fact, I’d argue that it would make for a better UX, because there’s less extra work to do to get the relevant components ready for display once they are ready to become visible again.

1 Like