Tom
Tom

Reputation: 8681

ngx-spinner not synchronizing with loading of angular page

I have implemented ngx-spinner in my angular 7 application.

The spinner loads but I want it to spin only when the data is loading. At the moment I can see some part of the page in advance and then spinner loads.

Where am I going wrong?

I have written the this.spinner.show(); in getAllocationsDetails()

HTML

<style>

  .alert {
    margin-top: 10px;
    height: 500x;
    position: relative;
    width: 100%;
    /* padding: 15px; */
    bottom: 0%;
    border: 1px solid transparent;
    border-radius: 4px;
    float: left;
  }

  .alert-danger {
    background-color: #f2dede;
    border-color: #ebccd1;
    color: #a94442;
  }


  table {
    border-collapse: collapse;
  }

  .spacing {
    padding-top: .5em;
    padding-bottom: .5em;
  }

  .k-tabstrip>.k-content.k-state-active {
     display: none!important;
}

</style>

<ngx-spinner bdOpacity = 0.9
bdColor = "#ce6d43"
size = "medium"
color = "#fff"
type = "ball-spin"
fullScreen = "true"
> <p style="color: white" > Loading... </p>
</ngx-spinner>

<div class="form-group row col-md-12" style="margin-top:30px">
  <label for="inputFax" class="col-md-1  col-form-label " style="font-weight: bold;">Eval Date</label>
  <div class="col-md-1">

    <kendo-datepicker style="width: 100% ;float: left;"  [format]="'MMMM yyyy'"  [topView]="'decade'" [bottomView]="'year'" [(ngModel)]="evalDate"
      (valueChange)="evalDateChanged($event)">
    </kendo-datepicker>
  </div>

  <div class="col-md-2" style="padding-left: 10px; width: 100%"><a class="btn btn-primary "
      (click)="downloadFundAllocationDetails()" [attr.href]="Url">Export To
      EXCEL<i title="PDF" class="fa fa-file-excel-o"></i> </a></div>
</div>

<div class="form-group row">
  <div class="panel panel-default col-md-12">
    <div *ngIf="AllocationDetails && AllocationDetails.ManagerAllocations" class="panel-body">
      <div [style.height.px]="GridHeight()" [style.width.%]="100" style="float: left;">
        <span style="text-decoration: underline; cursor: pointer;padding-right: 10px;"><a (click)="expandAll()">Expand
            All</a></span>
        <span style="text-decoration: underline; cursor: pointer;"><a (click)="collapseAll()">Collapse
            All</a></span>
        <ag-grid-angular #agGrid class="ag-theme-balham" [gridOptions]="GridOptions" style="width: 100%; height: 100%"
          [columnDefs]="ColumnDefs" [rowData]="AllocationDetails.ManagerAllocations" rowHeight="30" headerHeight="30"
          rowSelection="single">
        </ag-grid-angular>
      </div>

    </div>
  </div>
</div>

<div class="form-group row">

  <div class="panel panel-default col-md-12">
    <div style="height: 100%; width: 100%;"
      *ngIf="AllocationDetails && AllocationDetails.MissingProducts" class="alert alert-danger col-md-5">
      <p><strong>The investments made in the following products are not included in this report:</strong></p>
      <table>
        <ng-container *ngFor="let group of AllocationDetails.MissingProducts">
          <tr>
            <th> {{group[0].ProductType}}</th>
          </tr>
          <tr *ngFor="let post of group">
            <td> {{ post.ProductName  }} </td>
          </tr>
          <tr>
            <td class="spacing"></td>
          </tr>
        </ng-container>
      </table>
    </div>

    <div style="height: 100%; width: 100%; float:left;" *ngIf="AllocationDetails && AllocationDetails.ChartData"
      class="col-md-7">
      <app-pie-chart [series]="allocation_series"></app-pie-chart>
    </div>


  </div>
</div>

Typescript Component

import { Component, NgZone, Input } from '@angular/core';
import { OnInit } from '@angular/core';
import { AllocationsService } from '../services/allocations.service';
import { formatDate } from '@angular/common';
import { GridOptions } from 'ag-grid-community/dist/lib/entities/gridOptions';
import { Comparator } from '../utilities/comparator';
import { ActivatedRoute } from '@angular/router';
import { NgxSpinnerService } from 'ngx-spinner';


@Component({
    selector: 'mgr-allocations',
    templateUrl: './allocations.component.html'
})


export class AllocationsComponent implements OnInit {

    private Error: string;
    public evalDate: Date;
    private _evalDate: Date;
    public AllocationDetails: any;
    private _ManagerStrategyId: number;
    public GridOptions: GridOptions;
    ExportingToExcel: boolean;
    windowHeight: any;
    offset: number;
    ngZone: any;
    router: any;
    Comparator: Comparator;
    Route: any;
    public allocation_series: any;
    public LoadingAllocations = true;

    public get ManagerStrategyId(): number {
        return this._ManagerStrategyId;
    }


    @Input()
    public set ManagerStrategyId(value: number) {
        this._ManagerStrategyId = value;
    }


    constructor(private allocationsService: AllocationsService, private comparator: Comparator,
        private zone: NgZone, private route: ActivatedRoute,private spinner: NgxSpinnerService) {
        this.Comparator = comparator;
        this.Route = route;

        window.onresize = (e) => {
            this.ngZone.run(() => {
                this.windowHeight = window.innerHeight - this.offset;
                setTimeout(() => {
                    if (!this.GridOptions || !this.GridOptions.api) {
                        return;
                    }
                    this.GridOptions.api.sizeColumnsToFit();
                }, 500, true);
            });
        };
    }

    private FormattedDate(dateToFormat: Date): string {
        return formatDate(dateToFormat, 'yyyy/MM/dd', 'en');
    }


    setGridOptions() {
        this.GridOptions = {
            columnDefs: this.getColumns(),
            enableFilter: true,
            treeData: true,
            enableColResize: true,
            animateRows: true,
            groupDefaultExpanded: 1,
            enableSorting: true,
            suppressAggFuncInHeader: false,

            getDataPath: function (data) {
                return data.Hierarchy;
            },
            onGridReady: e => {
                if (!e || !e.api) {
                    return;
                }
                e.api.sizeColumnsToFit();
                this.setDefaultSortOrder();
            },
            getRowStyle: (params) => {
                if (params.node.level === 0) {
                    return { 'background-color': '#FCE7D7' };
                }
            },

            autoGroupColumnDef: {
                headerName: 'Manager Strategy', width: 300,
                valueFormatter: uniqueColumn
            },

        };
        function uniqueColumn(params) {
            //     if (!params.value)  return;
            //    return  params.value.split('#')[0];
            const startIndex = params.value.indexOf('#');
            if (startIndex === -1) { return params.value; }

            const endIndex = params.value.length;
            return params.value.replace(params.value.substring(startIndex, endIndex), '');

        }
    }

    ngOnInit() {


        // setTimeout(() => {
        //     /** spinner ends after 5 seconds */
        //     this.spinner.hide();
        // }, 5000);

        this.evalDate = new Date();
        this.setGridOptions();
        this.getAllocationsDetails(this.FormattedDate(this.evalDate));


    }

    public getAllocationsDetails(evalDate: string) {
        if (this.ManagerStrategyId != null) {
            this.initGrid();

            this.allocationsService.getAllocationsDetails(this.ManagerStrategyId, evalDate)
                .subscribe(data => {
                    this.spinner.show();
                    this.LoadingAllocations = true;
                    this.AllocationDetails = data;

                    this.GridOptions.rowData = this.AllocationDetails;
                    this.allocation_series = this.AllocationDetails.ChartData;

                    setTimeout(() => {
                       this.spinner.hide();
                    }, 1000, true);

                },
                    err => {
                        this.Error = 'An error has occurred. Please contact BSG';
                    },
                    () => {
                    });
        }
    }


    downloadFundAllocationDetails() {
        this.ExportingToExcel = true;
        this.allocationsService.downloadFundAllocationDetails(this.ManagerStrategyId, this.evalDate)
            .subscribe(blob => {
                const fileName = ' as of ' + 'TestDate';
                const fileURL = window.URL.createObjectURL(blob);
                const extention = '.xlsx';
                const downloadLink = document.createElement('a');
                downloadLink.href = fileURL;
                downloadLink.download = fileName + extention;
                document.body.appendChild(downloadLink);
                downloadLink.click();
                document.body.removeChild(downloadLink);
                this.ExportingToExcel = false;
            },
                err => {
                    this.ExportingToExcel = false;
                    this.Error = 'An error has occurred. Please contact BSG';
                },
                () => {
                });
    }


    public evalDateChanged(value: Date): void {
        this.getAllocationsDetails(this.FormattedDate((value)));
    }

    GridHeight() {
        if (!this.windowHeight) {
            this.windowHeight = window.innerHeight - this.offset + 10;
        }
        return this.windowHeight;
    }


    setDefaultSortOrder() {
        const defaultSortModel = [
            { colId: 'ManagerStrategyName', sort: 'asc' },
            { colId: 'UsdEmv', sort: 'desc' }
        ];
        this.GridOptions.api.setSortModel(defaultSortModel);
    }

    private initGrid() {
        const self = this;
    }

    private getColumns(): Array<any> {
        const self = this;
        const definition = [
            { headerName: 'Date', field: 'EvalDate', hide: true },
            { headerName: 'Firm ID', field: 'FirmID', hide: true },
            { headerName: 'Manager Strategy ID', field: 'FirmName', hide: true },
            { headerName: 'Firm', field: 'ManagerStrategyID', hide: true },
            { headerName: 'Manager Strategy', field: 'ManagerStrategyName', hide: false },
            { headerName: 'Fund ID', field: 'ManagerFundID', hide: true },
            { headerName: 'Fund', field: 'ManagerFundName' },
            { headerName: 'Product Name', field: 'ProductName'},
            {
                headerName: 'As Of', field: 'EvalDate',
                cellStyle: {textAlign: 'right'}
            },
            {
                headerName: 'EMV (USD)', field: 'UsdEmv', valueFormatter: this.currencyFormatter, rowGroup: false,
                cellRenderer: 'agGroupCellRenderer',
                aggFunc: 'sum',
                cellStyle: {textAlign: 'right'}
            },
            {
                headerName: '% of Fund Strategy', field: 'GroupPercent', valueFormatter: this.formatPercent, rowGroup: false,
                cellRenderer: 'agGroupCellRenderer',
                aggFunc: 'sum',
                cellStyle: {textAlign: 'right'}
            },
            {
                headerName: '% of Product', field: 'WeightWithEq',
                 valueFormatter: this.formatPercent,
                 cellStyle: {textAlign: 'right'}
            }
        ];
        return definition;
    }


    public expandAll() {
        this.GridOptions.api.expandAll();
    }

    public collapseAll() {
        this.GridOptions.api.collapseAll();
    }


    currencyFormatter(number) {
        // this puts commas into the number eg 1000 goes to 1,000,
        if (!isNaN(number.value)) {
            number = Math.floor(number.value).toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
            number = number === '0' ? '0.00' : number;
            return '$' + number;
        }
    }

    formatPercent(number) {
        if (!isNaN(number.value) && number.value !== null) {
            return (number.value).toFixed(2) + '%';
        }
    }

}

service code

The service returns an observable. It calls the http.get

getAllocationsDetails(mgrStrategyId: number, evalDate: string) {
                let pars = new HttpParams();
                pars = pars.append('id', mgrStrategyId.toString());
                pars = pars.append('date', evalDate);
                const url = this.config.api.allocationDetails;
                return this.http.get(url, { params: pars, withCredentials: true });
    }

Upvotes: 3

Views: 6784

Answers (3)

Yugant Pardeshi
Yugant Pardeshi

Reputation: 87

the solution is very simple. you have to display spinner before you are giving call to the subscription and then hide the spinner in the success of the subscription.

For Example,

ngOnInit(){
this.spinner.show();

this.yourServiceName.methodName().subscribe((response : any)=> {
// condition checks if any
this.spinner.hide();
},(error) => {
// if api gives you an error like 500 etc then also you have to stop the spinner
this.spinner.hide();
});

Thats it!

Upvotes: 0

Chathuran D
Chathuran D

Reputation: 2430

you have to Change public getAllocationsDetails(evalDate: string) code segment in typescript class to below changes

 public getAllocationsDetails(evalDate: string) {
        if (this.ManagerStrategyId != null) {
            this.initGrid();
 this.spinner.show();
            this.allocationsService.getAllocationsDetails(this.ManagerStrategyId, evalDate)
                .subscribe(data => {
                    this.spinner.show();
                    this.LoadingAllocations = true;
                    this.AllocationDetails = data;

                    this.GridOptions.rowData = this.AllocationDetails;
                    this.allocation_series = this.AllocationDetails.ChartData;

                    setTimeout(() => {
                       this.spinner.hide();
                    }, 1000, true);

                },
                    err => {
                        this.Error = 'An error has occurred. Please contact BSG';
                    },
                    () => {
                    });
        }
    }

this.spinner.show(); must initiate before service call initiate

see the sample code

Hope this will help..!!

Upvotes: 0

Daniel
Daniel

Reputation: 11162

Your problem is that you show the spinner in your subscribe. That is the code that's executing when the observable result is ready.

Instead you should start your spinner before the observable subscription, and stop the spinning inside the finalize function that runs when the observable has completed (see docs)

RxJS 6 syntax

(You have to use the .pipe operator and import the finalize function)

import { finalize } from 'rxjs/operators';

...

this.spinner.show();

this.allocationsService.getAllocationsDetails(this.ManagerStrategyId, evalDate)
  .pipe(
    finalize(() => this.spinner.hide())
  )
  .subscribe(data => {
    // Handle your data
  });

Upvotes: 3

Related Questions