0
votes

I have a component with an Add button that will go to the database, add a new record, then update the observable's data. The function to add the new record is a Promise that resolves once the new record has been added. While the component is reaching out to the database, I'd like to hide the Add button until the new record has been added. The template and component look roughly as follows:

Template:

<table>
...
</table>

<button (click)="addRow()" *ngIf="!addingRow">Add New</button>

Component Class (typescript):

export class AuctionComponent {
  addingRow: boolean = false;

  addRow() {
    this.addingRow=true;
    console.log('Adding Row');
    this.salesSvc.addBid()
      .then(()=> {
        this.addingRow = false;
        console.log('finished');
       });
  }
}

The promise to add the new record resolves in roughly one second. This is what I would think would happen:

  • Click on Add Button
  • {button disappears}
  • "Adding Row" shows up in the console
  • {wait a second for the promise to resolve}
  • {button reappears}
  • "finished" shows up in the console

Here is what is actually happening:

  • Click on Add Button
  • "Adding Row" shows up in the console
  • {wait for a second for the promise to resolve}
  • "finished" shows up in the console
  • {button disappears briefly & reappears}

I've tried using ngZone.run(), ChangeDetectorRef .markForCheck() & .detectChanges(), and setTimeout(). I've also played around with ChangeDetectionStrategy of Default & OnPush. What am I missing?

Update

So the promise is reaching out and saving the data to firebase. Something with having to do with resolving the promise in firebase seems to be holding this up.

I changed the component to just test the promise part of it:

export class AuctionComponent {
  addingRow: boolean = false;

  addRow() {
    this.addingRow=true;
    console.log('Adding Row');
  //  this.salesSvc.addBid()
    this.promise()
      .then(()=> {
        this.addingRow = false;
        console.log('finished');
       });
  }

  promise() {
    return new Promise((resolve, reject) => {
      setTimeout(resolve, 2000);
    });
  }
}

Doing this makes the button behave just like I anticipated.

The addBid method in the service looks like the following (this.af is AngularFire):

  addBid(sale: ISale) {
    return this.af.database.list(`sales/${sale.year}/bids`)
        .push({lot: null, price: 0, description: ''});
  }

So I thought that maybe something is happening with the firebase flavor of promise, so I wrapped the whole thing in my own promise:

  addBid(sale: ISale) {
    return new Promise((resolve, reject) => {
      this.af.database.list(`sales/${sale.year}/bids`)
        .push({lot: null, price: 0, description: ''})
        .then(() => resolve())
        .catch(() => reject());
    });   
  }

But again, I'm still getting the delayed response. Is there something in firebase/angularfire2 that's causing the repaint to not happen?

2
try <div *ngIf="!addingRow" ><button (click)="addRow()" >Add New</button></div> - refactor
Tested your code, and for me it worked just as should...? - AT82
@CleanCrispCode Good suggestion...I tried that, but am getting the same results. - jloosli
@AJT_82 I've done this before in other Angular2 projects without any problems, but I can't quite figure out why it's not working here. I'm assuming there's some sort of change detection optimization that I'm running into, but I don't know why I'm running into it now. - jloosli
@jloosli yes, as said, I tested your code and it works fine, there must be something else going on. What if you try to reproduce the issue in a plunker, that might narrow it down, to find the problem easier, if it works in plunker :) - AT82

2 Answers

0
votes

What you can do is, when you get the response from server store it in some variable and make another get method which will return true or false depending on the length of the data you will receive. So when you will have the data you will have the length of the data, in this case return true otherwise false. Bind this get method to the button.

second option: Initially you are setting the variable as true (when the button was clicked) this is correct. Now in get method you can return this variable as false/true (depending on the logic to hide/show) when you have the data length.

I think this should work in your case.

0
votes

For what it's worth, I was able to get this to work by wrapping the promise part of addRow in setTimeout(). I'm still not sure why that is needed or why it works, so I'd love to get some feedback or direction in that area.

  addRow() {
    this.addingRow = true;
    setTimeout(() => {
      this.salesSvc.addBid(this.sale)
        .then(() => {
          this.addingRow = false;
        });
    }, 0);