Search code examples
htmlangularcomponentsobservablesubscribe

Add animation to the refresh button in the grid Angular


When there is no updated data in a grid, the refresh button will visualy do nothing. This is confusing for the user, so i want to add an animation to the button during the reload of the grid. Use the font-awsome way.

HTML:

    <hum-button
  [tooltipContent]="'GridComponent.refreshGrid' | translate"
  iconCss="fa fa-refresh"
  [isPrimary]="true"
  [onlyIcon]="true"
  (click)="onRefreshGridClick()"
>
</hum-button>

Angular: grid.component.ts

public refreshData(): void {
if (typeof this.refreshGridFn === 'function') {
  this.refreshGridFn();
  return;
}

if (this.agGrid) {
  return this.agGrid.refreshData();
}

}

Angular: ag-grid.component.ts

  public refreshData(): void {
if (!this.customDatasource) {
  this.log('refreshData');
  if (this.dataSource && this.dataSource.rowCount > 0) {
    this.gridApi.refreshInfiniteCache();
  } else {
    this.gridApi.purgeInfiniteCache();
  }
}

}

this button


Solution

  • There're several options.

    One of then is using .css. If you defined

    .rotate {
      animation: rotation 2s infinite linear;
    }
    @keyframes rotation {
      from {
        transform: rotate(0deg);
      }
      to {
        transform: rotate(359deg);
      }
    }
    

    You can use some like

    <mat-icon [class.rotate]="loading">refresh</mat-icon>
    

    And put the variable loading to true/false

    You can also use Angular animations:

    animations:[
        trigger('rotate', [
        transition('void=>*',[style({transform:'rotate(0)'})]),
        transition ('* => *', [
          style({ transform: 'rotate(0)' }),
          animate ('650ms ease-in-out',style ({ transform: 'rotate(360deg)' }),
          ),
        ])
      ])
    

    You use

    <mat-icon [@rotate]="count" 
         (@rotate.done)="loading && count=count+1>refresh</mat-icon>
    

    And define two variables

    count:any;
    loading;
    

    When you want to start

    this.count=this.count!=1?1:0
    this.loading=true
    

    When finished

    this.loading=false
    

    In the stackblitz you has the two options (just click the buttons to start/end animation)

    Update

    Well, we want to use when call to an API. So I imagine a service that return an observable

    We has two approach:

    1. Using a variable and finalize rxjs operator.

        getData()
        {
          this.loading=true;
          this.count=this.count==1?0:1
          this.dataService.getData().pipe(finalize(()=>{
            this.loading=false
          })
          ).subscribe(res=>this.data=res)
        }
      
    2. Other approach is use an operator indicate as it's how in this SO (that sadly had a little attention) This operator need a Subject, so the functions becomes like

        getAllData()
        {
          this.count2=this.count2==1?0:1
          this.dataService.getData().pipe(indicate(this.loading$,0)
          ).subscribe(res=>this.data=res)
      
        } 
      

      But the .html it's a bit complex

      <!-using css-->
      <button mat-button (click)="getAllData()">
           <mat-icon [class.rotate]="loading$|async">
               refresh
           </mat-icon>
      </button>
      
      <!--using Angular animations-->
      <button mat-button (click)="getAllData()">
        <ng-container *ngIf="{loading:loading$|async} as load">
        <mat-icon [@rotate]="count2" 
                  (@rotate.done)="load.loading && count2=count2+1">
           refresh
        </mat-icon>
      </ng-container>
      </button>
      

    The advantage of use an operator is that you can use with any observable -even the rxjs operator of-

    I just update the stackblitz