ค้นหาเว็บไซต์

TypeScript - การเพิ่มค่าในอาร์เรย์ใน Ionic 3 ใช้งานไม่ได้


ฉันกำลังสร้างแอปด้วย Ionic 3 และ Firebase โดยหลัก ๆ ฉันต้องการที่จะสามารถถ่ายภาพหลายรูป อัปโหลดไปยัง Firebase Storage จากนั้นรับลิงก์ที่ชี้ไปยังภาพเหล่านั้นและใส่ลิงก์เหล่านั้นในเอกสารใน Firestore อย่างไรก็ตาม ฉันกำลังพบพฤติกรรมที่แปลก ๆ กับอาร์เรย์ที่เก็บ downloadURLs ถ้าฉันเรียกใช้ฟังก์ชันครั้งเดียว มันจะอัปโหลดเอกสารไปยัง Firestore แต่ลิงก์ในอาร์เรย์จะว่าง ถ้าฉันเรียกใช้ฟังก์ชันครั้งที่สอง ลิงก์จึงจะแสดงออกมา นี่คือโค้ดของฉัน:

ตัวแปร :

export class AboutPage {

  public photos: any;
  public downloadURLs: any;
  public link: string;
  public base64Image: string;

ngOnInit() {
    this.photos = [];
    this.downloadURLs = [];
  }

ฟังก์ชัน :

// Note: The uploading of the images works completely fine

  uploadForm() {
    //var testArray = [];
    var kuerzel = "teacher";
    var lehrerKuerzel = kuerzel.toUpperCase();
    var date = Date();
    var length = this.photos.length;
    var i = 0;
    while(i < length){
      const ref = firebase.storage().ref(`pruefungen/${lehrerKuerzel}/${date}/${i}`);
      ref.putString(this.photos[i], 'data_url').then((snapshot) => {
        this.downloadURLs.push(snapshot.downloadURL);
      });
      if(i + 1 == length)
      {
        var db = firebase.firestore().collection(`${lehrerKuerzel}`);
        db.add({
          TestName: pruefungsName,
          Klasse: klasse,
          Abteilung: abteilung,
          Datum: date,
          DownloadURLs: this.downloadURLs,
          Length: length,
          I: i
        });
      }
      i += 1;
    }

  }

สิ่งที่ฉันได้ลองทำ:

  • การใช้แอเรย์ในท้องถิ่น

  • การใส่ db.add({}); ใน .then() ในขณะที่อาร์เรย์แสดงผลเต็มเสมอ เงื่อนไข if ที่มีตัวแปร i ก็เป็นจริงเสมอ เนื่องจากบางเหตุผล i มักจะเท่ากับความยาว ดังนั้นจึงมีรายการในคอลเลกชันมากเกินไปเสมอ

คำตอบ #1

ฟังก์ชันการอัปโหลด res.putString ทำงานแบบอะซิงโครนัส ซึ่งหมายความว่า while ลูปของคุณจะทำงานจนเสร็จก่อนที่ไฟล์จะถูกอัปโหลดจริง ๆ ดังนั้น this.downloadURLs === [] ในเวลาที่มีการเพิ่มระเบียน และจะถูกเติมค่าในภายหลัง เมื่อรูปภาพเสร็จทีละหนึ่ง

คุณควรรอให้การอัปโหลดทั้งหมดเสร็จสิ้นก่อนที่จะเพิ่มระเบียน เพื่อที่คุณจะมี URL การดาวน์โหลดทั้งหมดเพื่อเขียนลงฐานข้อมูล

วิธีหนึ่งในการทำเช่นนั้นคือด้วย Observable ForkJoin ซึ่งอธิบายไว้ในหน้านี้ว่า:

หากคุณคุ้นเคยกับ Promises สิ่งนี้จะคล้ายกับ Promise.all() ตัวดำเนินการ forkJoin() ช่วยให้เราสามารถนำรายการของ Observables แล้วดำเนินการพร้อมกันได้ เมื่อ Observable ทุกตัวในรายการส่งค่าหนึ่ง คำสั่ง forkJoin จะส่งค่าของ Observable เดียวที่มีรายการของค่าที่ถูกแก้ไขทั้งหมดจาก Observables ในรายการ

บทความนั้นให้ตัวอย่างต่าง ๆ แก่คุณเกี่ยวกับการจัดการกับความต้องการประเภทนี้

ในกรณีของคุณโดยเฉพาะ ผมขอแนะนำให้คุณสร้างอาร์เรย์ของการเรียกเก็บข้อมูลในลูป แล้วใช้ ForkJoin เพื่อรอผลลัพธ์ จากนั้นจึงสร้างระเบียน ประมาณแบบนี้:

// extra import you'll need
import { forkJoin } from "rxjs/observable/forkJoin";

var kuerzel = "teacher";
var lehrerKuerzel = kuerzel.toUpperCase();
var date = Date();
var upload = this.photos.map(i => {
    const ref = firebase.storage().ref(`pruefungen/${lehrerKuerzel}/${date}/${i}`);
    return ref.putString(i, 'data_url');
});
forkJoin(upload).subscribe(results => {
    // results is an array of snapshots
    var db = firebase.firestore().collection(`${lehrerKuerzel}`);
    db.add({
        TestName: pruefungsName,
        Klasse: klasse,
        Abteilung: abteilung,
        Datum: date,
        DownloadURLs: results.map(i => i.downloadURL),
        Length: this.photos.length,
        I: this.photos.length - 1
    });
});

ยังไม่ได้ทดสอบโค้ด แต่ไอเดียนี้น่าจะใช้ได้ และอีกครั้ง คุณสามารถหาตัวอย่างที่สมบูรณ์มากกว่านี้ได้ในบทความที่ลิงก์ไว้

คำตอบ #2

ใช้แถวของ promises กับ Promise.all:

...

let promises = [];
while(i < length){
  promises.push(new Promise(resolve, reject) {
    const ref = firebase.storage().ref(`pruefungen/${lehrerKuerzel}/${date}/${i}`);
    ref.putString(this.photos[i], 'data_url').then((snapshot) => {
      this.downloadURLs.push(snapshot.downloadURL);
      resolve();
    }).catch(e => {
      console.log(e);
      reject();
    });
  })
  i++;
}

Promise.all(promises).then(() => {
  var db = firebase.firestore().collection(`${lehrerKuerzel}`);
  db.add({
    TestName: pruefungsName,
    Klasse: klasse,
    Abteilung: abteilung,
    Datum: date,
    DownloadURLs: this.downloadURLs,
    Length: length,
    I: i
  });
})