ExtJS 4: Grid Printer Plugin

September 7, 2011 | By

Ed Spencer created a plugin that is capable of creating a print version of an ExtJS grid. This plugin was originally created for ExtJS 3.x. I ported it to ExtJS 4, in case someone need it.

The plugin can be downloaded on the following link: https://github.com/loiane/extjs4-ux-gridprinter/archives/master

The zip file contains the source code of the plugin plus an example of how to use it. To run the example page, simply open it on any browser (you will need an internet connection).

To use the plugin in your project, copy the ux folder and paste it on your project, as the following sample project I created:

extjs4 ux gridprinter loiane 03 ExtJS 4: Grid Printer PluginPlugin Code:

/**
 * @class Ext.ux.grid.Printer
 * @author Ed Spencer (edward@domine.co.uk)
 * Helper class to easily print the contents of a grid. Will open a new window with a table where the first row
 * contains the headings from your column model, and with a row for each item in your grid's store. When formatted
 * with appropriate CSS it should look very similar to a default grid. If renderers are specified in your column
 * model, they will be used in creating the table. Override headerTpl and bodyTpl to change how the markup is generated
 * 
 * Usage:
 * 
 * 1 - Add Ext.Require Before the Grid code
 * Ext.require([
 *   'Ext.ux.grid.GridPrinter',
 * ]);
 * 
 * 2 - Declare the Grid 
 * var grid = Ext.create('Ext.grid.Panel', {
 *   columns: //some column model,
 *   store   : //some store
 * });
 * 
 * 3 - Print!
 * Ext.ux.grid.Printer.print(grid);
 * 
 * Original url: http://edspencer.net/2009/07/printing-grids-with-ext-js.html
 * 
 * Modified by Loiane Groner (me@loiane.com) - September 2011 - Ported to Ext JS 4
 * http://loianegroner.com (English)
 * http://loiane.com (Portuguese)
 */
Ext.define("Ext.ux.grid.Printer", {
	
	requires: 'Ext.XTemplate',

	statics: {
		/**
		 * Prints the passed grid. Reflects on the grid's column model to build a table, and fills it using the store
		 * @param {Ext.grid.Panel} grid The grid to print
		 */
		print: function(grid) {
			//We generate an XTemplate here by using 2 intermediary XTemplates - one to create the header,
			//the other to create the body (see the escaped {} below)
			var columns = grid.columns;

			//build a useable array of store data for the XTemplate
			var data = [];
			grid.store.data.each(function(item) {
				var convertedData = [];

				//apply renderers from column model
				for (var key in item.data) {
					var value = item.data[key];

					Ext.each(columns, function(column) {
						if (column.dataIndex == key) {
							convertedData[key] = column.renderer ? column.renderer(value) : value;
						}
					}, this);
				}

				data.push(convertedData);
			});

			//use the headerTpl and bodyTpl markups to create the main XTemplate below
			var headings = Ext.create('Ext.XTemplate', this.headerTpl).apply(columns);
			var body     = Ext.create('Ext.XTemplate', this.bodyTpl).apply(columns);
			
			var htmlMarkup = [
				'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
				'<html>',
				  '<head>',
				    '<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />',
				    '<link href="' + this.stylesheetPath + '" rel="stylesheet" type="text/css" media="screen,print" />',
				    '<title>' + grid.title + '</title>',
				  '</head>',
				  '<body>',
				    '<table>',
				      headings,
				      '<tpl for=".">',
				        body,
				      '</tpl>',
				    '</table>',
				  '</body>',
				'</html>'           
			];

			var html = Ext.create('Ext.XTemplate', htmlMarkup).apply(data); 

			//open up a new printing window, write to it, print it and close
			var win = window.open('', 'printgrid');

			win.document.write(html);

			if (this.printAutomatically){
				win.print();
				win.close();
			}
		},

		/**
		 * @property stylesheetPath
		 * @type String
		 * The path at which the print stylesheet can be found (defaults to 'ux/grid/gridPrinterCss/print.css')
		 */
		stylesheetPath: 'ux/grid/gridPrinterCss/print.css',
		
		/**
		 * @property printAutomatically
		 * @type Boolean
		 * True to open the print dialog automatically and close the window after printing. False to simply open the print version
		 * of the grid (defaults to true)
		 */
		printAutomatically: true,
		
		/**
		 * @property headerTpl
		 * @type {Object/Array} values
		 * The markup used to create the headings row. By default this just uses <th> elements, override to provide your own
		 */
		headerTpl: [ 
			'<tr>',
				'<tpl for=".">',
					'<th>{text}</th>',
				'</tpl>',
			'</tr>'
		],

		/**
		 * @property bodyTpl
		 * @type {Object/Array} values
		 * The XTemplate used to create each row. This is used inside the 'print' function to build another XTemplate, to which the data
		 * are then applied (see the escaped dataIndex attribute here - this ends up as "{dataIndex}")
		 */
		bodyTpl: [
			'<tr>',
				'<tpl for=".">',
					'<td>\{{dataIndex}\}</td>',
				'</tpl>',
			'</tr>'
		]
	}
});

Usage:

To use the plugin you can create a button and in the handler function you can call the static function of the plugin (you need to pass a grid instance as argument to the print function).

Ext.Loader.setConfig({enabled: true});

Ext.require([
    'Ext.grid.*',
    'Ext.data.*',
    'Ext.ux.grid.Printer',
]);

Ext.onReady(function() {

    // sample static data for the store
    var myData = [
        ['3m Co',                               71.72, 0.02,  0.03,  '9/1 12:00am'],
        ['Alcoa Inc',                           29.01, 0.42,  1.47,  '9/1 12:00am'],
        ['Altria Group Inc',                    83.81, 0.28,  0.34,  '9/1 12:00am'],
        ['American Express Company',            52.55, 0.01,  0.02,  '9/1 12:00am'],
        ['American International Group, Inc.',  64.13, 0.31,  0.49,  '9/1 12:00am'],
        ['AT&T Inc.',                           31.61, -0.48, -1.54, '9/1 12:00am'],
        ['Boeing Co.',                          75.43, 0.53,  0.71,  '9/1 12:00am'],
        ['Caterpillar Inc.',                    67.27, 0.92,  1.39,  '9/1 12:00am'],
        ['Citigroup, Inc.',                     49.37, 0.02,  0.04,  '9/1 12:00am'],
        ['E.I. du Pont de Nemours and Company', 40.48, 0.51,  1.28,  '9/1 12:00am'],
        ['Exxon Mobil Corp',                    68.1,  -0.43, -0.64, '9/1 12:00am'],
        ['General Electric Company',            34.14, -0.08, -0.23, '9/1 12:00am'],
        ['General Motors Corporation',          30.27, 1.09,  3.74,  '9/1 12:00am'],
        ['Hewlett-Packard Co.',                 36.53, -0.03, -0.08, '9/1 12:00am'],
        ['Honeywell Intl Inc',                  38.77, 0.05,  0.13,  '9/1 12:00am'],
        ['Intel Corporation',                   19.88, 0.31,  1.58,  '9/1 12:00am'],
        ['International Business Machines',     81.41, 0.44,  0.54,  '9/1 12:00am'],
        ['Johnson & Johnson',                   64.72, 0.06,  0.09,  '9/1 12:00am'],
        ['JP Morgan & Chase & Co',              45.73, 0.07,  0.15,  '9/1 12:00am'],
        ['McDonalds Corporation',               36.76, 0.86,  2.40,  '9/1 12:00am'],
        ['Merck & Co., Inc.',                   40.96, 0.41,  1.01,  '9/1 12:00am'],
        ['Microsoft Corporation',               25.84, 0.14,  0.54,  '9/1 12:00am'],
        ['Pfizer Inc',                          27.96, 0.4,   1.45,  '9/1 12:00am'],
        ['The Coca-Cola Company',               45.07, 0.26,  0.58,  '9/1 12:00am'],
        ['The Home Depot, Inc.',                34.64, 0.35,  1.02,  '9/1 12:00am'],
        ['The Procter & Gamble Company',        61.91, 0.01,  0.02,  '9/1 12:00am'],
        ['United Technologies Corporation',     63.26, 0.55,  0.88,  '9/1 12:00am'],
        ['Verizon Communications',              35.57, 0.39,  1.11,  '9/1 12:00am'],
        ['Wal-Mart Stores, Inc.',               45.45, 0.73,  1.63,  '9/1 12:00am']
    ];

    /**
     * Custom function used for column renderer
     * @param {Object} val
     */
    function change(val) {
        if (val > 0) {
            return '<span style="color:green;">' + val + '</span>';
        } else if (val < 0) {
            return '<span style="color:red;">' + val + '</span>';
        }
        return val;
    }

    /**
     * Custom function used for column renderer
     * @param {Object} val
     */
    function pctChange(val) {
        if (val > 0) {
            return '<span style="color:green;">' + val + '%</span>';
        } else if (val < 0) {
            return '<span style="color:red;">' + val + '%</span>';
        }
        return val;
    }

    // create the data store
    var store = Ext.create('Ext.data.ArrayStore', {
        fields: [
           {name: 'company'},
           {name: 'price',      type: 'float'},
           {name: 'change',     type: 'float'},
           {name: 'pctChange',  type: 'float'},
           {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
        ],
        data: myData
    });

    // create the Grid
    var grid = Ext.create('Ext.grid.Panel', {
        store: store,
        stateful: true,
        stateId: 'stateGrid',
        columns: [
            {
                text     : 'Company',
                flex     : 1,
                sortable : false,
                dataIndex: 'company'
            },
            {
                text     : 'Price',
                width    : 75,
                sortable : true,
                renderer : 'usMoney',
                dataIndex: 'price'
            },
            {
                text     : 'Change',
                width    : 75,
                sortable : true,
                renderer : change,
                dataIndex: 'change'
            },
            {
                text     : '% Change',
                width    : 75,
                sortable : true,
                renderer : pctChange,
                dataIndex: 'pctChange'
            },
            {
                text     : 'Last Updated',
                width    : 85,
                sortable : true,
                renderer : Ext.util.Format.dateRenderer('m/d/Y'),
                dataIndex: 'lastChange'
            }
        ],
        height: 350,
        width: 600,
        title: 'Array Grid with Print Option',
        renderTo: Ext.getBody(),
        tbar: [{
            text: 'Print',
            iconCls: 'icon-print',
            handler : function(){
            	Ext.ux.grid.Printer.printAutomatically = false;
            	Ext.ux.grid.Printer.print(grid);
            }
        }]
    });
});

There are some options you can customize, such as the stylesheet path and the printAutomatically.

And when we execute the code above, we will get a grid with a print button like the following:

extjs4 ux gridprinter loiane 01 ExtJS 4: Grid Printer Plugin

And when we click on the Print button, a new page will be opened with the following content:

extjs4 ux gridprinter loiane 02 ExtJS 4: Grid Printer Plugin

Demo

Demo: http://loianegroner.com/extjs/examples/extjs4-ux-gridprinter/

Github repository: https://github.com/loiane/extjs4-ux-gridprinter

The original plugin (ExtJS 3.x) – by Ed Spencer:  http://edspencer.net/2009/07/printing-grids-with-ext-js.html

Happy Coding! icon smile ExtJS 4: Grid Printer Plugin

Filed in: Ext JS 4 | Tags: , , , , , , , ,

Comments (12)

  1. Reza

    Fantastic! Thanks
    How we can place a title on top of table?

    • Loiane

      Hi Reza, you can modify the plugin to display a table title.
      You have to change the htmlMarkup variable with the new html content.

  2. Adrian

    Great job Loiane! Thanks!

    I did some changes to your code to make it work with Template columns too. I just replaced the following code:
                    for (var key in item.data) {                    var value = item.data[key];
                        Ext.each(columns, function(column) {                        if (column.dataIndex == key) {                            convertedData[key] = column.renderer ? column.renderer(value) : value;                        }                    }, this);                }
    with this code:
                    Ext.each(columns, function(column){                    var key = column.dataIndex;                    var value = item.get(key);                                        if(Ext.ClassManager.getName(column) == ‘Ext.grid.column.Template’){                        convertedData[key] = column.tpl ? column.tpl.apply(item.data) : value;                    }else{                        convertedData[key] = column.renderer ? column.renderer(value) : value;                                            }                })
    Cheers

  3. kprk

    Hi Loiane, did you get a chance to get working on export functionality? I am facing problem with existing one in ext3 to 4 migration. please let me know if you made changes in it 

    • Loiane

      Hi kprk,

      I did not have time to ported yet, but someone else started the migration to Ext JS 4 on Sencha Forum.
      There are still some bugs, but it working with Ext 4.

  4. Marcos

    Olá, Loiane!
    Qué grande é o seu trabalho!
    Quando estou a desenvolver com este plug-in recebo uma mensagem que me diz que “data” não é uma propriedade válida do undefined…
    Sabe o qué posso estar a fazer mal?
    Obrigadísimo!

  5. Marcos

    Estou a usar o versão 4. Já probei tantas coisas…
    Oxalá puderas ajudá me.
    Obrigadíssimo!

  6. Ankit

    Hi Loiane,
    This is really a good plugin.
    I am running into an issue while using it. I have a grid with pagination but print functionality only shows first page data and not all.
    Is there anything i could do to resolve this?

    • Loiane

      Hi Ankit,
      Unfortunatelly, no. If you are using pagination, the plugin will not work and will not print all your data. The plugin only prints the data that is being displayed on the grid.
      If you want to print all your data, you can create an hidden grid with all your data, but if you have too much information, you may have performance issues.

  7. ineffable

    Best plugin ever for anything.