magarcia

Events in Angular2

2 min read

During the migration of the feed component, I found some lines that I didn’t know how to code it on Angular 2. The following code is an extract of the code I’m talking about:

if (!feed.isLocalScreen) {
  // Until this timeout is reached, the "you are muted" notification
  // will not be displayed again
  var mutedWarningTimeout = now();

  scope.$on('muted.byRequest', function() {
    mutedWarningTimeout = secondsFromNow(3);
    MuteNotifier.muted();
  });

  scope.$on('muted.byUser', function() {
    // Reset the warning timeout
    mutedWarningTimeout = now();
  });

  scope.$on('muted.Join', function() {
    mutedWarningTimeout = now();
    MuteNotifier.joinedMuted();
  });

  scope.$watch('vm.feed.isVoiceDetected()', function(newVal) {
    // Display warning only if muted (check for false, undefined means
    // still connecting) and the timeout has been reached
    if (newVal && feed.getAudioEnabled() === false && now() > mutedWarningTimeout) {
      MuteNotifier.speaking();
      mutedWarningTimeout = secondsFromNow(60);
    }
  });
}

As you can see in the snippet, if the condition is true then the directive listens for events of type muted.byRequest, muted.byUser and muted.Join. The code that involves this events is easy, without any type of complexity (for now we ignore the $watch).

But, wait a minute, I have read the documentation of Angular 2 like a hundred times and I don’t remember nothing about “events” with Angular 1.X style. That’s because it not exist. Angular 2 don’t have a way to make events like in Angular 1, so I have to find a solution. After a search for a solution, I found this entry in laco’s blog.

Broadcaster

Basically, the idea is to make a service that implements the $broadcast and $on a method as we had in $rootScope. To do this we use Observables, very importants in Angular 2, and for this case, we use a Subject.

import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/map';

interface BroadcastEvent {
  key: any;
  data?: any;
}

export class Broadcaster {
  private _eventBus: Subject<BroadcastEvent>;

  constructor() {
    this._eventBus = new Subject<BroadcastEvent>();
  }

  broadcast(key: any, data?: any) {
    this._eventBus.next({ key, data });
  }

  on<T>(key: any): Observable<T> {
    return this._eventBus
      .asObservable()
      .filter(event => event.key === key)
      .map(event => <T>event.data);
  }
}

So, now we can start to use events like in the example:

// child.ts
@Component({
    selector: 'child'
})
export class ChildComponent {
  constructor(private broadcaster: Broadcaster) {
  }

  registerStringBroadcast() {
    this.broadcaster.on<string>('MyEvent')
      .subscribe(message => {
        ...
      });
  }

  emitStringBroadcast() {
    this.broadcaster.broadcast('MyEvent', 'some message');
  }
}

How I solved the problem?

I didn’t. These events are only to show the user information pop-ups about when he is muted, so it’s not a critical feature. By now these events are fired and listen in different components, and some of it still implemented in Angular 1.4.

This is a solution I want to share with you, but I’m not sure if this will be the way that I will use to solve the problem. Because these events probably won’t be necessary when I reimplement the MuteNotifier.