Client-Side Excel Library CTP

Mike Dour / Wednesday, October 29, 2014

If you haven’t seen it already, we recently released a 100% JavaScript-only, client-side Excel library for Ignite UI and I’m super excited about it. It allows you to read, write, and manipulate Excel workbooks. You can even create and solve formulas, all from inside the browser!! It was released in 14.2 as a CTP so we could get your feedback on it, but we will be releasing a complete RTM version in 15.1. You can find information and a live sample of it here. Definitely check out the overview page, which is packed with important information for using this library.

But that’s not even the coolest part. Not only did we deliver a purely JavaScript library for Excel workbooks, but it has all the features of our existing .NET Excel libraries. Did we re-write the entire C# Excel library in JavaScript to provide this level of feature parity? We could have, but it would have taken a lot of effort getting there not to mention the ongoing challenge of maintaining feature parity between the versions and addressing bugs in both implementations. So we came up with something better. We built a C# to JavaScript source-to-source compiler, or transcompiler. We have actually been using this for a few releases now to deliver some of the Ignite UI controls, but it was missing support for some constructs being used in the C# Excel library. So we really beefed up its language support as well as changed its semantic analysis engine. Now based on Microsoft’s .NET Compiler Platform ("Roslyn") for C# semantic analysis, our transcompiler is able to read in our existing C# Excel library and generate semantically equivalent JavaScript code. There are still a few rough edges to smooth out, but we are currently addressing these issues to deliver the highest quality Excel library we can in the next release.

Unfortunately, one of those rough edges was in documentation. We were not able to generate API documentation off the translated JavaScript code in time for the 14.2 release, so the preview is lacking a bit in terms of help. But luckily, since the code was translated from our existing C# code, you can just use the API documentation already available for the C# version and with a few minor changes, know exactly how to use the equivalent JavaScript code. You can find the API overview for the jQuery server-side Excel library here.

So what kinds of changes are we talking about? I have listed the differences below.

Namespace Name

With all of our existing Excel libraries, all types exist in the Infragistics.Documents.Excel namespace. There are a few types in sub-namespaces as well, such as Infragistics.Documents.Excel.Filtering. In the JavaScript code, all types can be found under the path “jQuery.ig.excel.”, or “$.ig.excel.” So in C#, you might create a workbook like so:

using Infragistics.Documents.Excel;

var workbook = new Workbook(WorkbookFormat.Excel2007);

But in JavaScript, you would create a workbook this way instead:

var workbook = new $.ig.excel.Workbook($.ig.excel.WorkbookFormat.excel2007);

Or, if you’d like to shorten things a bit, you can even do something like this:

var xl = $.ig.excel;

var workbook = new xl.Workbook(xl.WorkbookFormat.excel2007);

Also, regardless of the namespace in the C# Excel library, it always maps to $.ig.excel in the translated JavaScript. So, for example, the classes in Infragistics.Documents.Excel.Filtering would also exist under the $.ig.excel path.

Member Case

You may have noticed above that the enum member representing the Excel 2007 format was Pascal cased in the C# example and camel cased in the JavaScript example. This is another difference. When translating the code, we follow the JavaScript convention of using camel case for most functions and properties and Pascal case for constructor functions (note, however, that not all Pascal-cased entities should be constructed in your code. Many entities in the Excel library are to be constructed and managed internally and there are public APIs for constructing and obtaining these instances). So, for example, when calling the Recalculate method on a Workbook instance in C#, it would look like this:

var workbook = new Workbook(WorkbookFormat.Excel2007);

workbook.Recalculate();

But the equivalent function in JavaScript would be called like this:

var xl = $.ig.excel;
var
workbook = new xl.Workbook(xl.WorkbookFormat.excel2007);

workbook.recalculate();

Properties

Complex property get/set accessors are used throughout the C# Excel library. We could have translated these to equivalent get/set accessors using Object.create in JavaScript, but since we want to support older browsers such as IE8, this is not currently an option. So these properties have instead been translated to equivalent functions. And for read-write properties, they are translated to a single function which inspects the arguments collection to determine whether the getter or setter is being used. So, for example, if you want to get and set the value of a cell in C#, it might look like this:

var workbook = new Workbook(WorkbookFormat.Excel2007);

var worksheet = workbook.Worksheets.Add("Sheet1");

var cell = worksheet.GetCell("A1");

 

cell.Value = 7;

var v = cell.Value;

But in JavaScript, you would need to use a function call to get and set the value. Here is the equivalent code in JavaScript:

var xl = $.ig.excel;

var workbook = new xl.Workbook(xl.WorkbookFormat.excel2007);

var worksheet = workbook.worksheets().add('Sheet1');

var cell = worksheet.getCell('A1');

 

cell.value(7);

var v = cell.value();

Indexers

The indexer translation is essentially the same as that of properties. The only difference is that indexers are not accessed by name in C#. Instead there is language support so that square brackets denote the index to pass to the indexer property. Since this syntax has a different meaning in JavaScript, we instead translate indexers into a special function named “item” on the target object. It is used just like the functions translated from properties, except the index gets passed as the first argument. So you would access the first row of a worksheet in C# like this:

var workbook = new Workbook(WorkbookFormat.Excel2007);

var worksheet = workbook.Worksheets.Add("Sheet1");

 

var firstRow = worksheet.Rows[0];

But in JavaScript, it would be done like this:

var xl = $.ig.excel;

var workbook = new xl.Workbook(xl.WorkbookFormat.excel2007);

var worksheet = workbook.worksheets().add('Sheet1');

 

var firstRow = worksheet.rows().item(0);

However, there is a shortcut syntax that can be used in cases like this. When a property is read-only and returns a collection, the translated function can optionally accept indexer arguments when getting the collection. If they are specified, the function will return the element at the specified index instead of the collection. With this convenient syntax, the code above can be simplified to this:

var xl = $.ig.excel;

var workbook = new xl.Workbook(xl.WorkbookFormat.excel2007);

var worksheet = workbook.worksheets().add('Sheet1');

 

var firstRow = worksheet.rows(0);

Load/Save

Up until now, all differences I have mentioned were due to the translation process and therefore, there is a pretty easy transformation you can do to map from C# APIs to JavaScript APIs. However, for the Load and Save methods, this is not the case. The Workbook’s Load and Save methods in the C# API are synchronous because these operations can be done quickly for most files. However, on JavaScript things are much slower and it is likely that synchronous load and save operations will pause the browser long enough to cause a noticeable delay. So there is a good chance we will implement asynchronous loading and saving in the RTM or a future release. In anticipation of this, specialized load and save functions have been created for the Client-Side Excel library to support a callback style of loading and saving. Currently, these callbacks will be executed before the call to load/save returns since the operation is synchronous, but this may not be the case in the future. To save a workbook, you can use the following code:

var xl = $.ig.excel;

var workbook = new xl.Workbook(xl.WorkbookFormat.excel2007);

workbook.worksheets().add('Sheet1');

workbook.save(function (error, data) {

    // The operation is complete at this point

});

If the operation succeeds, the 2nd argument to the callback function (named “data” in this case) will be a Uint8Array containing the saved binary data. There is also an optional save options parameter which can be specified as the first argument and in the RTM version, these options will allow you to specify the format in which the data should be saved (Uint8Array, Base-64 encoded string, …). Loading documents is done similarly, but instead of calling load of a workbook instance, it is called off the Workbook constructor function, much like a static method in C#:

$.ig.excel.Workbook.load(data, function (error, workbook) {

    // The operation is complete at this point

});

If the operation succeeds, the 2nd argument to the callback function will be the loaded workbook instance. In this case, the 1st parameter to the load function can be a Uint8Array with the binary workbook data or a Base-64 encoded string of that binary data. The load function will automatically detect the format and load the workbook file assuming it is in the any of the following formats: .xls, .xlt, .xlsx, .xltx, .xlsm, or .xltm.

So hopefully this can help you get started with the Client-Side Excel library preview. There are a few things that don’t work properly yet (such as loading files with dates), but what we have provided should give you a good sense of what’s to come in 15.1. Please let us know what you think and if there are any pain points with the API or ways you think we can do better to make this library as easy as possible to use. Let us know at igniteui@infragistics.com. We look forward to your feedback. Thanks!