Serenity: How to have Grouping and Summary Info in Excel and PDF

Created on 21 Oct 2016  路  9Comments  路  Source: serenity-is/Serenity

For basic listing Excel and PDF works fine, but how can this be extended to having grouping and summary information displayed in the grid be shown in Excel and PDF export functions.

currently the table is again read and dumped in excel and PDF, therefore there is no way to show the groupings and group footers and overall report footer in excel and PDF exports.

I am thinking of designing templates for each of these excel files which would contain the header and footer rows. the footer rows as explained below

https://excelpackage.codeplex.com/wikipage?title=Using%20a%20template%20to%20create%20an%20Excel%20spreadsheet

can anyone suggest any better alternative.

add-sample

Most helpful comment

Replace your Modules\Common\Reporting\PdfExportHelper.ts in your solution with the following code
This will generate PDF with grouping.

namespace YourRootNameSpace.Common {
    export interface PdfExportOptions {
        grid: Serenity.DataGrid<any, any>;
        onViewSubmit: () => boolean;
        title?: string;
        hint?: string;
        separator?: boolean;
        reportTitle?: string;
        titleTop?: number;
        titleFontSize?: number;
        fileName?: string;
        pageNumbers?: boolean;
        columnTitles?: { [key: string]: string };
        tableOptions?: jsPDF.AutoTableOptions;
        output?: string;
        autoPrint?: boolean;
    }

    export namespace PdfExportHelper {

        function toAutoTableColumns(srcColumns: Slick.Column[], columnStyles: { [dataKey: string]: jsPDF.AutoTableStyles; },
            columnTitles: { [key: string]: string }) {
            return srcColumns.map(src => {
                let col: jsPDF.AutoTableColumn = {
                    dataKey: src.id || src.field,
                    title: src.name || ''
                };

                if (columnTitles && columnTitles[col.dataKey] != null)
                    col.title = columnTitles[col.dataKey];

                let style: jsPDF.AutoTableStyles = {};
                if ((src.cssClass || '').indexOf("align-right") >= 0)
                    style.halign = 'right';
                else if ((src.cssClass || '').indexOf("align-center") >= 0)
                    style.halign = 'center';

                columnStyles[col.dataKey] = style;

                return col;
            });
        }

        function toAutoTableData(entities: any[], keys: string[], srcColumns: Slick.Column[]) {
            let el = document.createElement('span');
            let row = 0;
            return entities.map(item => {
                let dst = {};
                for (let cell = 0; cell < srcColumns.length; cell++) {
                    let src = srcColumns[cell];
                    let fld = src.field || '';
                    let key = keys[cell];
                    let txt;
                    let html: string;
                    if (src.formatter) {
                        html = src.formatter(row, cell, item[fld], src, item);
                    }
                    else if (src.format) {
                        html = src.format({ row: row, cell: cell, item: item, value: item[fld] });
                    }
                    else {
                        dst[key] = item[fld];
                        continue;
                    }

                    if (!html || (html.indexOf('<') < 0 && html.indexOf('&') < 0))
                        dst[key] = html;
                    else {
                        el.innerHTML = html;
                        if (el.children.length == 1 &&
                            $(el.children[0]).is(":input")) {
                            dst[key] = $(el.children[0]).val();
                        }
                        else if (el.children.length == 1 &&
                            $(el.children).is('.check-box')) {
                            dst[key] = $(el.children).hasClass("checked") ? "Yes" : "No"
                        }
                        else
                            dst[key] = el.textContent || '';
                    }
                }
                row++;
                return dst;
            });
        }

        export function exportToPdf(options: PdfExportOptions): void {

            var g = options.grid;

            if (!options.onViewSubmit())
                return;

            includeAutoTable();

            var request = Q.deepClone(g.view.params) as Serenity.ListRequest;
            request.Take = 0;
            request.Skip = 0;

            var sortBy = g.view.sortBy;
            if (sortBy != null)
                request.Sort = sortBy;

            var gridColumns = g.slickGrid.getColumns();
            gridColumns = gridColumns.filter(x => x.id !== "__select__");

            request.IncludeColumns = [];
            for (var column of gridColumns)
                request.IncludeColumns.push(column.id || column.field);

            Q.serviceCall({
                url: g.view.url,
                request: request,
                onSuccess: response => {
                    var doc = new jsPDF('l', 'pt');
                    var groupings = g.view.getGrouping(); //group fields
                    var groupingColumns = gridColumns.filter(f => groupings.some(s => s.getter == f.field) == true);
                    var srcColumns = gridColumns.filter(f => groupings.some(s => s.getter == f.field) == false);
                    var columnStyles: { [dataKey: string]: jsPDF.AutoTableStyles; } = {};
                    var columns = toAutoTableColumns(srcColumns, columnStyles, options.columnTitles);
                    var keys = columns.filter(f => groupings.some(s => s.getter == f) == false).map(x => x.dataKey);

                    var totalPagesExp = "{{T}}";

                    var pageNumbers = options.pageNumbers == null || options.pageNumbers;

                    var autoOptions = $.extend({
                        margin: { top: 25, left: 25, right: 25, bottom: pageNumbers ? 25 : 30 },
                        startY: 90,
                        styles: {
                            fontSize: 8,
                            overflow: 'linebreak',
                            cellPadding: 2,
                            valign: 'middle'
                        },
                        columnStyles: columnStyles
                    }, options.tableOptions) as jsPDF.AutoTableOptions;

                    ///region Title
                    {
                        doc.setFontSize(18);
                        doc.autoTableText('Your Report Title', doc.internal.pageSize.width / 2,
                            options.titleTop || 25, { halign: 'center', fontSize: 18 });

                        let reportTitle = '';
                        if (groupingColumns[0])
                            reportTitle = groupingColumns.map(m => m.name).join(', ') + ' wise '

                        reportTitle += options.reportTitle || g.getTitle();
                        reportTitle += " Report";

                        doc.setFontSize(12);
                        doc.autoTableText(reportTitle, doc.internal.pageSize.width / 2,
                            options.titleTop + 30 || 60, { halign: 'center', fontSize: 14 });
                    }
                    ///region Header
                    {
                        var header = function (data) {

                        };
                        autoOptions.beforePageContent = header;
                    }

                    ///region Footer
                    {
                        if (pageNumbers) {
                            var footer = function (data) {
                                var str = data.pageCount;
                                // Total page number plugin only available in jspdf v1.0+
                                if (typeof doc.putTotalPages === 'function') {
                                    str = str + " / " + totalPagesExp;
                                }
                                doc.autoTableText(str, doc.internal.pageSize.width / 2,
                                    doc.internal.pageSize.height - autoOptions.margin.bottom, {
                                        halign: 'center'
                                    });
                            };
                            autoOptions.afterPageContent = footer;
                        }
                    }

                    ///region Content
                    {
                        var headerHeight = 100;
                        var headerFontSizeBase = 11;

                        var entities = (<Serenity.ListResponse<any>>response).Entities || [];

                        g.setItems(entities);

                        var groups = g.view.getGroups(); //grouped data
                        if (groups.length > 0) {
                            var ggg = function (grps: Slick.Group<any>[], parentGroupIndex) {
                                var endPosY = doc.autoTableEndPosY();
                                for (let i = 0; i < grps.length; i++) {
                                    var group = grps[i];
                                    var level = group.level + 1;
                                    var headerFontSize = headerFontSizeBase - level;

                                    doc.setFontSize(headerFontSize);

                                    var lineHeight = doc.getLineHeight();//2 is for padding

                                    if (endPosY < doc.autoTableEndPosY())
                                        endPosY = doc.autoTableEndPosY();

                                    if (i == 0 && parentGroupIndex == -1) { //for root levels first group
                                        endPosY += headerHeight + lineHeight * level;
                                    } else if (i == 0 && parentGroupIndex == 0) {
                                        endPosY += headerHeight + lineHeight * level * 1.5;
                                    } else if (i > 0 && parentGroupIndex == -1) {
                                        endPosY += 5;
                                    }
                                    else if (i > 0 && parentGroupIndex == 0) {
                                        endPosY += lineHeight;
                                    }
                                    else {
                                        endPosY += lineHeight * level * 1.5;
                                    }





                                    doc.autoTableText(group.title, 30 + level * 10, endPosY,
                                        { halign: 'left' });




                                    //console.log(i + ' - ' + level + ' - ' +endPosY + ' - ' + group.title);

                                    if (group.groups) {

                                        ggg(group.groups, i);

                                    } else {

                                        let data = toAutoTableData(group.rows, keys, srcColumns);
                                        autoOptions.startY = endPosY + lineHeight * 1.3;
                                        autoOptions.margin.left = 30 + level * 10;
                                        doc.autoTable(columns, data, autoOptions);
                                    }
                                }
                            }

                            ggg(groups, -1);

                        } else {
                            let data = toAutoTableData(g.getItems(), keys, srcColumns);
                            autoOptions.startY = headerHeight;
                            doc.autoTable(columns, data, autoOptions);
                        }
                    }

                    if (typeof doc.putTotalPages === 'function') {
                        doc.putTotalPages(totalPagesExp);
                    }


                    if (!options.output || options.output == "file") {
                        var fileName = options.reportTitle || "{0}_{1}.pdf";
                        fileName = Q.format(fileName, g.getTitle() || "report",
                            Q.formatDate(new Date(), "yyyyMMdd_HHmm"));
                        doc.save(fileName);
                        return;
                    }

                    if (options.autoPrint)
                        doc.autoPrint();

                    var output = options.output;
                    if (output == 'newwindow' || '_blank')
                        output = 'dataurlnewwindow';
                    else if (output == 'window')
                        output = 'datauri';

                    doc.output(output);
                }
            });
        }

        export function createToolButton(options: PdfExportOptions) {

            return <Serenity.ToolButton>{
                title: options.title || '',
                hint: options.hint || 'PDF',
                cssClass: 'export-pdf-button',
                onClick: () => exportToPdf(options),
                separator: options.separator
            };
        }

        function includeJsPDF() {
            if (typeof jsPDF !== "undefined")
                return;

            var script = $("jsPDFScript");
            if (script.length > 0)
                return;

            $("<script/>")
                .attr("type", "text/javascript")
                .attr("id", "jsPDFScript")
                .attr("src", Q.resolveUrl("~/Scripts/jspdf.min.js"))
                .appendTo(document.head);
        }

        function includeAutoTable() {
            includeJsPDF();

            if (typeof jsPDF === "undefined" ||
                typeof (jsPDF as any).API == "undefined" ||
                typeof (jsPDF as any).API.autoTable !== "undefined")
                return;

            var script = $("jsPDFAutoTableScript");
            if (script.length > 0)
                return;

            $("<script/>")
                .attr("type", "text/javascript")
                .attr("id", "jsPDFAutoTableScript")
                .attr("src", Q.resolveUrl("~/Scripts/jspdf.plugin.autotable.min.js"))
                .appendTo(document.head);
        }
    }
}

declare namespace Slick {
    interface RemoteView<TEntity> {
        getGroups(): Slick.Group<TEntity>[];
        getGrouping(): Slick.GroupInfo<TEntity>[];
    }
}

All 9 comments

Replace your Modules\Common\Reporting\PdfExportHelper.ts in your solution with the following code
This will generate PDF with grouping.

namespace YourRootNameSpace.Common {
    export interface PdfExportOptions {
        grid: Serenity.DataGrid<any, any>;
        onViewSubmit: () => boolean;
        title?: string;
        hint?: string;
        separator?: boolean;
        reportTitle?: string;
        titleTop?: number;
        titleFontSize?: number;
        fileName?: string;
        pageNumbers?: boolean;
        columnTitles?: { [key: string]: string };
        tableOptions?: jsPDF.AutoTableOptions;
        output?: string;
        autoPrint?: boolean;
    }

    export namespace PdfExportHelper {

        function toAutoTableColumns(srcColumns: Slick.Column[], columnStyles: { [dataKey: string]: jsPDF.AutoTableStyles; },
            columnTitles: { [key: string]: string }) {
            return srcColumns.map(src => {
                let col: jsPDF.AutoTableColumn = {
                    dataKey: src.id || src.field,
                    title: src.name || ''
                };

                if (columnTitles && columnTitles[col.dataKey] != null)
                    col.title = columnTitles[col.dataKey];

                let style: jsPDF.AutoTableStyles = {};
                if ((src.cssClass || '').indexOf("align-right") >= 0)
                    style.halign = 'right';
                else if ((src.cssClass || '').indexOf("align-center") >= 0)
                    style.halign = 'center';

                columnStyles[col.dataKey] = style;

                return col;
            });
        }

        function toAutoTableData(entities: any[], keys: string[], srcColumns: Slick.Column[]) {
            let el = document.createElement('span');
            let row = 0;
            return entities.map(item => {
                let dst = {};
                for (let cell = 0; cell < srcColumns.length; cell++) {
                    let src = srcColumns[cell];
                    let fld = src.field || '';
                    let key = keys[cell];
                    let txt;
                    let html: string;
                    if (src.formatter) {
                        html = src.formatter(row, cell, item[fld], src, item);
                    }
                    else if (src.format) {
                        html = src.format({ row: row, cell: cell, item: item, value: item[fld] });
                    }
                    else {
                        dst[key] = item[fld];
                        continue;
                    }

                    if (!html || (html.indexOf('<') < 0 && html.indexOf('&') < 0))
                        dst[key] = html;
                    else {
                        el.innerHTML = html;
                        if (el.children.length == 1 &&
                            $(el.children[0]).is(":input")) {
                            dst[key] = $(el.children[0]).val();
                        }
                        else if (el.children.length == 1 &&
                            $(el.children).is('.check-box')) {
                            dst[key] = $(el.children).hasClass("checked") ? "Yes" : "No"
                        }
                        else
                            dst[key] = el.textContent || '';
                    }
                }
                row++;
                return dst;
            });
        }

        export function exportToPdf(options: PdfExportOptions): void {

            var g = options.grid;

            if (!options.onViewSubmit())
                return;

            includeAutoTable();

            var request = Q.deepClone(g.view.params) as Serenity.ListRequest;
            request.Take = 0;
            request.Skip = 0;

            var sortBy = g.view.sortBy;
            if (sortBy != null)
                request.Sort = sortBy;

            var gridColumns = g.slickGrid.getColumns();
            gridColumns = gridColumns.filter(x => x.id !== "__select__");

            request.IncludeColumns = [];
            for (var column of gridColumns)
                request.IncludeColumns.push(column.id || column.field);

            Q.serviceCall({
                url: g.view.url,
                request: request,
                onSuccess: response => {
                    var doc = new jsPDF('l', 'pt');
                    var groupings = g.view.getGrouping(); //group fields
                    var groupingColumns = gridColumns.filter(f => groupings.some(s => s.getter == f.field) == true);
                    var srcColumns = gridColumns.filter(f => groupings.some(s => s.getter == f.field) == false);
                    var columnStyles: { [dataKey: string]: jsPDF.AutoTableStyles; } = {};
                    var columns = toAutoTableColumns(srcColumns, columnStyles, options.columnTitles);
                    var keys = columns.filter(f => groupings.some(s => s.getter == f) == false).map(x => x.dataKey);

                    var totalPagesExp = "{{T}}";

                    var pageNumbers = options.pageNumbers == null || options.pageNumbers;

                    var autoOptions = $.extend({
                        margin: { top: 25, left: 25, right: 25, bottom: pageNumbers ? 25 : 30 },
                        startY: 90,
                        styles: {
                            fontSize: 8,
                            overflow: 'linebreak',
                            cellPadding: 2,
                            valign: 'middle'
                        },
                        columnStyles: columnStyles
                    }, options.tableOptions) as jsPDF.AutoTableOptions;

                    ///region Title
                    {
                        doc.setFontSize(18);
                        doc.autoTableText('Your Report Title', doc.internal.pageSize.width / 2,
                            options.titleTop || 25, { halign: 'center', fontSize: 18 });

                        let reportTitle = '';
                        if (groupingColumns[0])
                            reportTitle = groupingColumns.map(m => m.name).join(', ') + ' wise '

                        reportTitle += options.reportTitle || g.getTitle();
                        reportTitle += " Report";

                        doc.setFontSize(12);
                        doc.autoTableText(reportTitle, doc.internal.pageSize.width / 2,
                            options.titleTop + 30 || 60, { halign: 'center', fontSize: 14 });
                    }
                    ///region Header
                    {
                        var header = function (data) {

                        };
                        autoOptions.beforePageContent = header;
                    }

                    ///region Footer
                    {
                        if (pageNumbers) {
                            var footer = function (data) {
                                var str = data.pageCount;
                                // Total page number plugin only available in jspdf v1.0+
                                if (typeof doc.putTotalPages === 'function') {
                                    str = str + " / " + totalPagesExp;
                                }
                                doc.autoTableText(str, doc.internal.pageSize.width / 2,
                                    doc.internal.pageSize.height - autoOptions.margin.bottom, {
                                        halign: 'center'
                                    });
                            };
                            autoOptions.afterPageContent = footer;
                        }
                    }

                    ///region Content
                    {
                        var headerHeight = 100;
                        var headerFontSizeBase = 11;

                        var entities = (<Serenity.ListResponse<any>>response).Entities || [];

                        g.setItems(entities);

                        var groups = g.view.getGroups(); //grouped data
                        if (groups.length > 0) {
                            var ggg = function (grps: Slick.Group<any>[], parentGroupIndex) {
                                var endPosY = doc.autoTableEndPosY();
                                for (let i = 0; i < grps.length; i++) {
                                    var group = grps[i];
                                    var level = group.level + 1;
                                    var headerFontSize = headerFontSizeBase - level;

                                    doc.setFontSize(headerFontSize);

                                    var lineHeight = doc.getLineHeight();//2 is for padding

                                    if (endPosY < doc.autoTableEndPosY())
                                        endPosY = doc.autoTableEndPosY();

                                    if (i == 0 && parentGroupIndex == -1) { //for root levels first group
                                        endPosY += headerHeight + lineHeight * level;
                                    } else if (i == 0 && parentGroupIndex == 0) {
                                        endPosY += headerHeight + lineHeight * level * 1.5;
                                    } else if (i > 0 && parentGroupIndex == -1) {
                                        endPosY += 5;
                                    }
                                    else if (i > 0 && parentGroupIndex == 0) {
                                        endPosY += lineHeight;
                                    }
                                    else {
                                        endPosY += lineHeight * level * 1.5;
                                    }





                                    doc.autoTableText(group.title, 30 + level * 10, endPosY,
                                        { halign: 'left' });




                                    //console.log(i + ' - ' + level + ' - ' +endPosY + ' - ' + group.title);

                                    if (group.groups) {

                                        ggg(group.groups, i);

                                    } else {

                                        let data = toAutoTableData(group.rows, keys, srcColumns);
                                        autoOptions.startY = endPosY + lineHeight * 1.3;
                                        autoOptions.margin.left = 30 + level * 10;
                                        doc.autoTable(columns, data, autoOptions);
                                    }
                                }
                            }

                            ggg(groups, -1);

                        } else {
                            let data = toAutoTableData(g.getItems(), keys, srcColumns);
                            autoOptions.startY = headerHeight;
                            doc.autoTable(columns, data, autoOptions);
                        }
                    }

                    if (typeof doc.putTotalPages === 'function') {
                        doc.putTotalPages(totalPagesExp);
                    }


                    if (!options.output || options.output == "file") {
                        var fileName = options.reportTitle || "{0}_{1}.pdf";
                        fileName = Q.format(fileName, g.getTitle() || "report",
                            Q.formatDate(new Date(), "yyyyMMdd_HHmm"));
                        doc.save(fileName);
                        return;
                    }

                    if (options.autoPrint)
                        doc.autoPrint();

                    var output = options.output;
                    if (output == 'newwindow' || '_blank')
                        output = 'dataurlnewwindow';
                    else if (output == 'window')
                        output = 'datauri';

                    doc.output(output);
                }
            });
        }

        export function createToolButton(options: PdfExportOptions) {

            return <Serenity.ToolButton>{
                title: options.title || '',
                hint: options.hint || 'PDF',
                cssClass: 'export-pdf-button',
                onClick: () => exportToPdf(options),
                separator: options.separator
            };
        }

        function includeJsPDF() {
            if (typeof jsPDF !== "undefined")
                return;

            var script = $("jsPDFScript");
            if (script.length > 0)
                return;

            $("<script/>")
                .attr("type", "text/javascript")
                .attr("id", "jsPDFScript")
                .attr("src", Q.resolveUrl("~/Scripts/jspdf.min.js"))
                .appendTo(document.head);
        }

        function includeAutoTable() {
            includeJsPDF();

            if (typeof jsPDF === "undefined" ||
                typeof (jsPDF as any).API == "undefined" ||
                typeof (jsPDF as any).API.autoTable !== "undefined")
                return;

            var script = $("jsPDFAutoTableScript");
            if (script.length > 0)
                return;

            $("<script/>")
                .attr("type", "text/javascript")
                .attr("id", "jsPDFAutoTableScript")
                .attr("src", Q.resolveUrl("~/Scripts/jspdf.plugin.autotable.min.js"))
                .appendTo(document.head);
        }
    }
}

declare namespace Slick {
    interface RemoteView<TEntity> {
        getGroups(): Slick.Group<TEntity>[];
        getGrouping(): Slick.GroupInfo<TEntity>[];
    }
}

Thank you for share!

Hi dfaruque,
Is this automatic once i have a group in my grid?
BTW, i tried to use the code but i received two errors.

  1. PdfExportHelper.ts(194,47): warning TS2349: Cannot invoke an expression whose type lacks a call signature.
  2. PdfExportHelper.ts(235,45): warning TS2345: Argument of type 'Group' is not assignable to parameter of type 'Group[]'.

Can you have a step by step of how to do this. Thanks!

This ts file seems to be some other version and therefore i am also receiving same errors as being experienced by @alex.

the current version being used is as under:

namespace SereneSample.Common {
export interface PdfExportOptions {
grid: Serenity.DataGrid;
onViewSubmit: () => boolean;
title?: string;
hint?: string;
separator?: boolean;
reportTitle?: string;
titleTop?: number;
titleFontSize?: number;
fileName?: string;
pageNumbers?: boolean;
columnTitles?: { [key: string]: string };
tableOptions?: jsPDF.AutoTableOptions
}

export namespace PdfExportHelper {

    function toAutoTableColumns(srcColumns: Slick.Column[], columnStyles: { [dataKey: string]: jsPDF.AutoTableStyles; },
            columnTitles: { [key: string]: string }) {
        return srcColumns.map(src => {
            let col: jsPDF.AutoTableColumn = {
                dataKey: src.id || src.field,
                title: src.name || ''
            };

            if (columnTitles && columnTitles[col.dataKey] != null)
                col.title = columnTitles[col.dataKey];

            let style: jsPDF.AutoTableStyles = {};
            if ((src.cssClass || '').indexOf("align-right") >= 0)
                style.halign = 'right';
            else if ((src.cssClass || '').indexOf("align-center") >= 0)
                style.halign = 'center';

            columnStyles[col.dataKey] = style;

            return col;
        });
    }

    function toAutoTableData(entities: any[], keys: string[], srcColumns: Slick.Column[] ) {
        let el = document.createElement('span');
        let row = 0;
        return entities.map(item => {
            let dst = {};
            for (let cell = 0; cell < srcColumns.length; cell++) {
                let src = srcColumns[cell];
                let fld = src.field || '';
                let key = keys[cell];
                let txt;
                let html: string;
                if (src.formatter) {
                    html = src.formatter(row, cell, item[fld], src, item);
                }
                else if (src.format) {
                    html = src.format({ row: row, cell: cell, item: item, value: item[fld] });
                }
                else {
                    dst[key] = item[fld];
                    continue;
                }

                if (!html || (html.indexOf('<') < 0 && html.indexOf('&') < 0))
                    dst[key] = html;
                else {
                    el.innerHTML = html;
                    if (el.children.length == 1 &&
                        $(el.children[0]).is(":input")) {
                        dst[key] = $(el.children[0]).val();
                    }
                    else if (el.children.length == 1 &&
                        $(el.children).is('.check-box')) {
                        dst[key] = $(el.children).hasClass("checked") ? "X" : ""
                    }
                    else
                        dst[key] = el.textContent || '';
                }
            }
            row++;
            return dst;
        });
    }

    export function exportToPdf(options: PdfExportOptions): void {

        var g = options.grid;

        if (!options.onViewSubmit())
            return;

        includeAutoTable();

        var request = Q.deepClone(g.view.params) as Serenity.ListRequest;
        request.Take = 0;
        request.Skip = 0;

        var sortBy = g.view.sortBy;
        if (sortBy != null)
            request.Sort = sortBy;

        var gridColumns = g.slickGrid.getColumns();
        gridColumns = gridColumns.filter(x => x.id !== "__select__");

        request.IncludeColumns = [];
        for (var column of gridColumns)
            request.IncludeColumns.push(column.id || column.field);

        Q.serviceCall({
            url: g.view.url,
            request: request,
            onSuccess: response => {
                let doc = new jsPDF('l', 'pt');
                let srcColumns = gridColumns;
                let columnStyles: { [dataKey: string]: jsPDF.AutoTableStyles; } = {};
                let columns = toAutoTableColumns(srcColumns, columnStyles, options.columnTitles);
                var keys = columns.map(x => x.dataKey);
                let entities = (<Serenity.ListResponse<any>>response).Entities || [];
                let data = toAutoTableData(entities, keys, srcColumns);

                doc.setFontSize(options.titleFontSize || 10);
                doc.setFontStyle('bold');
                let reportTitle = options.reportTitle || g.getTitle() || "Report";

                doc.autoTableText(reportTitle, doc.internal.pageSize.width / 2,
                    options.titleTop || 25, { halign: 'center' });

                var totalPagesExp = "{{T}}";

                let pageNumbers = options.pageNumbers == null || options.pageNumbers;
                var autoOptions = $.extend({
                    margin: { top: 25, left: 25, right: 25, bottom: pageNumbers ? 25 : 30 },
                    startY: 60,
                    styles: {
                        fontSize: 8,
                        overflow: 'linebreak',
                        cellPadding: 2,
                        valign: 'middle'
                    },
                    columnStyles: columnStyles
                }, options.tableOptions);

                if (pageNumbers) {
                    var footer = function (data) {
                        var str = data.pageCount;
                        // Total page number plugin only available in jspdf v1.0+
                        if (typeof doc.putTotalPages === 'function') {
                            str = str + " / " + totalPagesExp;
                        }
                        doc.autoTableText(str, doc.internal.pageSize.width / 2,
                            doc.internal.pageSize.height - autoOptions.margin.bottom, {
                                halign: 'center'
                            });
                    };
                    autoOptions.afterPageContent = footer;
                }

                doc.autoTable(columns, data, autoOptions);

                if (typeof doc.putTotalPages === 'function') {
                    doc.putTotalPages(totalPagesExp);
                }

                var fileName = options.reportTitle || "{0}_{1}.pdf";
                fileName = Q.format(fileName, g.getTitle() || "report",
                    Q.formatDate(new Date(), "yyyyMMdd_HHmm"));

                doc.save(fileName);
            }
        }); 
    }

    export function createToolButton(options: PdfExportOptions) {

        return <Serenity.ToolButton>{
            title: options.title || '',
            hint: options.hint || 'PDF',
            cssClass: 'export-pdf-button',
            onClick: () => exportToPdf(options),
            separator: options.separator
        };
    }

    function includeJsPDF() {
        if (typeof jsPDF !== "undefined")
            return;

        var script = $("jsPDFScript");
        if (script.length > 0)
            return;

        $("<script/>")
            .attr("type", "text/javascript")
            .attr("id", "jsPDFScript")
            .attr("src", Q.resolveUrl("~/Scripts/jspdf.min.js"))
            .appendTo(document.head);
    }

    function includeAutoTable() {
        includeJsPDF();

        if (typeof jsPDF === "undefined" ||
            typeof (jsPDF as any).API == "undefined" ||
            typeof (jsPDF as any).API.autoTable !== "undefined")
            return;

        var script = $("jsPDFAutoTableScript");
        if (script.length > 0)
            return;

        $("<script/>")
            .attr("type", "text/javascript")
            .attr("id", "jsPDFAutoTableScript")
            .attr("src", Q.resolveUrl("~/Scripts/jspdf.plugin.autotable.min.js"))
            .appendTo(document.head);
    }
}

}

sorry.
To avoid these errors you have do some hack

In jspdf.autotable.d.ts Replace
autoTableEndPosY?: number;
with
autoTableEndPosY(): number;

in Serenity.CoreLib.d.ts
replace this

class Group<TEntity> {
        isGroup: boolean;
        level: number;
        count: number;
        value: any;
        title: string;
        collapsed: boolean;
        totals: any;
        rows: any;
        groups: Group<TEntity>;
        groupingKey: string;
    }

with

class Group<TEntity> {
        isGroup: boolean;
        level: number;
        count: number;
        value: any;
        title: string;
        collapsed: boolean;
        totals: any;
        rows: any;
        groups: Group<TEntity>[];
        groupingKey: string;
    }

It is in initial stage. I will send a pull request later.

Thanks @dfaruque.

done the changes suggested by you, but it does not works for rows which do not have any identity column. can you please suggest what can be done in this case.

@dfaruque
Thank you for the code you shared, I m using viewwithoutID when I'm using default serenity PDF export code the data gets exported but I cannot get the grouping summary when I tried to use your PDF export code from _Ext module I get an error as below.

Uncaught Each data element must implement a unique '__id' property. Object at index '0' has no identity value:

@vinaykulkarni89 I know your post is old but I thought to add some useful information for you and others regarding using a view.

One way to get your primary key field (Unique Id ) for a view is to use Row_Number() OVER (Partition By SomeUniqueField Order By SomeField) As PkId

Was this page helpful?
0 / 5 - 0 ratings

Related issues

stixoffire picture stixoffire  路  3Comments

StefanTheiner picture StefanTheiner  路  3Comments

kilroyFR picture kilroyFR  路  3Comments

ga5tan picture ga5tan  路  3Comments

Akarsh03 picture Akarsh03  路  3Comments