Как получить Observable для данных из запроса Firestore в Angular 7?

У меня есть метод в моей службе, внутри которого я пытаюсь вернуть Observable объекта User. Вот мой код -

 constructor(private firestore: AngularFirestore,
    private db: AngularFireDatabase) { }

 /**
   * get user with given email, if not return null
   * 
   * @param email email to fetch
   */
  getUserByEmail(email: string): Observable<User> {

    var user: User;

    let userRef = this.firestore.collection("users").ref.where('email', '==', email);

    userRef.get().then(res => res.forEach(userDoc => {

      user = userDoc[0].data() as User; // since email IDs are unique, I want the 0th element.
      user.id = userDoc[0].id;

      console.info(user); // has the data

      // return user; // doesn't work, not the right thing

    }));

    console.info(user); // undefined, since call is async

    return of(user);
  }

Внутри компонента мне нужны эти данные для следующих шагов, поэтому я сделал это -

checkEmail() {

    var user$: User

    this.us.getUserByEmail(this.existingUserForm.value.email).subscribe(user => {

      console.info(user);

      if (user) {

          this.msg$ = "success";
          // next steps
      }
      else {
        this.msg$ = "User with this email does not exist!";
      }

    });

  }

Я не уверен, как вернуть наблюдаемое из моей службы, чтобы я мог использовать данные в своем компоненте. И правильно ли это делать то, что я пытаюсь сделать?

Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
5
0
11 203
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Если вам нужна наблюдаемая из Firestore, вам нужно вернуть .valueChanges () в AngularFirestoreCollection. Прочтите этот документ для справки: https://github.com/angular/angularfire2/blob/master/docs/firestore/querying-collections.md.

В вашем сервисе выполните: (для ясности я объявил переменные collection и user $).

getUserByEmail(email: string): Observable<User> {
const collection = this.firestore.collection<User>('users', ref => ref.where('email', '==', email))
const user$ = collection
  .valueChanges()
  .pipe(
    map(users => {
      const user = users[0];
      console.info(user);
      return user;
    })
  );

return user$;
}

Если вы хотите, чтобы идентификатор был у вашего пользователя, вам нужно использовать snapshotChanges (). Иногда проще сохранить идентификатор в пользовательских данных при сохранении в firestore, чтобы избежать использования snapshotChanges (на мой взгляд).

// Query the users by a specific email and return the first User with ID added    
return this.firestore.collection<User>('users', ref => ref.where('email', 
'==', email))
  .snapshotChanges()
  .pipe(map(users => {
    const user = users[0];
    if (user) {
      const data = user.payload.doc.data() as User;
      const id = user.payload.doc.id;
      return { id, ...data };
    }
    else {
      return null;
    }
  }));

В коде вашего компонента вы можете вызвать это с помощью

checkEmail() {
this.user$ = this.us.getUserByEmail('[email protected]')
  .pipe(
    tap(user => {
      if (user) {
        this.msg = 'success';
      } else {
        this.msg = 'User with this email does not exist!';
      }
    }));
}

Обратите внимание, что пользователь будет нулевым до тех пор, пока вы не вызовете subscribe для this. User $ OR в вашем html используйте асинхронный канал, который обрабатывает подписку и отмену подписки для наблюдаемого.

<div *ngIf = "(user$ | async) as user">{{ user.email }}</div>

Старайтесь избегать использования асинхронного канала для одного и того же наблюдаемого несколько раз в вашем html и вместо этого установите его как локальную переменную html, что я делаю выше (как пользователь), чтобы избежать ненужных поездок данных в db

Замечание о соглашения об именах. Имена переменных наблюдаемых часто имеют суффикс «$», в то время как другие (строка, числа и т. д.) Не имеют символа «$».

Спасибо за ответ, но мне также нужен идентификатор пользователя, который valueChanges не возвращает, будет ли это работать, если я использую изменения снимка?

Nivedita 02.01.2019 18:01

Правильно, это немного сложнее, но я отредактирую исходный ответ, чтобы показать код.

Jakob Segerslätt 03.01.2019 09:00

Создать услугу:

   constructor(private db: AngularFirestore) { }

      getCategories() {
        return this.db.collection('categories').valueChanges();
      }

используй это:

 categories$;

  constructor(categoryService: CategoryService) {
    this.categories$ = categoryService.getCategories();
  }

отображать ваши документы:

 <select id = "category" type = "text" class = "form-control">
        <option value = ""></option>
        <option *ngFor = "let c of categories$ | async" [value] = "c.$key">
          {{ c.name }}
        </option>
    </select>

Другие вопросы по теме