18

I'm having a hard time finding much in the way of examples/guides for using observables in an Angular2 service. There is stuff for html templates binding with EventEmitter but that doesn't seem right for a service.

One of the big driving themes is getting away from Promises in Angular2 but I can't seem to get the new syntax correct.

What I'm Doing

  • I have a FirebaseAuth Service that can be injected into other services or components.
  • I have a function that does an async call to firebase, in my example to create a user
  • I want to return a Observable(to replace the promise) that other services can use to do other things like create a profile when this is resolved

I'm fine if promises are the best solution for this example but I'd like to figure out what the Observable Way is.

My Service:

/*DS Work on firebase Auth */
import {Injectable} from 'angular2/angular2';

@Injectable()
export class FirebaseAuth {
  ref = new Firebase('https://myfirebase.firebaseio.com');
  //check if user is logged in
  getAuth(): any {
    return this.ref.getAuth();
  }

  //register a new user
  createUser(user: any): Promise<any> {
    return new Promise((resolve, reject) => {
      this.ref.createUser(user, function(error, userData) {
        if (error) {
          reject(error);
          console.log('Error creating user:", error');
        } else {
          resolve(userData);
          console.log('Successfully created user account with uid:', userData.uid);
        }
       })  
    })
  }
};

How would I rewrite this to use Observable and/or EventEmitter?

Dennis Smolek
  • 8,480
  • 7
  • 30
  • 39

1 Answers1

22

Actually it's almost the same thing, there a few changes

 createUser(user: any): any {
    return new Observable.create(observer => {
      this.ref.createUser(user, function(error, userData) {
        if (error) {
          observer.error(error);
          console.log("Error creating user:", error);
        } else {
          observer.next('success');
          observer.complete();
          console.log('Successfully created user account with uid:', userData.uid);
        }
       });  
    })
  }

And then you can suscribe to it (subscribe is the equivalent of then).

Here's a plnkr with an example using Observables

constructor() {
    this.createUser({}).subscribe(
        (data) => console.log(data), // Handle if success
        (error) => console.log(error)); // Handle if error
}

EventEmitter on the other hand is a Subject (documentation differs a little bit since angular2 moved to the last version, but it's still understandable).

_emitter = new EventEmitter();
constructor() {
    // Subscribe right away so we don't miss the data!
    this._emitter.toRx().subscribe((data) => console.log(data), (err) => console.log(err));
}
createUser(user: any) {
    this.ref.createUser(user, function(error, userData) {
        if (error) {
          this._emitter.throw(error);
          console.log('Error creating user:", error');
        } else {
          this._emitter.next(userData);
          this._emitter.return(); This will dispose the subscription
          console.log('Successfully created user account with uid:', userData.uid);
        }
    })  
}   

Here's a plnkr with an example using EventEmitter.

The difference in super short : Observable starts emitting the data when it finds subscribers; Subject emits info whether there are subscribers or not.

Note

In the EventEmitter example I used toRx(). This exposes the Subject but it's being refactored and we won't need toRx() anymore.

Useful resources Updated

RxJS In-Depth by Ben Lesh in AngularConnect's 2015 conference.

Thanks to Rob Wormald for pointing out this

You can see Sara Robinson's talk and her demo app and see it running here

Eric Martinez
  • 31,277
  • 9
  • 92
  • 91
  • 2
    Good answer on the semantics of Observable vs Subject. For firebase specifically, see also Sara Robinson's AngularConnect [talk](https://www.youtube.com/watch?v=RD0xYicNcaY) and [demo app](https://github.com/sararob/angular2base) for some specific tricks for Firebase and Angular2. – robwormald Oct 28 '15 at 03:21
  • **Extremely Helpful!!** Thank you very much for this Eric, I was struggling a bit to get it right and this is exactly what I needed. In the example's you provided it seems using Observable would be better than EventEmitter. What would be a reason to use it instead? As to Sarah's talk,It's a great talk and I watched it a few times actually and love the idea of using a pipe. Only problem is her example is tightly coupled to components and my services need to talk to Firebase too. – Dennis Smolek Oct 28 '15 at 15:03
  • 1
    The answer would more a personal opinion than a technical answer, so I apologize for that. In the [EventEmitter documentation](https://angular.io/docs/ts/latest/api/core/EventEmitter-class.html) says : `Use by directives and components to emit custom Events.`, in other words its purpose it's to emit events between components/directives, it isn't its purpose to handle more complexes scenarios. Of course, you can use it as you want, but I think you would be misusing it. – Eric Martinez Oct 28 '15 at 15:14
  • EricMartinez & @robwormald: I tried importing the regular Observable but it's not working correctly. I notice Eric's Plunker imported direct from RX so I tried that too but typescript cant find the modules, I saw this: https://github.com/ReactiveX/RxJS/issues/528 which is the exact issue I'm having. In The meantime I've brought it in with var but sublime isn't happy about it.. Any advice? – Dennis Smolek Oct 29 '15 at 00:48
  • @DennisSmolek sorry for the late answer. For now you'll have to stick to that workaround until it gets officialy fixed and it's being tracked in the issue I referenced about EventEmitter being refactored. – Eric Martinez Oct 30 '15 at 13:40
  • The latest versions of @reactivex/rxjs have fixed types, but there's still some strangeness to be worked out, I think. I would `npm install @reactivex/rxjs`, and then (assuming you're using TypeScript) import: `import {Observable} from '@reactivex/rxjs/dist/cjs/Rx'` That should give you appropriate type definitions and bypass the issues with defaults. – robwormald Oct 31 '15 at 06:13
  • 3
    Also, as Eric says, EventEmitter is really an Angular abstraction, and should be used pretty much only for emitting custom Events in components. Otherwise, just use Rx as if it was any other library. – robwormald Oct 31 '15 at 06:16
  • Thank you for the update Rob! I wanted to add that using the standard Rx observable is **NOT** firing the change detection correctly and **CANNOT** be used with the current async pipe. _(11/1/15)_ My firebase calls are working correctly but my view isn't getting updated until a different event fires. Reminds me of having to manually call $apply which sucks... – Dennis Smolek Nov 02 '15 at 16:13
  • @DennisSmolek current implementation of async pipe does not work with Observables but Promises (correct me if I'm wrong @robwormald). Anyway, this has been fixed but not yet released, see [#4893](https://github.com/angular/angular/pull/4893) – Eric Martinez Nov 02 '15 at 16:48