Ext.ux.Printer – printing for any ExtJS Component
July 28, 2009 by Ed Spencer · 23 Comments
After my recent foray into printing grids with ExtJS, I realised I needed to print some trees too. Seeing as some of the work was already done for the Grid example, it made sense to create a common API for printing any Ext.Component. And thus Ext.ux.Printer was born:
var grid = new Ext.grid.GridPanel({ // just a normal grid });
var tree = new Ext.tree.ColumnTree({ // just a normal column tree });
Ext.ux.Printer.print(grid);
Ext.ux.Printer.print(tree);
Each of the above opens a new window, renders some HTML (just a big table really), prints it and closes the window – all client side with no server side code required. Although trees and grids represent data quite differently internally, we can use the same API on Ext.ux.Printer to print them both.
Ext.ux.Printer uses Renderer classes to cope with a specific xtype, and adding Renderers for other components is easy. At the moment Ext.grid.GridPanel and Ext.tree.ColumnTree are supported out of the box, but let’s see how we’d add support for printing the contents of an Ext.Panel:
/**
* Prints the contents of an Ext.Panel
*/
Ext.ux.Printer.PanelRenderer = Ext.extend(Ext.ux.Printer.BaseRenderer, {
/**
* Generates the HTML fragment that will be rendered inside the <html> element of the printing window
*/
generateBody: function(panel) {
return String.format("<div class='x-panel-print'>{0}</div>", panel.body.dom.innerHTML);
}
});
Ext.ux.Printer.registerRenderer("panel", Ext.ux.Printer.PanelRenderer);
This is probably the simplest print renderer of all – we’re simply grabbing the HTML from inside a the panel’s body and returning it inside our own div. We subclassed Ext.ux.Printer.BaseRenderer, and in this case all we needed to do was provide an implementation for generateBody. Whatever this function returns is rendered inside the <body> tag of the newly-opened printing window.
Notice that we registered this renderer for all components with the xtype of ‘panel’. Internally, Ext.ux.Printer examines the xtype chain of the component you pass it to print, and uses the first renderer that matches. As many Ext components inherit from Ext.Panel this can function as a catch-all renderer.
Here’s how we’d use our new renderer:
var panel = new Ext.Panel({
html: {
tag: 'ul',
chidren: [
{tag: 'li', text: 'Item 1'},
{tag: 'li', text: 'Item 2'},
{tag: 'li', text: 'Item 3'}
]
}
});
Ext.ux.Printer.print(panel);
Pretty straightforward. You can now print Ext.Panels the same way you’d print a Grid or a Tree. Take a look at the Grid Renderer and the ColumnTree Renderer for examples of rendering more advanced components.
As usual, all of the Ext.ux.Printer source is available on Github, and the README file there contains instructions for installation and usage.
Finally, when the printing window is opened it includes a stylesheet that it expects to find at “/stylesheets/print.css”. There is a default print.css stylesheet included with the extension to get you started, and you can specify where to find this stylesheet like this:
Ext.ux.Printer.BaseRenderer.prototype.stylesheetPath = '/path/to/print/stylesheet.css';
Awesome addon, found it easy to implement and use was just curious about grid striping rows I've toyed with it with minimal success
God job, thank you for sharing. But this component not work on ie8 ( May be run ie7, ie6 …). I change the code "print" method on "Base.js" from "String.format("print-{0}-{1}…" to "String.format("print_{0}_{1}…" and it is working now.
Thanks again
Ebabil
Hi Ebabil,
Thanks for the heads up – I've pushed your fix up onto Github now. There's also a Printer-all.js file there now, which includes all the files in the right order for you.
Ed
toying some more with the print addon
if i render a field with a qtip it seems to break it
{ header: "Job", dataIndex: 'job_id', sortable: true, renderer: function(val,cell,record){ if(val != '') cell.attr = 'ext:qtip="'+record.data.tooltip+'" ext:qchilds="true"'; return val; } }
if i remove the cell.attr it works fine
also I modified the grid generateBody function
FROM ,headings,body TO '<thead>'+headings+'</thead>', body
that way at the top of every page it has the grid header
Maybe i'm placing the qtip on there incorrect I'm no ext expert by far.
Ed, this is brilliant!!!
I made a modification to allow for setting the title. I have some panels that the title is non-descriptive, however I needed the printed version to be a little more verbose, just replace the getTitle function with this one, and add this property.
/**
* Returns the title to give to the print window
* @param {Ext.Component} component The component to be printed
* @return {String} The window title
*/
getTitle: function(component) {
if(this.manualTitle){
return this.manualTitle;
}else{
return typeof component.getTitle == ‘function’ ? component.getTitle() : (component.title || “Printing”);
}
this.manualTitle = null;
},
/**
* @property manualTitle
* @type String
* A manualy set titile to override the components
*
*/
manualTitle : null,
Then call it like so:
Ext.ux.Printer.BaseRenderer.prototype.manualTitle = ‘Some descriptive title!!!’;
BTW: Ed, I can not find a email address to you on the site : (
I just started using the component in an internal app. Thank you for sharing.
Ed, this is a terrific extension! Thanks much!
Is there an ambition to extend it for Grouping Store???
Great extension!
Along the lines of Ebabil’s comment, if your component ID has dashes (e.g. my-grid) IE will throw an error on the call to window.open. Here is the quick fix I made:
String.format(”print_{0}_{1}”, component.getXType(), component.id.replace(/-/g,”_”))
I also threw in a few ‘\n’ characters in the HTML templates to make the output easier to read.
Hi,
if i want print ext.window?
Thank’s you help me a lot
The print popup didn’t work in IE6. I had to add an extra line to Ext.ux.Printer.BaseRenderer (Base.js)
if(Ext.isIE6) {name=’_blank’;}
var win = window.open(”, name);
And also. I tested in a very slow PC with IE6, so the print window appeared after 5 seconds. It may occur that the person closes the popup before the print window opens. In that case you get an error message.
—————————
Error
—————————
A Runtime Error has occurred.
Do you wish to Debug?
Line: 227
Error: ‘dialogArguments.__IE_PrintType’ is null or not an object
—————————
Yes No
—————————
Therefore I wrapped win.print and win.close with:
if (Ext.isIE6 && win.isOpen || !Ext.isIE6 && !win.closed){win.print(); win.close();}
Great Component! Thanks! But It doesn’t work in Opera for me. How I can fix it?
Thanks Ed, that’s a great component.
It seems to take a long time for the print window to come up when using Firefox (v3.5.8), a lot longer than it does in IE. It works in the end though.
I’m using Ext 3.2.1 and newest Ext.ux.Printer, this is from IE8:
Browser: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1)
Message: Invalid argument
Row: 17
Char: 5
Code: 0
URI: http://localhost/TiMS/bin/js/extjs/plugins/Ext.ux.Printer/renderers/Base.js
There is some problem with:
var name = component && component.getXType
? String.format(”print_{0}_{1}”, component.getXType(), component.id.replace(/-/g,”_”))
: “print”;
var win = window.open(”, name);
@iwosz sorry to hear about that. I don’t have a lot of time to give to this thing right now but if you’re able to find a fix and commit it I’d be happy to merge it in.
For Row striping i just modified bodyTpl
It might not be the most elegant solution but it works for me…
bodyTpl: new Ext.XTemplate(
”,
”,
‘\{{dataIndex}\}’,
”,
”,
{rClassVar: ‘rowClass’, stripeRow: ‘[xindex % 2 === 0 ? "x-grid3-row " : "x-grid3-row x-grid3-row-alt "]‘}
)
rowClass is just a value i add to ‘convertedData’ in ‘prepareData’ …it’s just …
…
grid.store.data.each(function(item) {
var convertedData = {};
convertedData.rowClass = grid.getView().getRowClass(item);
…
Thanks a lot Ed this works really well!
Hmm i guess all the slashes in the code made my comment go strange…
So just put this in the tr of bodyTpl
class=”{{[this.stripeRow]}} {{[this.rClassVar]}}”‘,
You just need to escape out one level of the attributes so they get written correctly when the template gets applied.
(in the same way {{dataindex}} is escaped already in the template)
Hi Ed,
I am glad that I found your site… great extension!
Is there an easy way or config-option to hide certain columns when printing.
Especially when I use custom renderers for some of the grids content (e.g. to render icons instead of text), the printable html-table will show only braces or empty cells…
Therefore I would love to “switch off” those columns when printig.
Cheer Thomas