Best way to replace deprecated Events

Currently I’m using events to access MyComponent’s functions from MyPage. So, when I click a button in MyPage a function from MyComponent is fired. I’m using this way:

In MyPage:

randomFunction() {
	this.events.publish('functionCall:myPageRandomFunction');
}

In MyComponent:

this.events.subscribe('functionCall:myPageRandomFunction', (eventData) => {
    //Doing things here
}

How I’m supposed to replace this methodology now that the events is deprecated? Should I use Observables? If yes, how can I ensure the same functionality?

By makig your own service that uses Obervables indeed, like you said.

Building a BehaviorSubject service that emits a string.

@rapropos has a great post for this: Ionic 4 Tab to page then back to Tab did not trigger ionViewWillEnter [Solved]

Even though, the better way would be not to do that and use router events or a more strictly typed emitter for whatever async stuff you want to capture or share as state within your app…

The string Event emitter is a gateway to event-soup, if you actually did not think of the above before yourself

Tom

Ok, I believe now I have a very superficial understanding of how BehaviorSubject works, but I still can’t implement it correctly. I cannot understand how I should receive the information from within the component.

Example:

randomFunction() {
	//this.events.publish('functionCall:myPageRandomFunction');
	let subject = new BehaviorSubject("randomFunction");
}

How I’m supposed to receive the randomFunction string inside MyComponent when randomFunction() is fired?

Lacking @Tommertom’s gift for diplomacy, I’m going to state more emphatically that there has to be a better way to achieve the overall goal than reinventing the flaws of events.

I cannot tell you how many thousands of times I have misspelled string constants and sent myself down needless rabbit holes. Computers are way better than humans about detecting this, so I think we should defer to them on this front.

There is another gigantic problem with this design: it obliterates encapsulation. Pages should not be calling functions in components. Period. Pages should not even know what functions components have. Components, likewise, should not know anything about the environment in which they are hosted. If I can’t strictly follow this rule, then I decide that I have made a mistake about where to draw the lines between page and component.

There are two good ways to tell a component something from outside: changing the value of a bound @Input property, and via a service that the component injects. I have no idea which of these makes more sense in your situation, because we are deep in XY problem territory here.

The post about managing user profile state that @Tommertom quoted describes how one would do this when the message is “some application-level state object has changed, and component needs to change how it’s rendering to reflect that”. An example of the other major category would be a situation where you have a master/detail display on a single page, in which case declaring an @Input property on the detail component would make more sense, and then when a new item is selected, point the @Input at the new selection. ngOnChanges will fire in the child component.

1 Like

There is always a challenge is guiding people into coding heaven and i sometimes prefer a slow (maybe painful?) way :grinning:

But sometimes some elementary stuff is needed to equip yourself with knowledge to avoid rabbits and holes

In this case I recommend to follow the Tpur of Heros case on angular.io before anything else to learn the basic principles of angular development

While that not resolves this question immediately, it will empower you to resolve and phrase ypur problem

And find that Google is your best friend, more then anyone else

Thank you very much both of you, as always, you helped me a lot.

This is very sad to know, I believe the core of my project is like this. Adding a little more information about my problem, my main page in this case is basically an UI with buttons and menus. My component is an OpenLayers map and is at the center of all this UI, most of the app’s functions are fully linked with that component’s functions and are currently accessed through Events. I defined the OpenLayers map as a component because it was a suggestion someone in Stack Exchange GIS gave me very early in my app development.

But at least I manage to put all of you say together and made it work without using events. From this suggestion from @rapropos and this example I created a service only to listen the functions from the page and link it to the component. What I did:

In MyService:

private myObservable = new Subject<string>();
currentData = this.myObservable.asObservable();
serviceFunction() {
	this.myObservable.next("randomFunction");
}

In MyPage:

randomFunction() {
	this.randomService.serviceFunction();
}

In MyComponent:

this.randomService.currentData.subscribe(value => {
	console.log("randomFunction was fired!");
});

Which is now working as expected! Please, if there is still a problem with this method tell me so I can fix it, otherwise I will use this service in the future to replace the Events.

1 Like

There is a nice chapter on component interaction on angular.io

Pending the importance of your for your users, taking some time studying it will help in the future

Would it be possible to define an interface that encapsulates everything about the current state of the “world” that the map is displaying?

interface Place {
  latlng: LatLngLiteral;
  name: string;
  icon?: string;
}

interface Layer {
  name: string;
  visible?: boolean;
  places: Place[];
}

interface World {
  title: string;
  layers: Layer[];
}

If this is doable, then perhaps it would be possible to replace many, if not all, of the page-to-component RPC calls with calls to a mutually injected WorldService that transform the World. The map component would respond to changes in the World.

While it might seem some combination of silly, extra work, or pointless, when I draw firm lines of responsibility like this, several good things happen:

  • the code becomes easier to read and somewhat self-documenting
  • it becomes more obvious where bugs lie
  • I can change things in one place later without breaking others as much
  • each part of the app can be isolated and tested separately
  • subtle assumptions I was making implicitly have to be made explicit and therefore either committed to or designed away
  • later features likely to be requested get easier (imagine how trivial it will be to save and restore map state, or share maps with other users)
1 Like