在 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;

}

现在所有图像都按预期显示在屏幕上