Как справиться с этой ситуацией с ошибкой, связанной с вызовом метода, который создает новый объект в Firestore ПОСЛЕ сохранения файла в хранилище?

#angular #firebase #google-cloud-firestore #rxjs #firebase-storage

#angular #firebase #google-cloud-firestore #rxjs #firebase-хранилище

Вопрос:

Я не очень разбираюсь в Angular и Firebase, и у меня следующая проблема.

У меня есть следующий код, который обрабатывает весь цикл регистрации пользователя. Это частично работает, но у меня есть проблема, которую я попытаюсь подробно объяснить (вероятно, потому, что у меня все еще есть некоторые трудности с функциональным программированием …):

 createUser(user, file) {
  console.log(user);

  if(user.password != user.password_confirmation) {
    let passwordError: any = new Object();
    passwordError["message"] = "Inserted password and inserted password confirmation are different";
    this.eventAuthError.next(passwordError);
  }
  else {
    this.afAuth.auth.createUserWithEmailAndPassword(user.email, user.password)
      .then( userCredential => {
        this.newUser = user;
        console.log(userCredential);
        userCredential.user.updateProfile( {
          displayName: user.firstName   ' '   user.lastName
        });

        this.uploadFileIntoFirebaseStore(file);


        this.insertUserData(userCredential, this.downloadURL)
          .then(() => {
            this.router.navigate(['/home']);
          });
      })
      .catch( error => {
        console.log(error);
        console.log(typeof error);
        this.eventAuthError.next(error);    // Emit the event passing the error object
      });
    }
}

uploadFileIntoFirebaseStore(fileToBeUploaded) {
  var n = Date.now();
  const filePath = `user_avatar/${n}`;
  const fileRef = this.storage.ref(filePath);

  const task = this.storage.upload(`user_avatar/${n}`, fileToBeUploaded);
  task
    .snapshotChanges()
    .pipe(
      finalize(() => {
        this.downloadURL = fileRef.getDownloadURL();
        this.downloadURL.subscribe(url => {
          if (url) {
            this.fb = url;
          }
          console.log(this.fb);
        });
      })
    )
    .subscribe(url => {
      if (url) {
        console.log(url);
      }
    });

    return this.fb

}

insertUserData(userCredential: firebase.auth.UserCredential, userAvatar) {

  let userObg = {
        name: this.newUser.firstName,
        surname: this.newUser.lastName,
        complete_name: this.newUser.firstName   " "   this.newUser.lastName,
        email: this.newUser.email,
        role: this.newUser.ruolo_utente,
        avatar: userAvatar
      };

  console.log("USER OBJECT: ", userObg);

  return this.db.doc(`Users/${userCredential.user.uid}`).set(userObg);
}
  

В основном он выполняет следующие действия:

  1. createUser() Возьмите пользовательский объект, который должен быть сохранен в коллекции FireStore, и параметр файла, который представляет собой изображение, которое должно быть сохранено в хранилище Firebase.

  2. Сначала он вызывает createUserWithEmailAndPassword() , который создает нового пользователя в службе аутентификации Firebase, используя имя пользователя и пароль (все работает нормально).

  3. Если пользователь создан правильно, он вводится в then() и вызывает uploadFileIntoFirebaseStore() метод, который используется для загрузки файла изображения в хранилище Firebase.

  4. Затем он вызывает insertUserData() метод, который создает и сохраняет новый объект, связанный с текущим пользователем, в FireStore.

Моя проблема возникает между точками 3 и 4. В деталях файл правильно сохраняется в хранилище Firebase, но мне кажется, что это асинхронная задача, и поэтому требуется некоторое время, прежде чем вернуть URL-адрес, связанный с этим файлом, в хранилище Firebase. Это занимает время, но тем временем insertUserData() запускается, поэтому здесь это поле URL (которое я должен сохранить в своем объекте) не определено.

Какой может быть разумный способ подождать, пока URL-адрес файла не будет «возвращен»? Возможно, мне не нужно ничего возвращать, но было бы неплохо поместить вызов insertUserData() (который вставляется в FireStore) в uploadFileIntoFirebaseStore() метод после получения URL-адреса, здесь:

 this.downloadURL.subscribe(url => {
    if (url) {
        this.fb = url;
        // I PUT THE CALL TO HERE
    }
    console.log(this.fb);
});
  

Что может быть разумным решением этой проблемы?

Комментарии:

1. Для такого кода очень полезно, если вы изучите синтаксис JavaScript / TypeScript async / await, поскольку это значительно упрощает работу с сериями обещаний.

Ответ №1:

Вам нужны .pipe() ваши потоки, иначе вы никогда не сможете их синхронизировать.

uploadFileIntoFirebaseStore должен return ли поток загрузки:

 return this.storage.upload(`user_avatar/${n}`, fileToBeUploaded)
  .snapshotChanges()
  .pipe(switchMap(() => fileRef.getDownloadURL());
  

Затем в вашем createUser вы можете использовать его:

 this.uploadFileIntoFirebaseStore(file).subscribe(downloadURL =>
  this.insertUserData(userCredential, downloadURL)
    .then(() => {
      this.router.navigate(['/home']);
    });
  );