Не удалось выяснить правильный синтаксис EventEmitter или Observable в службах Angular2

Мне трудно найти много примеров/руководств по использованию наблюдаемых в службе Angular2. Есть вещи для связывания html-шаблонов с EventEmitter, но это не подходит для службы.

Одна из главных движущих тем — уйти от промисов в Angular2, но я не могу правильно понять новый синтаксис.

Что я делаю

  • У меня есть служба FirebaseAuth, которую можно внедрить в другие службы или компоненты.
  • У меня есть функция, которая выполняет асинхронный вызов firebase, в моем примере для создания пользователя
  • Я хочу вернуть Observable(для замены обещания), который другие службы могут использовать для выполнения других действий, таких как создание профиля, когда это будет разрешено.

Я согласен, если обещания являются лучшим решением для этого примера, но я хотел бы выяснить, что такое Наблюдаемый способ.

Моя служба:

/*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);
        }
       })  
    })
  }
};

Как мне переписать это, чтобы использовать Observable и/или EventEmitter?


person Dennis Smolek    schedule 28.10.2015    source источник


Ответы (1)


На самом деле это почти то же самое, есть несколько изменений

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

И тогда вы можете suscribe к нему (subscribe эквивалентно then).

Вот plnkr с примером использования Observables

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

EventEmitter, с другой стороны, является Subject (документация немного отличается, поскольку angular2 перешел на последнюю версию, но все равно понятно).

_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);
        }
    })  
}   

Вот plnkr с примером использования EventEmitter.

Разница в суперкоротком: Observable начинает выдавать данные, когда находит подписчиков; Субъект выдает информацию независимо от того, есть подписчики или нет.

Примечание

В примере с EventEmitter я использовал toRx(). Это раскрывает Subject, но он подвергается рефакторингу, и нам больше не понадобится toRx().

Полезные ресурсы Обновлено

Подробное изучение RxJS, Бен Леш, Конференция AngularConnect 2015.

Спасибо Робу Вормалду за указание на это

Вы можете посмотреть выступление Сары Робинсон и ее демонстрационное приложение и посмотрите, как оно работает здесь

person Eric Martinez    schedule 28.10.2015
comment
Хороший ответ о семантике Observable vs Subject. В частности, для firebase см. также talk Сары Робинсон по AngularConnect и демонстрационное приложение для некоторых конкретных трюков для Firebase и Angular2. - person robwormald; 28.10.2015
comment
Чрезвычайно полезно!! Большое спасибо за это, Эрик, я немного пытался сделать это правильно, и это именно то, что мне было нужно. В приведенном вами примере кажется, что использование Observable будет лучше, чем EventEmitter. Что может быть причиной, чтобы использовать его вместо этого? Что касается выступления Сары, то это отличное выступление, и я смотрел его несколько раз, и мне нравится идея использовать трубку. Единственная проблема заключается в том, что ее пример тесно связан с компонентами, и мои службы также должны взаимодействовать с Firebase. - person Dennis Smolek; 28.10.2015
comment
Ответ будет скорее личным мнением, чем техническим ответом, поэтому я прошу прощения за это. В документации EventEmitter говорится: Use by directives and components to emit custom Events., в других словами, его цель - генерировать события между компонентами/директивами, его целью не является обработка более сложных сценариев. Конечно, вы можете использовать его по своему усмотрению, но я думаю, что вы будете использовать его неправильно. - person Eric Martinez; 28.10.2015
comment
ЭрикМартинес и @robwormald: Я пытался импортировать обычный Observable, но он работает неправильно. Я заметил, что Eric's Plunker импортирован напрямую из RX, поэтому я тоже попробовал это, но typescript не может найти модули, я видел это: github.com/ReactiveX/RxJS/issues/528 это именно та проблема, с которой я столкнулся. Тем временем я принес его с var, но возвышенное не в восторге от этого. Любой совет? - person Dennis Smolek; 29.10.2015
comment
@DennisSmolek извините за поздний ответ. На данный момент вам придется придерживаться этого обходного пути, пока он не будет официально исправлен и не будет отслеживаться в проблеме, о которой я упоминал, о рефакторинге EventEmitter. - person Eric Martinez; 30.10.2015
comment
Последние версии @reactivex/rxjs имеют фиксированные типы, но я думаю, что еще есть некоторые странности, которые нужно решить. Я бы npm install @reactivex/rxjs, а затем (при условии, что вы используете TypeScript) import: import {Observable} from '@reactivex/rxjs/dist/cjs/Rx' Это должно дать вам соответствующие определения типов и обойти проблемы со значениями по умолчанию. - person robwormald; 31.10.2015
comment
Кроме того, как говорит Эрик, EventEmitter на самом деле является абстракцией Angular, и его следует использовать только для создания пользовательских событий в компонентах. В противном случае просто используйте Rx, как если бы это была любая другая библиотека. - person robwormald; 31.10.2015
comment
Спасибо за новость Роб! Я хотел добавить, что использование стандартного наблюдаемого Rx НЕ правильно запускает обнаружение изменений и НЕ МОЖЕТ использоваться с текущим асинхронным каналом. (11/15/15) Мои вызовы firebase работают правильно, но мое представление не обновляется до тех пор, пока не сработает другое событие. Напоминает мне о необходимости вручную вызывать $apply, что отстой... - person Dennis Smolek; 02.11.2015
comment
Текущая реализация асинхронного канала @DennisSmolek работает не с Observables, а с Promises (поправьте меня, если я ошибаюсь, @robwormald). В любом случае, это было исправлено, но еще не выпущено, см. #4893. - person Eric Martinez; 02.11.2015