Search code examples
angularobserver-patterneventemitter

Create EvenEmitter dynamically in Angular


I am trying to implement Observer kind of pattern using EventEmitter in Angular.

For example I have a publisher called component1Publisher

component1Publisher: EventEmitter<any> = new EventEmitter();

publishing the events like below

this.component1Publisher.emit(data);

Subscribe in multiple components like below

component1Publisher.subscribe((data:any) => {
            //do something here;

          });

You can see the publisher is statically declared.In my project publisher and subsciber is completely dynamic. So I want to create this EventEmitter instance and subscriber based on my unique component name.Something like below

createEvent(uniqueName:string){
//new unique eventEmitter
}

My problem I have a reusable framework to create a dashboard that user can bring widgets provided by the framework itself. So I am trying to communicate between widgets to make it interactive. For example when you click widget should notify all the other widgets.To do that I am planning to tell the user to provide an dynamic service. In dynamic service have some method like onWidgetClick(), which will fire by framework by dynamically resolve the service. In that method user can create an event to publish. It will notify all the widgets with the new data and framework will update it with new data(all the reusable widgets have the subscriber) .

Could you please help me on this or suggest is there any alternative to do this kind of implementation


Solution

  • I would define a map (i think that's the right term?) inside whatever component/directive 'this' refers to. Something like:

    componentPublishers: { [index:string] : EventEmitter<any> } = {};
    

    That code just says, I'm creating an object that will contain EventEmitters, all identified by strings (you can use strings or numbers as identifiers, but in nearly all cases people use strings).

    Then you can add EventEmitters to it with any name you like, by using the bracket notation (which is used in JS to add new properties to objects).

    createEvent(uniqueName:string){
      this.componentPublishers[uniqueName] = new EventEmitter<any>();
    
     // and maybe you'd want to return it?
    
      return this.componentPublishers[uniqueName];
    }
    

    After its created, you should be able to access it like normal without bracket notation. By using the map as I have shown above, typescript will know that any property of the componentPublishers object will be an event emitter.

    this.componentPublishers.myNewEventEmitter.emit('some value');
    

    Note: you'll get errors if you try to use bracket notation to add properties to the object, if you don't instantiate it first. I just instantiated it as an empty object.

     ... =  {};
    

    So make sure to do that.

    You could also just create an array of event emitters and push new ones into it, but that gets clumsy very quickly bc you have to keep track of the indices. By using a map you can access them by name like a regular object, and still have Typescript's static type checking.