Ionic Events replace with Angular Observables

2020-07-29 23:15发布

问题:

I understand Ionic events will be deprecated in next version. Currently I use events to executes functions in my parent page from sub components. Here is an example:

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');

How would I do the above using angular observables?

回答1:

you can use rxjs Subject for that so first create a common service eg.

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

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

}

then publish like

this.eventService.publishFormRefresh();

and subscribe in some componenet

this.subscription = this.eventService.formRefreshSource$.subscribe(data => {
  //do something here
});

and cancel subscription on ngOnDestroy

this.subscription.unsubscribe()


回答2:

USER EVENTS AS YOU USED TO IN IONIC 3. You just have to import this service/provider in a page you want to use it. Reference https://git.furworks.de/opensourcemirror/Ionic/commit/e5f2a18230f3ca3017f0302fb57ef275d0f63a8b

i

mport { Injectable } from '@angular/core';

export type EventHandler = (...args: any[]) => any;
@Injectable({
  providedIn: 'root',
})
export class Events {
  private c = new Map<string, EventHandler[]>();

  constructor() {
  //   console.warn(`[DEPRECATION][Events]: The Events provider is deprecated and it will be removed in the next major release.
  // - Use "Observables" for a similar pub/sub architecture: https://angular.io/guide/observables
  // - Use "Redux" for advanced state management: https://ngrx.io`);
  }
  /**
   * Subscribe to an event topic. Events that get posted to that topic will trigger the provided handler.
   *
   * @param topic the topic to subscribe to
   * @param handler the event handler
   */
  subscribe(topic: any, ...handlers: EventHandler[]) {
    let topics = this.c.get(topic);
    if (!topics) {
      this.c.set(topic, topics = []);
    }
    topics.push(...handlers);
  }

  /**
   * Unsubscribe from the given topic. Your handler will no longer receive events published to this topic.
   *
   * @param topic the topic to unsubscribe from
   * @param handler the event handler
   *
   * @return true if a handler was removed
   */
  unsubscribe(topic: string, handler?: EventHandler): boolean {
    if (!handler) {
      return this.c.delete(topic);
    }

    const topics = this.c.get(topic);
    if (!topics) {
      return false;
    }

    // We need to find and remove a specific handler
    const index = topics.indexOf(handler);

    if (index < 0) {
      // Wasn't found, wasn't removed
      return false;
    }
    topics.splice(index, 1);
    if (topics.length === 0) {
      this.c.delete(topic);
    }
    return true;
  }

  /**
   * Publish an event to the given topic.
   *
   * @param topic the topic to publish to
   * @param eventData the data to send as the event
   */
  publish(topic: string, ...args: any[]): any[] | null {
    const topics = this.c.get(topic);
    if (!topics) {
      return null;
    }
    return topics.map(handler => {
      try {
        return handler(...args);
      } catch (e) {
        console.error(e);
        return null;
      }
    });
  }
}


回答3:

You can create a service with a BehaviorSubject and you can pass data and subscribe to it wherever you want. In your case pass in the child and subscribe in the parent component.



回答4:

// make service using : ionic g service events

    import { Injectable } from '@angular/core';
    import { Subject } from 'rxjs';

    @Injectable({
    providedIn: 'root'
     })

     export class EventsService {

     private refreshData=new Subject();

     constructor() {};

     refreshData= this.refreshDatasObservable();

     refresh(message){
     this.refreshDatanext(message)
     }

   }

in app.module.ts

      import {EventsService} from './service/vents.service'; // add it to   provider 

       providers:[....
               EventsService
                ....
                ]

in your desired page subscribe and call like this

      import {EventsService} from './service/vents.service'; //import service                                   
          constructor(private eventsService:EventsService){

           //for refreshing or publishing
           this.eventsService.refresh('i am data');

          //for subscribing to change
          this.eventsService.refreshChatData$.subscribe(data=>{
            //do something
            })

           }


回答5:

For anyone else finding this in the same setup I was in, here's another possible route. We're looking at going from Ionic V3 -> V4 and know we have some headaches in store. One being events deprecated. I decided to look how the ionic-angular implementation of events was set up, and realized I kind of liked it. I made a service mimicking their implementation, hoping that replacing the ionic-angular Events later will be easier.

@Injectable()
export class Events {
    private _channels: { [key: string]: ((...args: any[]) => void)[] } = {};
    public subscribe(topic: string, handler: (...args: any[]) => void) {
        if (!this._channels[topic]) {
            this._channels[topic] = [];
        }
        this._channels[topic].push(handler);
    }
    public publish(topic: string, ...args: any[]) {
        if (this._channels[topic] && this._channels[topic].length) {
            this._channels[topic].forEach(handler => handler(...args));
        }
    }
    public unsubscribe(topic: string, handler: ((...args: any[]) => void) = null) {
        if (!handler) {
            delete this._channels[topic];
        }
        const index = this._channels[topic].indexOf(handler);
        if (index > 0) {
            this._channels[topic].splice(index, 1);
        }
        if (!this._channels[topic].length) {
            delete this._channels[topic];
        }
    }
}

I know this doesn't use Observables, but wanted to offer a different solution for anyone else hoping for a drop-in fix for ionic-angular/events I simplified / removed some of their code, if there's any concerns let me know.



回答6:

This can be dealt with by using an Events service as pointed out in this medium post Dealing with breaking change in Ionic 5

The author illustrates how one can refactor code with minimal ease and maintain the flow.