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';

Related posts

About Ed Spencer
Software Architect at Sencha Inc where I lead the development of Ext JS and supporting projects. A longtime lover of JavaScript and related geekery, currently living in Palo Alto.

Comments

23 Responses to “Ext.ux.Printer – printing for any ExtJS Component”
  1. weazil says:

    Awesome addon, found it easy to implement and use was just curious about grid striping rows I've toyed with it with minimal success

  2. Anonymous says:

    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

  3. Edward Spencer says:

    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

  4. weazil says:

    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.

  5. TopKatz says:

    Ed, this is brilliant!!!

  6. TopKatz says:

    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!!!’;

  7. TopKatz says:

    BTW: Ed, I can not find a email address to you on the site : (

  8. Jorge says:

    I just started using the component in an internal app. Thank you for sharing.

  9. Paul Holser says:

    Ed, this is a terrific extension! Thanks much!

  10. Itari says:

    Is there an ambition to extend it for Grouping Store???

  11. 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.

  12. Luca says:

    Hi,

    if i want print ext.window?

  13. Paul says:

    Thank’s you help me a lot

  14. Christiaan says:

    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();}

  15. Ivan says:

    Great Component! Thanks! But It doesn’t work in Opera for me. How I can fix it?

  16. Ian says:

    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.

  17. iwosz says:

    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);

  18. Ed Spencer says:

    @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.

  19. Gareth says:

    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!

  20. Gareth says:

    Hmm i guess all the slashes in the code made my comment go strange…

  21. Gareth says:

    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)

  22. Thomas says:

    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

Trackbacks

Check out what others are saying about this post...
  1. [...] we want to print things, like grids or trees. The Ext JS printing plugin is pretty good for that. But what if we want to export them instead? Enter [...]



Speak Your Mind

Tell us what you're thinking...
and oh, if you want a pic to show with your comment, go get a gravatar!