New: data table support in report builder API

As you can see in the new bank statement example, jsreports now includes support for data tables defined either in JSON or using the report builder API.  Here’s an example:

jsreports table element example

This table uses nested JSON data, applies a grouping, and then uses CSS to add the borders and bold text.  Cell formats are supplied as configuration properties.  Here’s the code:

.table(0, 1.25, 4.75, 2.5, { data: 'changeItems', hasFooter: true, 
    groupBy: 'category', fontSize: 9, hideRowWhenExpr: '!description' 
})
.column('50%', '   [description]', '', '', { 
    align: 'left', group0Header: '[category]' })
.column('25%', '[currentPeriod]', 'This Period', '[SUM(currentPeriod)]', { 
    align: 'right',
    detailStyle: {
        pattern: '#,##0.00'
    },
    group0Header: '[SUM(currentPeriod)]',
    group0HeaderStyle: {
        pattern: '#,##0.00'
    }
})
.column('25%', '[ytd]', 'Year-to-Date', '[SUM(ytd)]', { 
    align: 'right',
    detailStyle: {
        pattern: '#,##0.00'
    },
    group0Header: '[SUM(ytd)]',
    group0HeaderStyle: {
        pattern: '#,##0.00'
    }
})

We’ll be adding support for tables in the report designer soon.  For now, they’re only available through the API.

Enhancements to pivot table functionality

Company founder Ebeneezer P. Jsreports has informed us that our bonuses this year will be determined by the number of blog posts we write, so you can look forward to seeing a lot more from me here in the coming months.

Let’s see, what to write about today? Here’s something. We just made some improvements to the pivot engine in jsreports. In case you aren’t familiar, the pivot feature lets you generate columns in a report on the fly from distinct values in the rows of your data source. To use it, check the “enable pivot” box in the designer (under Data & Groups -> Pivot, in the left panel).

report-designer-pivot

You’ll see two areas in the canvas: the non-shaded area between the two purple lines, in the center of the canvas above, is the “pivot column”–this is what will get repeated. You’ll get one copy of this column for each unique value in your pivot field (unless you turn on bucketing, in which case you could get one column for each distinct month, or year, for example).

The other, shaded area in the report is the reserved space for the generated columns. As columns are generated, they’ll fill this space.  If there are too many columns to fit in the shaded space, the report will expand horizontally to fit all columns. Any non-shaded space to the right, and any elements in that space, will be shifted rightward so that they remain in the same position relative to the right edge of the report.

Here’s what happens when you run the report:

jsreports-pivot-output

The pivot column has been repeated left-to-right across the reserved space. The logo remains positioned where it was on the canvas, because it was outside of the repeating pivot column. Each element in the pivot column aggregates the values for its particular grouping and pivot value.

That’s all there is to it. You could put whatever you want in the pivot cells and it will be cloned and any text expressions evaluated against the set of rows that make up that pivot cell.

How to render PDF directly into an <iframe>

I’m trying to make a point of writing up a quick blog entry whenever I answer an interesting question from a customer. Today, the interesting question was how to render a report into a section of the page, but in PDF format, not HTML. The answer is to use a little-known config option for the jsreports.export method, called outputHandler.

Using outputHandler, you can provide a function that will intercept the resulting PDF blob and do whatever you want.  We normally use this on mobile to generate a download file, but you can also use the blob as the source for a new browser tab or iframe content.  Here’s an example:

jsreports.export({
    format: 'pdf',
    report_def: myReportDef,
    datasets: myDataSources,
    outputHandler: function(pdfBlob) {
        $('.report-output').attr('src', URL.createObjectURL(pdfBlob));
    }
});

You’ll need an <iframe> in order to handle the PDF content, and you should set its type property, like this:

<iframe type="application/pdf" class="report-output"></iframe>

And here’s what it looks like in Chrome:

jsreports-demo-pdf-to-iframe

 

New features! Page breaks and subreports

It’s been a while since the last update — things have been busy here.  But today I get to announce a couple of great new features: page breaks and subreports.

jsreports page break and subreport elements

The page break element gives you control over where pages break in PDF output mode.  Just drag it to the right place in your report template, and you’ll get corresponding page breaks in the PDF.  You can set the “every Nth record” property to only break after a certain number of records.

The subreport feature lets you embed a report within another report.  When you add the subreport to the canvas in the designer, you can jump directly into the subreport to edit it.  You can link data between outer report and the subreport by matching field values, too.

jsreports subreport element options

We’ve got a bunch of other work in progress that I’ll write about soon.  If you have specific feature requests or want to know more about these new features, email us at support@jsreports.com.

Our first plugin for the report designer

I get requests from customers for features that would make great additions to jsreports but that not everyone will need.  For things that aren’t core product features, we’ve been working on the idea of plugins–extra modules you can load individually as needed.

Today as part of version 1.2.37 we’re including our first ever plugin: a report selector for the designer.  Previously, as the developer, you had to specify a single report template whenever a designer was opened.  Now, you can provide a list of reports and allow the user to switch between them, and optionally create new reports and delete them too.

reportlist-plugin-demo

You can use the plugin by specifying it in the plugins config property of the designer, like this:

var designer = new jsreports.Designer({
    embedded: true,
    container: $(".report-designer-container"),
    dataSources: data,
    report: null,
    showDownloadButton: true,
    plugins: [{
        pluginType: 'ReportList',
        reports: [ report1, report2 ],
        listeners: {
            "deletereport": function(evt, report) {
                // TODO handle report deletion here
            }
        }
    }]
});

Full documentation for the plugin is here: ReportList plugin API docs

For this first plugin, we’ll be including it as part of the main jsreports distribution.  As the collection of plugins grows we’ll begin to bundle them separately.

Got ideas for plugins?  Let us know.

Improvements to Jasper Reports support on the way

Just FYI, we’ve been getting a few requests lately for increased compatibility with Jasper Reports.  We’re working on it!

It’s not likely we’ll support every nook and cranny of the Jasper Reports feature set, since our goals are not identical with Jasper Reports, but we think we can do a solid job of making our report designer handle the most common features people are using.

First up is enabling our designer to open Jasper Reports’s .jrxml file format.  Right now, we can go from our JSON format to .jrxml but not the other way.  That means that right now you can create brand new reports and send them to Jasper, but you can’t yet modify existing reports that you’ve got in the Jasper format.

If you have Jasper Reports .jrxml reports you’re using today and you’d like to add a report designer into your web application to let your end users modify and customize their own reports, shoot me an email so I can make sure we cover your use case.

Photo of a bus

San Francisco home prices vs jsreports file size, 2014-2015

The latest version of jsreports weighs in at a hefty 873 kB. To somebody like me who grew up on 360kB floppy disks, that seems excessive. To find out what’s going on, I dug a little deeper into the data.

San Francisco Home Prices vs jsreports File Size, 2014 - 2015

Here you can see that through the end of 2014 and 2015, jsreports file size has closely tracked housing prices in San Francisco. What does this mean? Two things:

1. Invest in jsreports now before you’re priced out of the market forever.
2. jsreports file size is due for a correction.

Regarding corrections, I’ve tasked helper gnome Jerry with finding a way to trim the fat. His first idea, removing every third byte, was unsuccessful. But I’m told he’s got something better in the works which I hope to be able to announce in the next couple of weeks.

Graph made with the very nice plot.ly.

Testing generated binary files with CasperJS / PhantomJS

Tinkering with CasperJS today to see if we can use it in our testing pipeline. Turns out it’s not that easy in PhantomJS to handle binary files generated in the browser, like the PDF and Excel files generated by jsreports. Here’s the solution that finally worked:

casper.on('page.initialized', function(page) {
    page.evaluate(function() {
        window.saveAs = function(blob) {
            var reader = new FileReader();
            reader.onloadend = function() {
                window.callPhantom(reader.result);
            }
            reader.readAsBinaryString(blob); 
        };
    });
    page.onCallback = function(binaryStr) {
        var fOut = fs.open('download.pdf', 'wb');
        fOut.write(binaryStr);
        fOut.close();
    };
});

We inject a custom window.saveAs into the browser, which jsreports will find and use automatically; that function turns the Blob into a string and passes it out to PhantomJS via callPhantom. Back in PhantomJS, we write the string to a binary file in the test directory.

Why a JavaScript based reporting solution?

Why jsreports?  There’s a number of reporting solutions already out there, so why another?

Two reasons. First, I’ve been working with those other reporting solutions for more than a decade and in each case I’ve found things I wanted to improve. jsreports is intended to overcome the limitations I’ve encountered with those other solutions.  More on that in a future post.

Second, applications are moving in the direction of lightweight, decoupled components.  Our goal is to make a lighter, simpler reporting framework that can easily slot into any stack. JavaScript gives us great fit with modern stacks like Angular, React, and NodeJS.  We’re starting with great client-side reporting, but we’ll also run on the server where that makes sense.

jsreports should be the solution you go to if you’re building new web or SaaS software, need powerful and intuitive reporting for your end users, and don’t want to get locked into a heavyweight server-based solution.

So here are the two main goals for jsreports at the outset:

1. Be the easiest full-featured reporting solution to drop into any web-based application stack

2. Provide the easiest, most intuitive end-user report creation and customization tools on the market

Try it out and let me know how we’re doing so far.