4
votes

I am currently implementing ag-grid in my angular 7 application. I need to include the footer for each group which I have achieved. I would now need to display the Total of EMV(USD) and Percent Column like the screenshot below. The row should be blank for each cell except for the two columns mentioned and the totals should be displayed

enter image description here

Component

import { Component, Injectable, NgZone, ViewEncapsulation, ViewChild, 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 { TestBed } from '@angular/core/testing';


@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;
    windowHeight: any;
    offset: number;
    ngZone: any;
    router: any;
    Comparator: Comparator;
    Route: any;

    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) {
        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');
    }


    get MissingProductKeys() {
        const  missingProductsTypesNames = this.AllocationDetails.MissingProducts.flat().map(({ProductType}) => ProductType);
        const  uniqueProductTypeNames = new Set(missingProductsTypesNames);
        return  Array.from(uniqueProductTypeNames.values());
    }

    setGridOptions() {
        this.GridOptions = {
            columnDefs: this.getColumns(),
            enableFilter: true,
             treeData: true,
            enableColResize: true,
            animateRows: true,
            groupDefaultExpanded: 1,
            enableSorting: true,
            suppressCellSelection: true,
            groupIncludeFooter: true,
            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) {

        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() {
        this.evalDate = new Date();
        this.setGridOptions();
        this.getAllocationsDetails(this.FormattedDate(this.evalDate));

    }

     getAllocationsDetails(evalDate: string) {
        if (this.ManagerStrategyId != null) {
            this.initGrid();
           //this.GridOptions.api.showLoadingOverlay();
            this.allocationsService.getAllocationsDetails(this.ManagerStrategyId, evalDate)
                .subscribe(data => {
                    this.AllocationDetails = data;
                    this.GridOptions.rowData = this.AllocationDetails.ManagerAllocations;
                    setTimeout(() => {
                      //  this.GridOptions.api.hideOverlay();
                    }, 100, true);
                },
                    err => {
                        this.Error = 'An error has occurred. Please contact BSG';
                    },
                    () => {
                      //  this.GridOptions.api.hideOverlay();
                    });
        }
    }


    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: 'ManagerFundName', sort: 'asc' }
        ];
        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: 'Portfolio', field: 'ProductName' },
            { headerName: 'As Of', field: 'EvalDate',   cellRenderer: (data) => {
                return data.value ? (new Date(data.value)).toLocaleDateString() : '';
             } },
            { headerName: 'EMV (USD)', field: 'UsdEmv',  valueFormatter: currencyFormatter },
            { headerName: 'Percent', field: 'GroupPercent', valueFormatter: formatPercent },





        ];
        function currencyFormatter(params) {
            if (!isNaN(params.value)) {
            return '$' + formatNumber(params.value);
            }
        }

        function formatNumber(number) {
            // this puts commas into the number eg 1000 goes to 1,000,
             return Math.floor(number).toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
        }

        function formatPercent(number) {
            if (!isNaN(number.value)) {
            return (number.value * 100).toFixed(2) + '%';
            }
        }
        return definition;
    }
}

New code based on proposed answer

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: 'Portfolio', field: 'ProductName' },
            {
                headerName: 'As Of', field: 'EvalDate', cellRenderer: (data) => {
                    return data.value ? (new Date(data.value)).toLocaleDateString() : '';
                }
            },
            {
                headerName: 'EMV (USD)', field: 'UsdEmv', valueFormatter: this.currencyFormatter,
                cellRenderer: 'agGroupCellRenderer',
                aggFunc: 'sum',
                cellRendererParams: {
                    footerValueGetter: (params) =>   params.value

                }
            },
            {
                headerName: 'Percent', field: 'GroupPercent', valueFormatter: this.formatPercent,
                cellRenderer: 'agGroupCellRenderer',
                aggFunc: 'sum',
                cellRendererParams: {
                    footerValueGetter: (params) => params.value
                }
            }
        ];
        return definition;
    }


    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)) {
            return (number.value * 100).toFixed(2) + '%';
        }
    }

New Screenshot

enter image description here

4
and pls stop sharing the screenshots, share plinkr\stackblitz url instead where everyone can test ur approach - un.spike

4 Answers

5
votes

I've made it in a different way:

let pinnedBottomData = this.generatePinnedBottomData();
this.gridApi.setPinnedBottomRowData([pinnedBottomData]);

generatePinnedBottomData(){
    // generate a row-data with null values
    let result = {};

    this.gridColumnApi.getAllGridColumns().forEach(item => {
        result[item.colId] = null;
    });
    return this.calculatePinnedBottomData(result);
}

calculatePinnedBottomData(target:any){
    //list of columns for aggregation
    let columnsWithAggregation = ['age']
    columnsWithAggregation.forEach(element => {
        this.gridApi.forEachNodeAfterFilter((rowNode: RowNode) => {
            if (rowNode.data[element])
                target[element] += Number(rowNode.data[element].toFixed(2));
        });
        if (target[element])
            target[element] = `Age Sum: ${target[element].toFixed(2)}`;
    })
    return target;
}

Demo

2
votes

For each column definition, you can pass an extra parameter to change how the column is rendered

cellRenderer:'agGroupCellRenderer',
cellRendererParams: {
    footerValueGetter: (params) => 'Text (' + params.value + ')'
},
aggFunc: "sum"

where params.value is the column name

2
votes

Ag Grid has attributes something called groupIncludeFooter, groupIncludeTotalFooter. You can refer it on https://www.ag-grid.com/javascript-grid-grouping/#grouping-footers

  <ag-grid-angular
      ...
      [groupIncludeFooter]="true"
      [groupIncludeTotalFooter]="true"
      ...
    ></ag-grid-angular>

https://plnkr.co/edit/pBG3to7sebxDC7Yq

Hope it helps :-)

1
votes

I have found this earlier on, whereby you can make use of aligned grids as footer. They have included an example, so you should refer to it.

A summary, on the component.html, you have to include the ag-grid-angular component twice on your template, each of them with its own individual gridOptions properties

<ag-grid-angular #topGrid [gridOptions]="topOptions"></ag-grid-angular>
<ag-grid-angular #bottomGrid [gridOptions]="bottomOptions".... ></ag-grid-angular>

And on your component.ts, you will define your columnDefs as usual, as well as the respective gridOptions.

As mentioned on the example on the link above, aligning the two grids will give you a footer grid which is vertically aligned to your main grid. This way, you can display the total value, etc on that footer.