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