Going from Ionic Events to Observables

With Ionic events being deprecated I am having difficulties going to observables.

For a simple page refresh that is executed in a subcomponent of the page, I would:

In my main page it subscribes to the event to refresh:

constructor(){
  this.eventFormRefresh = (obj) => {
    this.fetch(obj.isReset);
  };
  this.events.subscribe('form:refresh', this.eventFormRefresh);
}

ngOnDestroy(): void {
  this.events.unsubscribe('form:refresh', this.eventFormRefresh);
}

In a subcomponent I activate the refresh by publishing the ‘form:refresh’ event as so:

this.events.publish('form:refresh');

To do the above via RXJS I was shown a solution using subjects as below:

Common Service:

@Injectable()
export class EventService{
  private formRefreshAnnouncedSource = new Subject();
  formRefreshSource$ = this.formRefreshAnnouncedSource.asObservable();

  publishFormRefresh(){
    this.formRefreshAnnouncedSource.next()
  }

}

then publish like

this.eventService.publishFormRefresh();

The question I have is the above method the angular way to use observables?

I have seen tutorials that use new Observable instead but do not know how to implement in the similar fashion as ionic events.

Appreciate if I can get any guidance on this as I do find observables confusing to say the least. Thanks in advance!

Sure, it’s possible to use Observables as a drop-in replacement for Events, and your implementation isn’t terrible, but how about taking this as an opportunity to rethink the design to take advantage of the more expressive potential that Observables provide?

Especially given the example you’ve posted here of “form refresh”, this idiom sounds like a way to implement what appears to be the larger goal of “have a single source of truth for business objects, with all changes to it propagated to any components that present them to the user”.

I don’t know if the following applies to you, but it sure did to me, and took me about a year of banging my head consistently against a wall when I started with Ionic:

I am a systems programmer, having written a bunch of imperative stuff in C/C++/Pascal, even all the way back to PowerPC, 68000, and 6502 assembly and machine language. So my go-to mindset was always “how do I make X happen” or “how do I tell Y to do Z?”. I still hear questions in this vein every day here in these forums, and remain firmly convinced that the proper answer to them, while probably frustrating to the questioner in the short term, is “change your entire way of looking at the world”.

Web apps aren’t imperative, they’re reactive, and your EventService, as I see it, is an attempt to use a reactive language to try to say imperative things, which (at least in my experience) results in Vogon poetry.

Mobile apps (web apps in general) tend to have two primary purposes: (A) to present information that can often change in unpredictable ways to a user, and (B) to convey information from the user to some external place (even if that just constitutes “remembering” that information for later).

Action-at-a-distance introduces orders of magnitude of complexity in testing and debugging and opportunities for unforseen interaction problems in apps, so I believe it’s best to both minimize how much it’s done and make it targeted as precisely as possible. Instead of “hey everybody, refresh your form”, maybe things can be restated as either the type A idiom described in the earlier link, or as a type B idiom that also doesn’t require an event. Say a user presses a refresh button. The click handler for that button, instead of firing an event off into the void, would be much better served first finding the single source of truth that is stale, and then calling a refresh method in it. DI is perfect for the first part of that, which is the only hard part, as then all one has to say is injectedService.refresh().

So sorry for the wall of text, but maybe it will inspire you (or some other poor sap who bothered to read this far) to rearchitect the app so that nobody would ever know that Events once lived there.

Thanks rapropos I always value your feedback in this forum.

First and foremostly the current solution of using the Subject to publish a change, you’re saying is ok? I am having a frustrating experience with the constant deprecations/changes in Ionic so at the very least I would like to implement a solution that is still considered the ionic/angular way not to come up against this in 12 months time.

Secondly in regards to your insightful comment you are absolutely right. Rather than casting a SOS message out, I should be considering a direct approach. The only thing is when it become inter communication between components the only way I know to achieve this was via events unless it’s a direct child then I would use the @ouput event emitter function.

Based on your suggestion on using DI I sort of get where you are coming from but still having issues how this would be implemented in practice.

Perhaps if I elaborate on the above example and say the child component publishing the event is actually a modal controller ie popup window. In this popup window there are some basic UI settings to the parent page. When I click save, the modal is dismissed and I publish the event ‘form:refresh’ to the parent page to execute ngOnit (run some functions specific to the page view only, refresh the view etc no API calls). How would you do it with a more direct method?

PS only a conceptual example but if I can have guidance on this and I understand it there maybe some hope for me yet!

Thanks in advance.

Its unfair to blame ionic for all dependency updates/changes third parties do. Like the angular team, maintainers of rxjs, angularfire, etc

U r using cutting edge technologies in a highly changing world. Meaning u will continue to learn on a monthly basis.

If u dont like that, maybe try cobol?

Perhaps read my comment properly, I said it was frustrating not blaming. I am seeking a solution that is considered best practice so I don’t need to worry about changes in 12 months time…But thanks for the comedic relief! :wink:

1 Like

I think here is the fundamental issue. I consider ngInit to be an extension of the constructor, doing things that would otherwise be done in the constructor to put the component into a sane initial state but simply can’t be for whatever technical reason. It should never need to run more than once each time the component is instantiated.

I’m not really clear on what this means, so…

Let’s say that popup has a “theme” setting that affects what colors are used for various stuff on the page that popped it (and, perhaps, everywhere else in the app). What I would do is define a ThemeService that exposes an Observable<Theme> just like the ProfileService in the post I linked to earlier. A Theme could have a name and a bunch of colors in it (yes, this particular example could be better done using just CSS variables, but it’s the first idea that came to mind when you said “basic UI settings”).

Any page that cares would inject the ThemeService, subscribe to its watch() Observable, and in that subscription react to theme changes. This obviates the need to imperatively re-call ngInit completely, and designs away the “event” mentality. All the popup has to do is also inject ThemeService, and directly or indirectly cause a next() call on the BehaviorSubject<Theme> that underlies the ThemeService's Observable<Theme> representing the current theme.

1 Like

Thanks after doing further research and your response. It makes sense now! Also your recommendation in this post also helped. In short rather using the event mechanism to say hey there is change do something. Focus on what is actually being changed and use observables to propogate the change directly to the page/component.

1 Like