Nick
Nick

Reputation: 236

Angular http request in ngif

I have a table of data and one column has a button in it that opens a dialog popup based on the object from that row. I am trying to show/hide the button based on a http request that has to be called once per button to determine if the button should show/hide for that row. I set up a network call like this:

setupApi() {
  return this.http.get(url);
}

   shouldShowButton() {
      this.service.getJson()
        .subscribe(data => {
          return true;
        }, error => {
          return false;
        });
    }

And then on my button I have this:

<button mat-raised-button *ngIf="shouldShowButton() |  async"></button>

I am getting error on the ng-if saying my function returns void.

Upvotes: 1

Views: 1305

Answers (4)

AVJT82
AVJT82

Reputation: 73357

I would restructure this a bit, and handle all request in the TS before displaying.

Reading your comments, we can gather....

If you have an unknown amount of items, you can use forkJoin to perform all requests in parallel based on the original array, then add the value to your original array that you are looping in your table in template. I would add a boolean prop in your array, where we determine that should the button be shown or not, here called showBtn

So Let's say your data looks like (after adding the bool prop):

myDat = [
  { name: "name1", showBtn: false },
  { name: "name2", showBtn: false },
  { name: "name3", showBtn: false },
  { name: "name4", showBtn: false }
];

Use array map to push requests to a helper array, which we then use forkJoin on, then add the boolean value to your original array:

shouldShowButton() {
  let reqs = [];
  this.myDat.map(btn => reqs.push(this.myService.getJson(btn.name)));

  this.myData = forkJoin(...reqs).pipe(
    map(val => {
      return val.map((curr, i, arr)=> {
        return {... this.myDat[i], showBtn: curr}
      })
    })
  )
}

now you have all data you need inside the observable myData, which you can use in the template like:

<tr *ngFor="let item of myData | async">
  <td>
    {{item.name}}
  </td>
  <td>
    <button *ngIf="item.showBtn" (click)="doSomething(item)">Button!</button>
  </td>
</tr>

STACKBLITZ DEMO

Upvotes: 1

Sameer Ahmed
Sameer Ahmed

Reputation: 190

async pipe doesn't work like that, also your approach to show hide a button is not correct as there might several thousands of rows in the table and calling an api this many time is not good for the application performance.

You can try below:

  1. Use Variable based *ngIf.
  2. You can also include a flag in your table data to show/hide the button
  3. Or remove async pipe it may work fine.

Upvotes: 0

Bert Maurau
Bert Maurau

Reputation: 979

The async pipe expects an Observable. I would recommend using a variable instead of a function (to keep it simple and because running functions in your template is 'bad practice') like:

public showButton: boolean;

ngOnIinit() {
  this.shouldShowButton();
}

shouldShowButton() {
  this.service.getJson().subscribe(data => {
              this.showButton = true;
            }, error => {
              this.showButton = false;
            });
 }



 <button mat-raised-button *ngIf="this.showButton"></button>

For an 'unknown amount of buttons' you could use an object.

public showButtons: any = {};

ngOnIinit() {
      this.shouldShowButton('button1');
      this.shouldShowButton('button2');
      this.shouldShowButton('button2');
      ....
    }

    shouldShowButton(buttonName: string) {
      this.service.getJson(buttonName).subscribe(data => {
                  this.showButtons[buttonName] = true;
                }, error => {
                  this.showButtons[buttonName] = false;
                });
     }


<button mat-raised-button *ngIf="this.showButtons['button1']"></button>
<button mat-raised-button *ngIf="this.showButtons['button2']"></button>
<button mat-raised-button *ngIf="this.showButtons['button3']"></button>

Upvotes: 2

Wandrille
Wandrille

Reputation: 6811

An *ngIf with the async pipe is expecting an Observable. You give a Subscription instead.

  • remove your subcription in your .ts
  • or do as @Bert Maurau suggests

Upvotes: 0

Related Questions