如何将图像源绑定到 ngFor 循环内的可观察对象?
How to bind image source to an observable inside an ngFor loop?
我正在尝试在 ngFor 循环中显示图像,如下所示:
HTML
<div *ngFor="let leg of flight.route">
<img [src]="getImage(leg.airline) | async" />
</div>
TS
getImage(airlineCode: string): Observable<any> {
return this.companyLogoService.getLogo(airlineCode);
}
这行不通。执行此操作时,对 getImage()
的所有请求似乎都相互中断并无限循环:
根据,处理这个问题的一种方法是手动创建一个模板变量来保存可观察对象,然后将这个变量传递给 [src]
标签,但我负担不起因为 ngFor 循环。
如何正确绑定此图片源?
编辑:
对 getImage()
的请求没有相互干扰。这个行为是Angular变化检测发疯的原因,因为我直接映射了一个函数调用到[src]
,这是不支持的。加载单个图像时出现同样的问题。
使用 components
.
这里正在工作Sample
我想说这不是一个好方法,因为更改检测 运行 经常发生,并且每次更改检测 'getImage' 都会被调用。
您可以尝试在初始化时获取所有图像并将其填充到您的模板中
getFlights() {
this.flights = /*code to get flights*/;
// after you have retrieved flights
this.flights.forEach((flight, index) => {
readImageFile(index, flight.airline);
});
}
getImage(index: number, airlineCode: string) {
this.companyLogoService.getLogo(airlineCode)
.subscribe((result) => {
// result depends on your server i.e. base64 data, image url etc
this.flights[index].img = result;
});
}
<div *ngFor="let flight of flights | async;">
<img [src]="flight.img">
</div>
由于显然没有这样的方法来实现这种绑定,我使用一个数组来保存我的可观察对象,并在我的模板中按索引映射它们。
TS
@Component({
selector: 'flight-card',
templateUrl: './flight-card.component.html',
styleUrls: ['./flight-card.component.scss']
})
export class FlightCardComponent implements OnInit {
@Input()
public flight: Flight;
public airlineImages: Observable<any>[];
constructor(private companyLogoService: CompanyLogoService) {
}
ngOnInit() {
this.airlineImages = [];
this.flight.route.forEach(leg => {
this.airlineImages.push(this.companyLogoService.getLogo(leg.airline));
});
}
}
HTML
<div *ngFor="let leg of flight.route; let i = index">
<img [src]="this.airlineImages[i] | async" />
</div>
正如所描述的,变更检测 导致了这种行为,它每次都会调用该方法并触发该方法(http 请求)。
一种可行的方法是在组件初始化时加载图像。 (重要的是,在你的组件中添加一个*ngIf,否则你的对象可能不会被初始化)
#1 父组件
<ns-bills-cardview *ngIf="billsBook" [billsBook]="billsBook"></ns-bills-cardview>
#2 在您的组件中,只需从缓存或 http 请求中加载图像
export class BillsCardviewComponent implements OnInit {
@Input() billsBook: BillsBook[];
productDocuments: BillBookPrictureData[] = [];
ngOnInit(): void {
this.authService.userTake1Observable.pipe(take(1)).subscribe(user => {
if (user) {
// pre-loading billBook detail images
this.loadingImages();
}
});
}
onLoadImage(productUUID): string {
const picture: BillBookPrictureData = this.productDocuments.find(
productDocument => productDocument.key === productUUID
);
return picture.data;
}
onItemTap(bill: BillsBook, productUUID: string) {
this.billDetailDialogModalService.openBillDetailDialogModal(this.vcRef, this.modal, bill.uuid, productUUID);
}
/**
* This method will get called to re-load all billBook images
*/
private loadingImages() {
this.billsBook.forEach(bill => {
const billUUID = bill.uuid;
bill.products.forEach(product => {
const billBookPicture: BillBookPrictureData = { key: null, data: null };
// cache billPictureData
billBookPicture.key = product.productDocument.uuid;
billBookPicture.data = this.loadImage(billUUID, billBookPicture.key);
this.productDocuments.push(billBookPicture);
});
});
}
/**
* This method will load all images from appSettings or send http request
* @param string billUUID
* @param string productUUID
*/
private loadImage(billUUID: string, productUUID: string): string {
//load billBook images from cache
if (this.appSettingsService.hasBillDetailFileData(billUUID, productUUID)) {
return this.appSettingsService.getBillDetailFileData(billUUID, productUUID);
}
//load billBook images via http request
this.documentControllerService
.getDocumentFileDataUsingGET(productUUID)
.pipe(take(1))
.subscribe(
fileData => {
//store image in cache
this.appSettingsService.setBillDetailFileData(fileData, billUUID, productUUID);
//push loaded image to productDocument array
const billBookPicture: BillBookPrictureData = { key: productUUID, data: fileData };
this.productDocuments.push(billBookPicture);
},
error => console.log(error)
);
}
}
#3 面具
<StackLayout *ngFor="let product of item.products" (tap)="onItemTap(item, product.uuid)">
<Image
*ngIf="productDocuments"
[src]="'data:image/jpeg;base64, ' + onLoadImage(product.productDocument.uuid)"
[alt]="product.name"
width="200"
height="120"
stretch="aspectFill"
></Image>
.
.
.
</StackLayout>
此解决方案适用于加载一张或多张图片。
我正在尝试在 ngFor 循环中显示图像,如下所示:
HTML
<div *ngFor="let leg of flight.route">
<img [src]="getImage(leg.airline) | async" />
</div>
TS
getImage(airlineCode: string): Observable<any> {
return this.companyLogoService.getLogo(airlineCode);
}
这行不通。执行此操作时,对 getImage()
的所有请求似乎都相互中断并无限循环:
根据[src]
标签,但我负担不起因为 ngFor 循环。
如何正确绑定此图片源?
编辑:
对 getImage()
的请求没有相互干扰。这个行为是Angular变化检测发疯的原因,因为我直接映射了一个函数调用到[src]
,这是不支持的。加载单个图像时出现同样的问题。
使用 components
.
这里正在工作Sample
我想说这不是一个好方法,因为更改检测 运行 经常发生,并且每次更改检测 'getImage' 都会被调用。
您可以尝试在初始化时获取所有图像并将其填充到您的模板中
getFlights() {
this.flights = /*code to get flights*/;
// after you have retrieved flights
this.flights.forEach((flight, index) => {
readImageFile(index, flight.airline);
});
}
getImage(index: number, airlineCode: string) {
this.companyLogoService.getLogo(airlineCode)
.subscribe((result) => {
// result depends on your server i.e. base64 data, image url etc
this.flights[index].img = result;
});
}
<div *ngFor="let flight of flights | async;">
<img [src]="flight.img">
</div>
由于显然没有这样的方法来实现这种绑定,我使用一个数组来保存我的可观察对象,并在我的模板中按索引映射它们。
TS
@Component({
selector: 'flight-card',
templateUrl: './flight-card.component.html',
styleUrls: ['./flight-card.component.scss']
})
export class FlightCardComponent implements OnInit {
@Input()
public flight: Flight;
public airlineImages: Observable<any>[];
constructor(private companyLogoService: CompanyLogoService) {
}
ngOnInit() {
this.airlineImages = [];
this.flight.route.forEach(leg => {
this.airlineImages.push(this.companyLogoService.getLogo(leg.airline));
});
}
}
HTML
<div *ngFor="let leg of flight.route; let i = index">
<img [src]="this.airlineImages[i] | async" />
</div>
正如所描述的,变更检测 导致了这种行为,它每次都会调用该方法并触发该方法(http 请求)。
一种可行的方法是在组件初始化时加载图像。 (重要的是,在你的组件中添加一个*ngIf,否则你的对象可能不会被初始化)
#1 父组件
<ns-bills-cardview *ngIf="billsBook" [billsBook]="billsBook"></ns-bills-cardview>
#2 在您的组件中,只需从缓存或 http 请求中加载图像
export class BillsCardviewComponent implements OnInit {
@Input() billsBook: BillsBook[];
productDocuments: BillBookPrictureData[] = [];
ngOnInit(): void {
this.authService.userTake1Observable.pipe(take(1)).subscribe(user => {
if (user) {
// pre-loading billBook detail images
this.loadingImages();
}
});
}
onLoadImage(productUUID): string {
const picture: BillBookPrictureData = this.productDocuments.find(
productDocument => productDocument.key === productUUID
);
return picture.data;
}
onItemTap(bill: BillsBook, productUUID: string) {
this.billDetailDialogModalService.openBillDetailDialogModal(this.vcRef, this.modal, bill.uuid, productUUID);
}
/**
* This method will get called to re-load all billBook images
*/
private loadingImages() {
this.billsBook.forEach(bill => {
const billUUID = bill.uuid;
bill.products.forEach(product => {
const billBookPicture: BillBookPrictureData = { key: null, data: null };
// cache billPictureData
billBookPicture.key = product.productDocument.uuid;
billBookPicture.data = this.loadImage(billUUID, billBookPicture.key);
this.productDocuments.push(billBookPicture);
});
});
}
/**
* This method will load all images from appSettings or send http request
* @param string billUUID
* @param string productUUID
*/
private loadImage(billUUID: string, productUUID: string): string {
//load billBook images from cache
if (this.appSettingsService.hasBillDetailFileData(billUUID, productUUID)) {
return this.appSettingsService.getBillDetailFileData(billUUID, productUUID);
}
//load billBook images via http request
this.documentControllerService
.getDocumentFileDataUsingGET(productUUID)
.pipe(take(1))
.subscribe(
fileData => {
//store image in cache
this.appSettingsService.setBillDetailFileData(fileData, billUUID, productUUID);
//push loaded image to productDocument array
const billBookPicture: BillBookPrictureData = { key: productUUID, data: fileData };
this.productDocuments.push(billBookPicture);
},
error => console.log(error)
);
}
}
#3 面具
<StackLayout *ngFor="let product of item.products" (tap)="onItemTap(item, product.uuid)">
<Image
*ngIf="productDocuments"
[src]="'data:image/jpeg;base64, ' + onLoadImage(product.productDocument.uuid)"
[alt]="product.name"
width="200"
height="120"
stretch="aspectFill"
></Image>
.
.
.
</StackLayout>
此解决方案适用于加载一张或多张图片。