[Kendo UI] Funcionamiento del Grid – Parte 5 – Exportación Excel/PDF

Vamos a utilizar el control Kendo-UI Grid de Telerik para configurar la exportación de su contenido a PDF o Excel.

Vamos a usar la plantilla Kendo UI ASP.NET MVC 5 Application junto con el framework AngularJS 1.
En el siguiente enlace podéis descargar el proyecto del servicio web OData que usaremos para este artículo.

Una vez descargado el proyecto, hay que ejecutarlo para tomar nota de la url. En nuestro caso es http://localhost:49952/

A continuación, vamos a crear el proyecto siguiendo los siguientes pasos:

  • Creamos un nuevo proyecto usando la plantilla de KendoUI y AngularJS
  • Comprobamos si tenemos en .js asociado a nuestra cultura. Para ello buscamos el archivo “kendo.culture.es-ES.min.js” en la carpeta /Scripts/kendo/2016.2.607/ o dentro del subcarpeta “cultures” . Si no lo encontramos, vamos a añadirlo copiándolo de la carpeta de instalación de Kendo (por defecto, C:\Program Files (x86)\Telerik\Kendo UI Professional Q2 2016\js\cultures) a la carpeta de nuestro proyecto /Scripts/kendo/2016.2.607.
    Nota: en nuestro caso no lo teníamos, por lo que hemos tenido que copiarlo a mano.
  • Creamos el controlador “HomeController”, añadimos un método de acción llamado “index” y, por último, creamos su vista.
    Nota: a la hora de crear la vista, hay que marcar la opción “Usar página de diseño”, para que se genere el _Layout.cshtml predeterminado.
  • Modificamos el fichero “/App_Start/BundleConfig.cs”, para añadir los bundles de angular, kendo y otro para la exportación.
    using System.Web;
    using System.Web.Optimization;
    
    namespace GridExportacion
    {
        public class BundleConfig
        {
            // For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862
            public static void RegisterBundles(BundleCollection bundles)
            {
                bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                            "~/Scripts/jquery-{version}.js"));
    
                // Use the development version of Modernizr to develop with and learn from. Then, when you're
                // ready for production, use the build tool at http://modernizr.com to pick only the tests you need.
                bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
                            "~/Scripts/modernizr-*"));
    
                bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
                          "~/Scripts/bootstrap.js",
                          "~/Scripts/respond.js"));
    
                bundles.Add(new StyleBundle("~/Content/css").Include(
                          "~/Content/bootstrap.css",
                          "~/Content/site.css"));
    
                //angular
                bundles.Add(new ScriptBundle("~/bundles/angular").Include(
                          "~/Scripts/kendo/2016.2.607/angular.min.js"));
    
                //kendo
                bundles.Add(new ScriptBundle("~/bundles/kendo").Include(
                          "~/Scripts/kendo/2016.2.607/kendo.all.min.js",
                          "~/Scripts/kendo/2016.2.607/kendo.culture.es-ES.min.js"));
    
                bundles.Add(new StyleBundle("~/Content/kendocss").Include(
                          "~/Content/kendo/2016.2.607/kendo.common.min.css",
                          "~/Content/kendo/2016.2.607/kendo.rtl.min.css",
                          "~/Content/kendo/2016.2.607/kendo.silver.min.css",
                          "~/Content/kendo/2016.2.607/kendo.mobile.all.min.css"));
    
                //exportacion
                bundles.Add(new ScriptBundle("~/bundles/kendoExportacion").Include(
                          "~/Scripts/kendo/2016.2.607/pako_deflate.min.js", 
                          "~/Scripts/kendo/2016.2.607/jszip.min.js"));        }
        }
    }
    

    Observa como hemos creado un bundle con las librerías de exportación:

    A continuación, vamos a modificar el fichero “/Views/Shared/_Layout.cshtml”

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>@ViewBag.Title - My ASP.NET Application</title>
        @Styles.Render("~/Content/css")
        @Styles.Render("~/Content/kendocss")
        @Scripts.Render("~/bundles/modernizr")
        @Scripts.Render("~/bundles/jquery")
        @Scripts.Render("~/bundles/bootstrap")
        @Scripts.Render("~/bundles/angular")
        @Scripts.Render("~/bundles/kendo")
        @Scripts.Render("~/bundles/kendoExportacion")
    </head>
    <body>
        <div class="navbar navbar-inverse navbar-fixed-top">
            <div class="container">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>
                    @Html.ActionLink("Application name", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
                </div>
                <div class="navbar-collapse collapse">
                    <ul class="nav navbar-nav"></ul>
                </div>
            </div>
        </div>
    
        <div class="container body-content">
            @RenderBody()
            <hr />
            <footer>
                <p>&copy; @DateTime.Now.Year - Kendo UI Grid Exportacion</p>
            </footer>
        </div>
    
        @RenderSection("scripts", required: false)
    </body>
    </html>
    
  • Por último, vamos a preparar nuestra vista “Index” para trabajar con Angular. Para ello creamos un módulo (appGridExportacion) y controlador (ctrlGridExportacion) Angular
    @{
        ViewBag.Title = "Index";
    }
    <div ng-app="appGridExportacion" ng-controller="ctrlGridExportacion">
    </div>
    <script>
        angular.module("appGridExportacion", ["kendo.directives"])
                .controller("ctrlGridExportacion", function ($scope, $http) {
                }).run(function () {
                    kendo.culture("es-ES");
                })
    </script> 
    

En este punto, ya tenemos nuestro proyecto preparado pasa usar Kendo, Angular y el servicio web OData.

A continuación, vamos a mostrar un sencillo ejemplo de un Grid editable de clientes, el cual esta enlazado a un origen de datos OData-V4.

@{
    ViewBag.Title = "Index";
}
<div ng-app="appGridExportacion" ng-controller="ctrlGridExportacion">
    <div class="panel panel-primary">
        <div class="panel-heading">
            <h3 class="panel-title">Grid Kendo UI - Exportacion</h3>
        </div>
        <div class="panel-body">
            <div kendo-grid="GridExportacionPDF" k-options="opcionesGridExportacionPDF">
            </div>
        </div>
    </div>
</div>

<script>
    angular.module("appGridExportacion", ["kendo.directives"])
            .controller("ctrlGridExportacion", function ($scope, $http) {

                $scope.origenDatosClientes = new kendo.data.DataSource({
                    type: "odata-v4",
                    transport: {
                        read: {
                            url: "http://localhost:49952/Clientes?$expand=TipoCliente",
                            dataType: "json"
                        },
                        create: {
                            type: "POST",
                            url: "http://localhost:49952/Clientes",
                            contentType: "application/json",
                            dataType: "json",
                            complete: function (e) {
                                $scope.recuperarClientes();
                            }
                        },
                        update: {
                            url: function (data) {
                                return "http://localhost:49952/Clientes" + "(" + data.Id + ")";
                            },
                            type: "PUT",
                            contentType: "application/json",
                            dataType: "json"
                        },
                        destroy: {
                            url: function (data) {
                                return "http://localhost:49952/Clientes" + "(" + data.Id + ")";
                            },
                            dataType: "json",
                            type: "DELETE"
                        }
                    },
                    schema: {
                        data: function (data) {
                            return data.value;
                        },
                        total: function (data) {
                            return data['@@odata.count'];
                        },
                        model: {
                            id: "Id",
                            fields: {
                                Id: { editable: false, type: "number" },
                                Nombre: { type: "string" },
                                Mail: { type: "string" },
                                Telefono: { type: "string" },
                                Direccion: { type: "string" },
                                TipoClienteId: { type: "number" },
                                TipoCliente: {
                                    defaultValue: { Id: 0, Nombre: "" },
                                    fields: {
                                        Id: { type: "number" },
                                        Nombre: { type: "string" }
                                    }
                                }
                            }
                        }
                    },
                    pageSize: 5,
                    serverPaging: true,
                    serverFiltering: true,
                    serverSorting: true,
                    error: function (e) {
                        alert("Status: " + e.status + "; Error message: " + e.errorThrown);
                    }
                });

                $scope.opcionesComboTipoCliente = {
                    dataSource: new kendo.data.DataSource({
                        type: "odata-v4",
                        transport: {
                            read: {
                                url: "http://localhost:49952/TiposCliente",
                            }
                        }
                    }),
                    dataTextField: "Nombre",
                    dataValueField: "Id"
                }
                $scope.comboBoxTipoCliente = function (container, options) {
                    var editor = $('<select kendo-combo-box k-options="opcionesComboTipoCliente" data-bind="value:' + options.field + '"/>')
                    .appendTo(container);
                }
                $scope.opcionesGridExportacionPDF = {
                    groupable: {
                        messages: {
                            empty: "Para agrupar el listado, arrastre la cabecera de la columna aquí"
                        }
                    },
                    filterable: true,
                    pageable: {
                        pageSize: 5,
                        previousNext: true,
                        numeric: true,
                        buttonCount: 5,
                        input: true,
                        pageSizes: [5, 10, 15, 20, 30, "all"],
                        refresh: true,
                        info: true,
                        messages: {
                            display: "Mostrando {0}-{1} de {2} clientes",
                            empty: "No hay datos de clientes",
                            page: "Introduzca Página",
                            of: " de {0}",
                            itemsPerPage: "registros por página",
                            first: "Primera",
                            last: "Última",
                            next: "Siguiente",
                            previous: "Anterior",
                            refresh: "Actualizar",
                            morePages: "Más paginas"
                        }
                    },
                    sortable: {
                        allowUnsort: true,
                        mode: "multiple"
                    },
                    columnMenu: {
                        columns: true,
                        filterable: true,
                        sortable: true
                    },
                    columns: [
                        { field: "Id", title: "Identificador" },
                        { field: "Nombre", title: "Nombre Completo" },
                        { field: "Mail", title: "e-Mail" },
                        { field: "Telefono", title: "Telefono" },
                        { field: "Direccion", title: "Dirección" },
                        { field: "TipoClienteId", title: "Tipo", editor: $scope.comboBoxTipoCliente, template: "#=TipoCliente.Nombre#" },
                        {
                            command: [
                                {
                                    name: "edit",
                                    text: { edit: "Editar", cancel: "Cancelar Edición", update: "Grabar Cambios" }
                                },
                                {
                                    name: "destroy",
                                    text: "Borrar"
                                }
                            ],
                            title: "&nbsp;",
                            width: "250px"
                        }
                    ],
                    dataSource: $scope.origenDatosClientes,
                    editable: {
                        createAt: "bottom",
                        mode: "inline"
                    },
                    pdf: {
                        allPages: true
                    },
                    toolbar: [
                        {
                            name: "create",
                            text: "Nuevo Cliente"
                        },
                        {
                            name: "pdf",
                            text: "Exportar a PDF"
                        }
                    ]
                };

                $scope.recuperarClientes = function () {
                    $scope.opcionesGridGuardarEstado.dataSource.read();
                };

            }).run(function () {
                kendo.culture("es-ES");
            })
</script>

En el fragmento de código anterior, se realizan 2 operaciones para configurar la exportación:

  • Hemos usado la propiedad “pdf.allPages” de las opciones de configuración del grid, para exportar todas las páginas del grid.
  • Hemos añadido el botón “pdf” a la toolbar. Nota: este botón es uno de los botones de opción pre-configurados por telerik.

En un artículo anterior, ya explicamos en detalle las opciones relacionadas con la edición y la configuración del DataSource sobre ODataV4.

En la siguiente imagen se puede ver el aspecto del grid

ExportacionPdfAllPages

Al pulsar sobre el botón “Exportar a PDF”, se genera un PDF con el nombre Export.

Si cambiamos el valor de “allPages” a false y volvemos a ejecutarlo, entonces el proceso de exportación solo tiene en cuenta la página actual.
Además, hay que tener en cuenta que la creación del PDF se realiza en la parte cliente. Las peticiones al servidor se realizan únicamente para recuperar datos. De hecho, si volvemos a establecer “allPages” como true, se hace una petición al servidor por cada página. En la siguiente imagen se puede ver una captura de las peticiones http.

CapturaPeticionesRedChrome

En la imagen anterior se pueden ver 3 peticiones de datos paginadas (usando los parámetros skip y top de OData) y una petición para la fuente “DejavuSans.ttf” (usada como fuente en la creación del PDF).

El proceso de exportación, también se puede iniciar por código, invocando al método “saveAsPDF” del objeto del Grid Kendo UI.

    <div class="panel panel-primary">
        <div class="panel-heading">
            <h3 class="panel-title">Grid Kendo UI - Exportacion Manual</h3>
        </div>
        <div class="panel-body">
            <div class="box wide">
                <a href="#" class="k-button" id="btnExportacionManualPdf" ng-click="exportacionPdf()">Exportación Manual PDF</a>
            </div>
        </div>
    </div>
    …
    $scope.exportacionPdf = function () {
        $scope.GridExportacionPDF.saveAsPDF();
    };

ExportacionPdfAllPagesManual

A continuación, vamos a ver las principales opciones de configuración:

  • autor: permite configurar el nombre del autor del PDF.
  • creator: determina el nombre de la aplicación que genero del PDF. El valor por defecto es “Kendo UI PDF Generator”
  • subject: permite configurar el asunto del PDF.
  • title: permite configurar el título del PDF.
  • avoidLinks: permite configurar el comportamiento de los enlaces http. Si queremos mantener los enlaces http debemos establecer el valor a false.
  • date: determina la fecha de creación del PDF. El valor por defecto de “new Date()”
  • fileName: nos permite especificar el nombre del fichero PDF.
  • landspace: determina si la exportación es en formato vertical o apaisado.
  • margin: determina los márgenes del PDF. Sus valores se puede establecer el formato “mm”, “cm”, “in” y “pt”. El valor se puede personalizar para “left”, “top”, “right” y “bottom”. El valor por defecto es 0.
  • paperSize: especifica el tamaño del papel. Hay 2 opciones para configurarlo:
    • Usar los valores predefinidos como “A4”, “A3”
    • Usar un array con 2 valores que especifican el ancho y el alto.

          paperSize: ["70mm", "70mm"]
      

    Nota: Si no es especifica un valor para “paperSize”, el Grid calcula de forma automática el tamaño en base a las dimensiones del Grid.

A continuación, se muestra un fragmento de código con las principales opciones para la exportación del PDF

                    pdf: {
                        allPages: true,
                        author: "ProgrammingApps",
                        avoidLinks: false,
                        creator: "Kendo Grid ProgrammingApps",
                        subject: "Listado de Clientes",
                        title: "Listado de Clientes",
                        date: new Date("2016/06/18"),
                        fileName: "Clientes.pdf",
                        landscape: true,
                        margin: {
                            left: "10mm",
                            right: "10mm",
                            top: "10mm",
                            bottom: "10mm"
                        },
                        paperSize: "A4"
                    },

Si ahora volvemos a exportar el PDF, el resultado lo podemos ver en Clientes.

Por último, vamos a configurar la exportación a Excel. El proceso es muy similar al del PDF:

  • Añadir la opción “Excel” a la toolbar.
  • Configurar el objeto “Excel” de las opciones de configuración del grid.
    Nota: La opción “filterable”, hace que el Excel generado incluya autofiltros en cada columna
                    excel: {
                        allPages: true,
                        fileName: "Clientes.xlsx",
                        filterable: true,
                    },
                    toolbar: [
                        {
                            name: "create",
                            text: "Nuevo Cliente"
                        },
                        {
                            name: "excel",
                            text: "Exportar a Excel"
                        }
                    ]

ExportacionExcel

Nota: al igual que ocurría con la exportación a PDF, la exportación a Excel también es realizada en la parte cliente.

Otra forma de exportación a Excel, consiste en copiar las celdas directamente del Grid y pegándolas en un Excel. Para ello hay que configurar el parámetro “selectable” como “multiple cell” y “allowCopy” como true.

                    excel: {
                        allPages: true,
                        fileName: "Clientes.xlsx",
                        filterable: true,
                    },
                    selectable: "multiple cell",
                    allowCopy: true,
                    toolbar: [
                        {
                            name: "create",
                            text: "Nuevo Cliente"
                        },
                        {
                            name: "excel",
                            text: "Exportar a Excel"
                        }
                    ] 

Para seleccionar múltiples celdas, basta con mantener pulsado la tecla “CTRL” y hacer clic sobre ellas. Por último, para copiar las celdas hay que usar la combinación “CTRL + C”.
En la siguiente imagen se puede ver el aspecto del grid con múltiples celdas seleccionadas.

ExportacionExcelCopiar

En el siguiente enlace se puede ver el excel generado “Clientes”.

os proyectos de ejemplo usados para este articulo son: