Хобрук: Ваш путь к мастерству в программировании

Observable undefined при загрузке первой страницы - предоставлены как рабочие, так и нерабочие реализации, поиск причины

Я вызываю loadUserData() в конструкторе службы. Но связанный наблюдаемый объект не определен, когда страница загружается в первый раз. Однако, когда я вызываю функцию загрузки явно из компонента, она работает. Пожалуйста, посмотрите две реализации в ngOnInit() в файле component.ts. Эта услуга предоставляется с использованием интерфейса ModuleWithProviders со статическим forRoot() в app.module.ts. Если я вручную уйду, а затем вернусь на эту страницу, resumeData$ наблюдаемое сработает. и мне не нужно вызывать loadUserdata() из файла component.ts. Что мне здесь не хватает?

Загрузить файл службы данных

import { Injectable } from '@angular/core';
import { AngularFirestore } from '@angular/fire/firestore';
import { AngularFireAuth } from '@angular/fire/auth';
import { User } from '../model/user.model';
import { ResumeData } from '../model/resumeData.model';
import { Observable, of } from 'rxjs';
import { map, first } from 'rxjs/operators';
import { convertSnaps } from './db-utils';
import { Summary } from '../model/summary.model';
import { Category } from '../model/skill.model';
import { Education } from '../model/education.model';
import { CoreCompetency } from '../model/competency.model';

@Injectable()
export class LoadFirestoreDataService {
  user: firebase.User | null;
  summaryList$: Observable<Summary[]>;
  categoryList$: Observable<Category[]>;
  educationList$: Observable<Education[]>;
  coreCompetencyList$: Observable<CoreCompetency[]>;
  resumeData$: Observable<ResumeData[]>;

  constructor(private db: AngularFirestore, private afAuth: AngularFireAuth) {
    this.afAuth.authState.subscribe(user => {
      if (user) {
        this.user = user;
        this.loadUserData();
      } else {
        this.user = null;
      }
    });
  }

  loadUserData(): Observable<ResumeData[]> {
    // console.log(this.user);
    const user = <User>JSON.parse(localStorage.getItem('user'));
    // console.log(userId);
    // if (user.uid) {
    this.resumeData$ = this.db
      .collection('users')
      .doc(user.uid)
      .collection('userData')
      .snapshotChanges()
      .pipe(map(snaps => convertSnaps<ResumeData>(snaps)));

    return this.resumeData$;
  }
}

Файл component.ts

  ngOnInit() {
// Following does not work
    this.dataService.resumeData$.subscribe(data => {
      console.log(data);
    });
// Following works 
    this.dataService.loadUserData().subscribe(data => {
      console.log(data);
    });

  }

Правки: Только что обнаружил, что даже после успешной аутентификации функция loadUserData() не вызывалась из конструктора(). Сейчас ищу почему так.

Этот фрагмент кода был преобразован в следующую более удобочитаемую форму:

  constructor(private db: AngularFirestore, private afAuth: AngularFireAuth) {
    this.loadUserData();
  }

  loadUserData(): void {
    const user = <User>JSON.parse(localStorage.getItem('user'));


    this.resumeData$ = this.db
      .collection('users')
      .doc(user.uid)
      .collection('userData')
      .snapshotChanges()
      .pipe(
        map(snaps => {
          return convertSnaps<ResumeData>(snaps)[0];
        })
      );

    this.summaryList$ = this.resumeData$.pipe(pluck('summaryList'));
    this.educationList$ = this.resumeData$.pipe(pluck('educationList'));
    this.categoryList$ = this.resumeData$.pipe(pluck('categoryList'));
    this.coreCompetencyList$ = this.resumeData$.pipe(pluck('coreCompetencyList'));
  }

и файл component.ts выглядит следующим образом:

  ngOnInit() {
    this.summaryList$ = this.dataService.summaryList$;

  }

подписка wan выполняется с использованием асинхронного канала:

        <div *ngFor="let item of (summaryList$ | async) as summaryList; let i = index">
          <app-summary-item [item]="item" [itemIndex]="i"></app-summary-item>
        </div>

  • Насколько я вижу, resumeData$ определяется только при вызове loadUserData(). Итак, вы также можете сделать это внутри ngOnInit: this.service.loadUserData() и на следующей строке this.service.resumeData$.subscribe(...), и все должно работать нормально. 21.05.2020
  • Вы можете использовать BehaviorSubject вместо Observable ? 21.05.2020
  • Нуб здесь. Изучение субъектов поведения. 21.05.2020

Ответы:


1

Функция loadUserData() возвращает Observable, которая абсолютно ничего не делает без вызова для нее метода subscribe(). В вашем рабочем фрагменте кода вы действительно подписываетесь на него:

// Following works 
this.dataService.loadUserData().subscribe(data => {
  console.log(data);
});

в то время как в вашем нерабочем коде вы просто слушаете resumeData$ излучает (но ваш конструктор выполняет только функцию this.loadUserData() без подписки на ее возвращенный Observable):

// Following does not work
this.dataService.resumeData$.subscribe(data => {
  console.log(data);
});

Итак, чтобы исправить, просто попробуйте подписаться в своем конструкторе:

constructor(private db: AngularFirestore, private afAuth: AngularFireAuth) {
  this.afAuth.authState.subscribe(user => {
    if (user) {
      this.user = user;
      this.loadUserData().subscribe(); // <------- NEEDED CHANGE
    } else {
      this.user = null;
    }
  });
}
21.05.2020
  • в то время как в вашем неработающем коде вы просто слушаете, что издаетSusumeData$ (но ваш конструктор выполняет только функцию this.loadUserData() без подписки на ее возвращаемый Observable) ------ Понимание этого лучше ---- вы имеете в виду, что this.loadUserData() даже не вернет наблюдаемое, на которое я могу подписаться позже? 21.05.2020
  • Вы можете быть не правы. Потому что я все еще могу сделать это без подписки в конструкторе. Пожалуйста, смотрите мои правки. Я где-то читал, что мне следует подписаться вручную и использовать async каналы для предотвращения утечек памяти. Поэтому я переместил код в этом направлении 21.05.2020
  • Пожалуйста, смотрите мои правки. Я считаю, что что-то не так в блоке if, который подписан на authState. Я убрал эту логику из этого сервиса, так как планировал в дальнейшем использовать гарды canActivate, тогда все заработало. 21.05.2020
  • @mozpider, вы правы (и я ошибаюсь), на самом деле ваша функция loadUserData() заполняет resumeData$, на которую вы пытаетесь подписаться позже. Итак, моя единственная подсказка для вас нерабочий код, что loadUserData() почему-то скорее всего вообще не вызывается... Отлаживать поток пробовали? Какой Angular вы используете? Начиная с версии 6.0 вы можете использовать @Injectable({ providedIn: 'root' ,}) вместо решения статической функции forRoot(). В любом случае, пожалуйста, попробуйте создать stackblitz (с фиктивными / смоделированными данными), и я постараюсь вам помочь. 23.05.2020
  • Новые материалы

    Решения DBA Metrix
    DBA Metrix Solutions предоставляет удаленного администратора базы данных (DBA), который несет ответственность за внедрение, обслуживание, настройку, восстановление базы данных, а также другие..

    Начало работы с Блум
    Обзор и Codelab для генерации текста с помощью Bloom Оглавление Что такое Блум? Некоторые предостережения Настройка среды Скачивание предварительно обученного токенизатора и модели..

    Создание кнопочного меню с использованием HTML, CSS и JavaScript
    Вы будете создавать кнопочное меню, которое имеет состояние наведения, а также позволяет вам выбирать кнопку при нажатии на нее. Финальный проект можно увидеть в этом Codepen . Шаг 1..

    Внедрите OAuth в свои веб-приложения для повышения безопасности
    OAuth — это широко распространенный стандарт авторизации, который позволяет приложениям получать доступ к ресурсам от имени пользователя, не раскрывая его пароль. Это позволяет пользователям..

    Классы в JavaScript
    class является образцом java Script Object. Конструкция «class» позволяет определять классы на основе прототипов с чистым, красивым синтаксисом. // define class Human class Human {..

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

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