如何将图像源绑定到 ngFor 循环内的可观察对象?

Posted

技术标签:

【中文标题】如何将图像源绑定到 ngFor 循环内的可观察对象?【英文标题】:How to bind image source to an observable inside an ngFor loop? 【发布时间】:2019-12-28 13:19:33 【问题描述】:

我正在尝试在这样的 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() 的所有请求似乎都相互中断并无限循环:

根据similar threads,处理此问题的一种方法是手动创建一个保存可观察对象的模板变量,然后将此变量传递给[src] 标记,但由于 ngFor,我不能这样做循环。

如何正确绑定这个图片源?

编辑:

getImage() 的请求没有互相干扰。此行为是由 Angular 更改检测引起的,因为我直接将函数调用映射到 [src],因此我发疯了,这是不支持的。同样的问题也发生在要加载的单个图像上。

【问题讨论】:

【参考方案1】:

我想说这不是一个好方法,因为更改检测经常运行,并且每次更改检测都会调用“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>

【讨论】:

这就是我最终做的事情,但我将图像与组件输入数据分开 您可以为图像创建一个单独的数组,但我强烈建议只运行一次此类代码,因为在这种情况下,更改检测会严重影响应用程序性能 我认为这很好,因为我现在指的是一个变量(一个可观察的数组)而不是一个函数。该数组在组件启动时填充,然后永远不会改变。【参考方案2】:

使用components 执行此操作。

这里正在工作Sample

【讨论】:

我不明白你的回答与我的问题有什么关系 我已经编写了示例组件来解决您的问题,您不想将其插入到数组中 你能提供示例 api 我会映射它【参考方案3】:

由于显然没有这种方法可以实现这种绑定,因此我使用了一个数组来保存我的 observables,并通过模板中的索引映射它们。

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>

【讨论】:

【参考方案4】:

如上所述,变更检测会导致这种行为,它每次都会调用该方法并触发该方法(一个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"
     
     
     stretch="aspectFill"
     ></Image>
     .
     . 
     .
  </StackLayout>

此解决方案可适用于加载一张或多张图片。

【讨论】:

以上是关于如何将图像源绑定到 ngFor 循环内的可观察对象?的主要内容,如果未能解决你的问题,请参考以下文章

knockoutjs 可观察对象绑定的可观察数组

如何将 TabControl 的项目绑定到 wpf 中的可观察集合?

使用字符串插值将控件从FormGroup绑定到* ngFor内的模板

如何将变量绑定到图像源字符串?

更改ngFor循环中调用的组件内的父值

Ionic 4 Angular模板与异步管道绑定到可观察