在 Angular ngOnInit 方法中加载图像和用户短列表失败,图像始终未定义
Loading images together with short list of users in Angular ngOnInit method fails, images are always undefined
我有一个页面包含几张 bootstrap 卡片(大约 2 到 5 张)。他们提供学生信息。我还想加载这些学生的图像(从服务器)以及他们的详细信息,但我不知道如何加载。这是我得到的:
students: { student: Student, hasImage: boolean, image: any }[] = [];
loadingData: boolean;
imageToShow: any;
constructor(private userService: UsersService, private fileService: FilesService) {
}
createImageFromBlob(image: Blob) {
const reader = new FileReader();
reader.addEventListener('load', () => {
this.imageToShow = reader.result;
}, false);
if (image) {
reader.readAsDataURL(image);
}
}
ngOnInit() {
this.userService.getStudentsOfLoggedUser().subscribe(res => {
this.loadingData = true;
res.forEach(std => {
if (std.imageUrl == null) {
this.students.push({student: std, hasImage: false, image: undefined});
} else {
this.fileService.getImage(std.imageUrl).subscribe(data => {
this.createImageFromBlob(data);
});
this.students.push({student: std, hasImage: true, image: this.imageToShow});
}
this.loadingData = false;
});
});
}
此代码无法从服务器获取图像,它们总是丢失(未定义)。正确的应该是什么样子?
你必须把 this.students.push({student: std, hasImage: true, image: this.imageToShow}); 放在 this.fileService.getImage 订阅因为它的执行是异步的。
另一方面,您必须避免嵌套订阅。检查这个 link
https://coryrylan.com/blog/angular-multiple-http-requests-with-rxjs
此外,如果您使用订阅,则必须在 ngOnDestroy 方法中取消订阅以避免内存泄漏。检查其他 link
https://brianflove.com/2016/12/11/anguar-2-unsubscribe-observables/
希望对你有用
此致
据我了解,发生这种情况的原因是因为 forEach 本质上是同步的,但这一行是异步的:
this.fileService.getImage(std.imageUrl).subscribe(data => {
this.createImageFromBlob(data);
});
相反,您可以这样承诺:
this.userService.getStudentsOfLoggedUser().subscribe(res => {
this.loadingData = true;
let count = 0;
new Promise((resolve, reject) => {
res.forEach(std => {
count++;
if (std.imageUrl == null) {
this.students.push({ student: std, hasImage: false, image: undefined });
} else {
this.fileService.getImage(std.imageUrl).subscribe(data => {
this.createImageFromBlob(data);
});
this.students.push({ student: std, hasImage: true, image: this.imageToShow });
}
if (count > res.length -1) {
resolve(); //Resolve condition
}
});
}).then(() => {
this.loadingData = false;
})
});
这样,只有在所有异步调用都完成后,您的代码才会被解析,防止您获取未定义的数据。
将所有建议和答案组合在一起后,工作代码如下所示:
createImageFromBlob(studentCard: StudentCard, image: Blob) {
const reader = new FileReader();
reader.addEventListener('load', () => {
studentCard.image = reader.result
}, false);
if (image) {
reader.readAsDataURL(image);
}
}
ngOnInit() {
this.userService.getStudentsOfLoggedUser().subscribe(res => {
this.loadingData = true;
res.forEach(std => {
const studentCard = new StudentCard();
studentCard.student = std;
if (!std.imageUrl || std.imageUrl == null) {
studentCard.hasImage = false;
} else {
studentCard.hasImage = true;
}
this.students.push(studentCard);
});
let count = 0;
this.students.forEach(std => {
if (std.hasImage) {
count++;
new Promise((resolve, reject) => {
this.fileService.getImage(std.student.imageUrl).subscribe(data => {
this.createImageFromBlob(std, data);
console.log(std);
});
if (count > this.students.length - 1) {
resolve(); //Resolve condition
}
}).then(() => {
// there is nothing to do here
});
}
});
this.loadingData = false;
});
}
另外除了我声明的用于交换数据的 StudentCard 的匿名类型:
export class StudentCard {
student: Student;
hasImage: boolean;
image: any;
}
现在所有图像都按预期显示在屏幕上
我有一个页面包含几张 bootstrap 卡片(大约 2 到 5 张)。他们提供学生信息。我还想加载这些学生的图像(从服务器)以及他们的详细信息,但我不知道如何加载。这是我得到的:
students: { student: Student, hasImage: boolean, image: any }[] = [];
loadingData: boolean;
imageToShow: any;
constructor(private userService: UsersService, private fileService: FilesService) {
}
createImageFromBlob(image: Blob) {
const reader = new FileReader();
reader.addEventListener('load', () => {
this.imageToShow = reader.result;
}, false);
if (image) {
reader.readAsDataURL(image);
}
}
ngOnInit() {
this.userService.getStudentsOfLoggedUser().subscribe(res => {
this.loadingData = true;
res.forEach(std => {
if (std.imageUrl == null) {
this.students.push({student: std, hasImage: false, image: undefined});
} else {
this.fileService.getImage(std.imageUrl).subscribe(data => {
this.createImageFromBlob(data);
});
this.students.push({student: std, hasImage: true, image: this.imageToShow});
}
this.loadingData = false;
});
});
}
此代码无法从服务器获取图像,它们总是丢失(未定义)。正确的应该是什么样子?
你必须把 this.students.push({student: std, hasImage: true, image: this.imageToShow}); 放在 this.fileService.getImage 订阅因为它的执行是异步的。
另一方面,您必须避免嵌套订阅。检查这个 link https://coryrylan.com/blog/angular-multiple-http-requests-with-rxjs
此外,如果您使用订阅,则必须在 ngOnDestroy 方法中取消订阅以避免内存泄漏。检查其他 link https://brianflove.com/2016/12/11/anguar-2-unsubscribe-observables/
希望对你有用
此致
据我了解,发生这种情况的原因是因为 forEach 本质上是同步的,但这一行是异步的:
this.fileService.getImage(std.imageUrl).subscribe(data => {
this.createImageFromBlob(data);
});
相反,您可以这样承诺:
this.userService.getStudentsOfLoggedUser().subscribe(res => {
this.loadingData = true;
let count = 0;
new Promise((resolve, reject) => {
res.forEach(std => {
count++;
if (std.imageUrl == null) {
this.students.push({ student: std, hasImage: false, image: undefined });
} else {
this.fileService.getImage(std.imageUrl).subscribe(data => {
this.createImageFromBlob(data);
});
this.students.push({ student: std, hasImage: true, image: this.imageToShow });
}
if (count > res.length -1) {
resolve(); //Resolve condition
}
});
}).then(() => {
this.loadingData = false;
})
});
这样,只有在所有异步调用都完成后,您的代码才会被解析,防止您获取未定义的数据。
将所有建议和答案组合在一起后,工作代码如下所示:
createImageFromBlob(studentCard: StudentCard, image: Blob) {
const reader = new FileReader();
reader.addEventListener('load', () => {
studentCard.image = reader.result
}, false);
if (image) {
reader.readAsDataURL(image);
}
}
ngOnInit() {
this.userService.getStudentsOfLoggedUser().subscribe(res => {
this.loadingData = true;
res.forEach(std => {
const studentCard = new StudentCard();
studentCard.student = std;
if (!std.imageUrl || std.imageUrl == null) {
studentCard.hasImage = false;
} else {
studentCard.hasImage = true;
}
this.students.push(studentCard);
});
let count = 0;
this.students.forEach(std => {
if (std.hasImage) {
count++;
new Promise((resolve, reject) => {
this.fileService.getImage(std.student.imageUrl).subscribe(data => {
this.createImageFromBlob(std, data);
console.log(std);
});
if (count > this.students.length - 1) {
resolve(); //Resolve condition
}
}).then(() => {
// there is nothing to do here
});
}
});
this.loadingData = false;
});
}
另外除了我声明的用于交换数据的 StudentCard 的匿名类型:
export class StudentCard {
student: Student;
hasImage: boolean;
image: any;
}
现在所有图像都按预期显示在屏幕上