user11052170
user11052170

Reputation:

Angular Load Image without (click) function

I am loading data from an image from html using * ngFor.

To load the image, another function is performed, which needs the ID that is loaded in * ngFor, to find the image with that ID.

To get the image, I used (click) where I pass the product ID, but the goal is to load the images without using the click, ie automatically.

What I have

HTML

 <div class="row tab-pane Galeria">
        <div *ngFor="let product of products" class="col-xl-2 col-lg-3 col-md-4 col-sm-6">
          <div class="image-item" (click)="fillModal($event, product)">
              <a class="d-block image-block h-100">
              <homeImage> 
                    <img *ngIf [src]="Images" class="Images img-fluid" alt="">                  
              </homeImage> 
              </a>           
            <div class="ImageText" (click)="ImageInfo(product.id)">{{product.name}}</div>
          </div>
        </div>
      </div>

Component.ts

ngOnInit() {
    this.GetProducts();
  }

  GetProducts() {
    let self = this;
    this.Global.refreshToken().subscribe(function (result) {
      self.homeService.getProducts().then(function (resultado) {
        if (resultado) {
         self.products = resultado;
        }
      }).catch();
    }); 
  }


  ImageInfo(id) {
      var self = this;
      self.Global.refreshToken().subscribe(function (result) {
        self.homeService.getImage(id).then(function (resultado) {
          if (resultado) {
            self.Images = resultado;
          }
        }).catch();
      });
    }

Service.ts

getProducts(): Promise<any> {
    let self = this;
    let urlAux = self.url + "/Products/GetProducts";
    return axios.get(urlAux, {
      headers: {
        Authorization: 'Bearer ' + localStorage.getItem("access_token")
      }
    })
      .then(this.extraData)
      .catch(this.handleErroPromisse);
  }

  getImage(id): Promise<any> {
    let self = this;
    let urlAux = self.url + "/Products/GetImage/" + id;

    return axios.get(urlAux, {'responseType': 'arraybuffer'})
      .then((response) => {
        let image = btoa(
          new Uint8Array(response.data)
            .reduce((data, byte) => data + String.fromCharCode(byte), '')
        );
        return `data:${response.headers['content-type'].toLowerCase()};base64,${image}`;
      });
  }

What I tested and didn't work:

Does not work execute the function in a src :(

 <div class="row tab-pane Galeria">
        <div *ngFor="let product of products" class="col-xl-2 col-lg-3 col-md-4 col-sm-6">
          <div class="image-item" (click)="fillModal($event, product)">
              <a class="d-block image-block h-100">
              <homeImage> 
                    <img *ngIf [src]="ImageInfo(product.id)" class="Images img-fluid" alt="">                  
              </homeImage> 
              </a>           
            <div class="ImageText">{{product.name}}</div>
          </div>
        </div>
      </div>

Upvotes: 1

Views: 495

Answers (2)

Thatkookooguy
Thatkookooguy

Reputation: 7012

The action you're performing is asynchronous (it returns a Promise).

That's why you either need to make sure the attribute is flagged to wait for an asynchronous action, or make the action 'less asynchronous' :-).

Do the processing up-front

If you immediately parse the images in your typescript, you can skip the async part of the function and do the same thing in the component.ts file.

Component.ts

ngOnInit() {
  this.GetProducts();
}

GetProducts() {
  let self = this;
  this.Global.refreshToken()
    .subscribe(function (result) {
      self.homeService.getProducts()
        .then(function (resultado) {
          if (resultado) {
            self.products = resultado;
          }
        })
        .then(() => {
          if (self.products) {
            return Promise.all(self.products.map((product) => self.ImageInfo(product.id)));
          }
        })
        .catch((err) => console.error(err));
    });
}


ImageInfo(id) {
  var self = this;
  self.Global.refreshToken().subscribe(function (result) {
    self.homeService.getImage(id).then(function (resultado) {
      if (resultado) {
        // self.Images = resultado;

        self.images[id] = resultado; // <-- changed to id so you can fetch easily
      }
    }).catch();
  });
}

HTML

 <div class="row tab-pane Galeria">
  <div *ngFor="let product of products" class="col-xl-2 col-lg-3 col-md-4 col-sm-6">
    <div class="image-item" (click)="fillModal($event, product)">
        <a class="d-block image-block h-100">
        <homeImage> 
              <img *ngIf="images[product.id]" [src]="images[product.id]" class="Images img-fluid" alt="">                  
        </homeImage> 
        </a>           
      <div class="ImageText">{{product.name}}</div>
    </div>
  </div>
</div>

Now, I'm just showing the image if we already fetched the data when we fetched the products. That way, the async action happens in the component.ts and the HTML is kept synchronous.

The main difference between the two solutions is if you want to use prefetching or not.

Upvotes: 2

hansmaad
hansmaad

Reputation: 18905

It doesn't work because your ImageInfo(id) doesn't return anything that could be assign to the img src. It loads image info and stores the resultado into this.Images. But in the template you bind the return value of the function to src.

What you probably want to do is to load all products in ngOnInit and then load all ImageInfos for all products (assuming that your API is working like this...)

Something like:

async ngOnInit() {
    this.products = await this.service.loadProducts();
    for (const p of this.product) {
       this.images[p.id] = await this.imageService.getInfo(p.id));
    }
}

and

<div *ngFor="let product of products">
  <img *ngIf="images[product.id]" [src]="images[product.id].src" > 
</div>

Upvotes: 1

Related Questions