Using the Ext JS PivotGrid
July 29, 2010 by Ed Spencer · 2 Comments
One of the new components we just unveiled for the Ext JS 3.3 beta is PivotGrid. PivotGrid is a powerful new component that reduces and aggregates large datasets into a more understandable form.
A classic example of PivotGrid’s usefulness is in analyzing sales data. Companies often keep a database containing all the sales they have made and want to glean some insight into how well they are performing. PivotGrid gives the ability to rapidly summarize this large and unwieldy dataset – for example showing sales count broken down by city and salesperson.
A simple example
We created an example of this scenario in the 3.3 beta release. Here we have a fictional dataset containing 300 rows of sales data (see the raw data). We asked PivotGrid to break the data down by Salesperson and Product, showing us how they performed over time. Each cell contains the sum of sales made by the given salesperson/product combination in the given city and year.
Let’s see how we create this PivotGrid:
var SaleRecord = Ext.data.Record.create([
{name: 'person', type: 'string'},
{name: 'product', type: 'string'},
{name: 'city', type: 'string'},
{name: 'state', type: 'string'},
{name: 'month', type: 'int'},
{name: 'quarter', type: 'int'},
{name: 'year', type: 'int'},
{name: 'quantity', type: 'int'},
{name: 'value', type: 'int'}
]);
var myStore = new Ext.data.Store({
url: 'salesdata.json',
autoLoad: true,
reader: new Ext.data.JsonReader({
root: 'rows',
idProperty: 'id'
}, SaleRecord)
});
var pivotGrid = new Ext.grid.PivotGrid({
title : 'Sales Performance',
store : myStore,
aggregator: 'sum',
measure : 'value',
leftAxis: [
{dataIndex: 'person', width: 80},
{dataIndex: 'product', width: 90}
],
topAxis: [
{dataIndex: 'year'},
{dataIndex: 'city'}
]
});
The first half of this ought to be very familiar – we just set up a normal Record and Store. This is all we need to load our sample data so that it’s ready for pivoting. This is all exactly the same code as for our other Store-bound components like Grid and DataView so it’s easy to take an existing Grid and turn it into a PivotGrid.
The second half of the code creates the PivotGrid itself. There are 5 main components to a PivotGrid – the store, the measure, the aggregator, the left axis and the top axis. Taking these in turn:
- Store – the Store we created above
- Measure – the field in the data that we want to aggregate (in this case the sale value)
- Aggregator – the function we use to combine data into the cells. See the docs for full details
- Left Axis – the fields to break data down by on the left axis
- Top Axis – the fields to break data down by on the top axis
The measure and the items in the axes must all be fields from the Store. The aggregator function can usually be passed in as a string – there are 5 aggregator functions built in: sum, count, min, max and avg.
Renderers
This is all we need to create a simple PivotGrid; now it’s time to look at a few more advanced options. Let’s start with renderers. Once the data for each cell has been calculated, the value is passed to an optional renderer function, which takes each value in turn and returns another value. One of the PivotGrid examples shows average heights in feet and inches but the calculated data is in decimal. Here’s the renderer we use in that example:
new Ext.grid.PivotGrid({
store : myStore,
aggregator: 'avg',
measure : 'height',
//turns a decimal number of feet into feet and inches
renderer : function(value) {
var feet = Math.floor(value),
inches = Math.round((value - feet) * 12);
return String.format("{0}' {1}\"", feet, inches);
},
//the rest of the config
});
Customising cell appearance
Another one of the PivotGrid examples uses a custom cell style. As with the renderer, each cell has the opportunity to alter itself with a custom function – here’s the one we use in the countries example:
new Ext.grid.PivotGrid({
store : myStore,
aggregator: 'avg',
measure : 'height',
viewConfig: {
getCellCls: function(value) {
if (value < 20) {
return 'expense-low';
} else if (value < 75) {
return 'expense-medium';
} else {
return 'expense-high';
}
}
},
//the rest of the config
});
Reconfiguring at runtime
A lot of the power of PivotGrid is that it can be used by users of your application to summarize datasets any way they want. This is made possible by PivotGrid’s ability to reconfigure itself at runtime. We present one final example of a PivotGrid that can be reconfigured at runtime. Here’s how we perform the reconfiguration:
//the left axis can also be changed
pivot.topAxis.setDimensions([
{dataIndex: 'city', direction: 'DESC'},
{dataIndex: 'year', direction: 'ASC'}
]);
pivot.setMeasure('value');
pivot.setAggregator('avg');
pivot.view.refresh(true);
It’s easy to change the axes, dimension, aggregator and measure at any time and then refresh the data. The calculations are all performed client side so there is no need for another round-trip to the server when reconfiguring. The example linked above gives an example interface for updating a PivotGrid, though anything that can make the API calls above could be used.
I hope you enjoy the new components in this Ext JS 3.3 beta and look forward to comments and suggestions. Although we’re only at beta stage I think the additions are already quite robust so feel free to stress-test them.
Offline Apps with HTML5: A case study in Solitaire
June 21, 2010 by Ed Spencer · 3 Comments
One of my contributions to the newly-launched Sencha Touch mobile framework is the Touch Solitaire game. This is not the first time I have ventured into the dizzying excitement of Solitaire game development; you may remember the wonderful Ext JS Solitaire from 18 months ago. I’m sure you’ll agree that the new version is a small improvement.
Touch Solitaire is beautiful and feels like a native app
Solitaire is a nice example of a fun application that can be written with Sencha Touch. It makes use of the provided Draggables and Droppables, CSS-based animations, the layout manager and the brand new data package. The great thing about a game like this though is that it can be run entirely offline. Obviously this is simple with a native application, but what about a web app? Our goal is not just having the game able to run offline, but to save your game state locally too.
The answer comes in two parts:
Web Storage and the Sencha data package
HTML5 provides a brand new API called Web Storage for storing data locally. You can read all about it on my Web Storage post on Sencha’s blog but the summary is that you can store string data locally in the browser and retrieve it later, even if the browser or the user’s computer had been restarted in the meantime.
The crucial part of the sentence above is that we can only store string data. In the case of a game of Solitaire we need to store data on the elapsed time and number of moves as well as the location and status of each card. This doesn’t sound like the kind of data we want to manually encode into a string, so thankfully the data package comes to the rescue.
The Sencha Touch data package is a complete rewrite of the package that has been so successful in powering Ext JS 3.x. It shares many of the same philosophies and adds the learning we have gained from developing Ext JS 3.x over the past year. One of the new capabilities it offers us is a Local Storage proxy, which automatically marshalls your model data into local storage and transparently restores it when you need it.
Using the new proxy is simple – all we need to do is set up a new Store, specifying the Proxy and the Model that will be saved to it. Models are the spiritual successor to Ext JS 3.x’s Records. Now whenever we add, remove or update model instances in the store they are automatically saved to localStorage for us. Loading the store again is equally easy:
//set the store up
var gameStore = new Ext.data.Store({
proxy: new Ext.data.LocalStorageProxy({
id: 'solitaire-games'
}),
model: 'Game'
});
//saves all outstanding modifications, deletions or creations to localStorage
gameStore.sync();
//load our saved games
gameStore.read({
scope: this,
callback: function(records) {
//code to load the first record
}
});
And just like that we can save and restore games with Web Storage. We can visit our app’s webpage and start a game then come back later and find it automatically restored. But we still can’t play offline, for that we need the application cache.
The HTML5 Application Cache Manifest
The application cache is one of the best features of HTML5. It provides a simple (though sometimes frustrating) way of telling the browser about all of the files your application relies on so that it can download them all ready for offline use. All you have to do is create what’s known as a manifest file which lists all of the files the application needs – the Solitaire manifest looks like this:
CACHE MANIFEST #rev49 resources/icon.png resources/loading.png resources/themes/wood/board.jpg resources/themes/wood/cards.png resources/css/ext-touch.css resources/solitaire-notheme.css resources/themes/wood/wood.css resources/themes/metal/metal.css ext-touch-debug.js solitaire-all-debug.js
We tell the browser about the manifest file by pointing to it in the tag’s manifest atttibute. When the browser finds this file it downloads each of the listed assets so that they are ready for offline consumption. Note that it does not automatically include them on the page, you still need to do that yourself via the usual link and script tags. Here’s a snippet of the Solitaire index.html file:
<!doctype html>
<html manifest="solitaire.manifest">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Solitaire</title>
<link rel="stylesheet" href="resources/css/ext-touch.css" type="text/css">
<link rel="stylesheet" href="resources/solitaire-notheme.css" type="text/css">
<link rel="stylesheet" href="resources/themes/wood/wood.css" type="text/css">
<script type="text/javascript" src="ext-touch-debug.js"></script>
<script type="text/javascript" src="solitaire-all-debug.js"></script>
Note the manifest file definition in the html element at the top, and the fact that we still include our page resources the normal way. It sounds easy, but without a little setup first it can be a very frustrating experience. Usually your browser will try to cache as many files as possible, including the manifest file itself – we don’t want this. As soon as your browser has a long-term cache of the manifest file it is extremely difficult to update your application – all of the files are already offline and won’t be updated, and the browser won’t even ask the server for an updated manifest file.
Preventing this behaviour turns out to be fairly easy, and the solution in its simplest form comes in the shape of a .htaccess file with contents like the following:
<Files solitaire.manifest>
ExpiresActive On
ExpiresDefault "access"
</Files>
This directs Apache to tell the browser not to cache the manifest file at all, instead requesting the file from the server on every page load. Note that if the device is currently offline it will use the last manifest file it received.
This is half the battle won, but let’s say you change one of your application files and reload – you’ll find nothing happened. This is because when your browser asked the server for the manifest file it actually asked if the file had changed or not. As the manifest itself wasn’t updated, the server responds with a 304 (Not Modified) and your browser keeps the old file.
To make the browser pick up on the change to the application file you need to update the manifest file itself. This is where the mysterious “#rev49″ comes in on the manifest example file above. This is a suggestion from the excellent diveintohtml5 article on the subject – whenever you change any application files just bump up the revision number in the manifest file and your browser will know to download the updated files.
One final detail is that your Apache server probably isn’t set up to server manifest files with the correct mime type, so be sure to add the following line to your Apache config and restart the server:
AddType text/cache-manifest .manifest
Wrapping it up
Offline access is a big deal for mobile apps and Sencha Touch makes them much easier to write. The benefit is not so much that the apps can run without an internet connection (many modern touch devices have a near-permanent connection to the internet already), but that web apps can now be treated as first-class citizens alongside native apps.
The fact that many devices allow your users to save your app to their home screen and load it as though it were native is an important step – you keep all of the advantages of web app deployment while gaining some of the benefits of native apps. As more and more native hardware APIs become available to web apps their importance will only grow.
If you want to check out Solitaire’s offline support for yourself visit the application’s site and save it to your iPad’s home page. Try turning on airplane mode and loading the app and see how it behaves as though it were native. If you don’t have an iPad, you can load the app in up-to-date versions of Chrome or Safari and get a similar experience.
Jaml updates
January 29, 2010 by Ed Spencer · Leave a Comment
Jaml seems to have been getting a lot of interest lately. Here are a few quick updates on what’s been going on:
- Tom Robinson added support for CommonJS
- Eneko Alonso ported the project to MooTools, creating mooml
- Carl Furrow wrote up a nice comparison on Jaml and EJS
- Jaml is now a rendering option in JavaScriptMVC, along with John Resig’s microtemplates
- Andrew Dupont committed a series of patches such as improving Jaml’s efficiency and optionally removing the ‘with’ and ‘eval’ magic
In addition Jaml was recently picked up by Ajaxian, and a couple of people have written up blog posts about Jaml in languages other than English, which is great to see.
Jaml is up on Github and has a number of forks already. If you like the library and have something to add, fork away and send me a pull request!
If you’ve never seen Jaml before or have forgotten what it does, it turns this:
div(
h1("Some title"),
p("Some exciting paragraph text"),
br(),
ul(
li("First item"),
li("Second item"),
li("Third item")
)
);
Into this:
<div>
<h1>Some title</h1>
<p>Some exciting paragraph text</p>
<br />
<ul>
<li>First item</li>
<li>Second item</li>
<li>Third item</li>
</ul>
</div>
OSX Screensaver emulation with Canvas: That’s Bean
December 6, 2009 by Ed Spencer · 3 Comments
OS X has a pretty little screensaver which takes a bunch of images and ‘drops’ them, spinning, onto the screen. Think of it like scattering photographs onto a table, one at a time.
Naturally, there’s a desperate need for a JavaScript/Canvas port of this functionality, resulting in the following:
I had to limit the video capture framerate a bit so the video makes it look less smooth than it actually is. Check it out running in your own browser here.
For obvious reasons I have called the code behind this Bean, and it’s all available up on Github.
For the curious, here’s a little explanation about how it works. Bean starts off with a blank canvas and a list of image urls, which it preloads before getting started. It then drops one image at a time, rotating it as it goes. Each falling image is called a Plunger, because it plunges.
Each Plunger gets a random position and rotation to end up in, and takes care of drawing itself to the canvas on each frame by calculating its current size and rotation as it falls away from you.
Drawing each Plunger image on every frame quickly starts to kill the CPU, so we take a frame snapshot every time a Plunger has finished its descent. This just entails drawing the completed Plunges first and then using Canvas’ getImageData API to grab the pixel data for the image.
This gives us a snapshot of all of the fallen Plungers, meaning we can just draw a single background image and the currently falling Plunger on each frame. This approach ensures the performance remains constant, as we are only ever drawing a maximum of 2 images per frame. Each time a Plunger finishes its descent a new snapshot is taken.
Bean attempts to draw a new frame roughly 25 times per second and modern browsers seem to handle this pretty well. Safari pulls around 60% of one core on my MacBook Pro, with Firefox somewhat less performant. Needless to say, I didn’t even bother trying to make this work with IE.
Here’s the code to set the Bean in motion. This is using a few bundled APOD images:
var bean = new Bean({
imageUrls: [
'images/DoubleCluster_cs_fleming.jpg',
'images/NGC660Hagar0_c900.jpg',
'images/filaments_iac.jpg',
'images/m78wide_tvdavis900.jpg',
'images/sunearthpanel_sts129.jpg',
'images/NGC253_SSRO_900.jpg',
'images/Ophcloud_spitzer_c800.jpg'
],
canvasId : 'main',
fillBody : true
});
bean.onReady(function(bean) {
bean.start();
});
Jaml: beautiful HTML generation for JavaScript
November 4, 2009 by Ed Spencer · 30 Comments
Generating HTML with JavaScript has always been ugly. Hella ugly. It usually involves writing streams of hard-to-maintain code which just concatenates a bunch of strings together and spits them out in an ugly mess.
Wouldn’t it be awesome if we could do something pretty like this:
div(
h1("Some title"),
p("Some exciting paragraph text"),
br(),
ul(
li("First item"),
li("Second item"),
li("Third item")
)
);
And have it output something beautiful like this:
<div>
<h1>Some title</h1>
<p>Some exciting paragraph text</p>
<br />
<ul>
<li>First item</li>
<li>Second item</li>
<li>Third item</li>
</ul>
</div>
With Jaml, we can do exactly that. Jaml is a simple library inspired by the excellent Haml library for Ruby. It works by first defining a template using an intuitive set of tag functions, and then rendering it to appear as pretty HTML. Here’s an example of how we’d do that with the template above:
Jaml.register('simple', function() {
div(
h1("Some title"),
p("Some exciting paragraph text"),
br(),
ul(
li("First item"),
li("Second item"),
li("Third item")
)
);
});
Jaml.render('simple');
All we need to do is call Jaml.register with a template name and the template source. Jaml then stores this for later use, allowing us to render it later using Jaml.render(). Rendering with Jaml gives us the nicely formatted, indented HTML displayed above.
So we’ve got a nice way of specifying reusable templates and then rendering them prettily, but we can do more. Usually we want to inject some data into our template before rendering it – like this:
Jaml.register('product', function(product) {
div({cls: 'product'},
h1(product.title),
p(product.description),
img({src: product.thumbUrl}),
a({href: product.imageUrl}, 'View larger image'),
form(
label({'for': 'quantity'}, "Quantity"),
input({type: 'text', name: 'quantity', id: 'quantity', value: 1}),
input({type: 'submit', value: 'Add to Cart'})
)
);
});
In this example our template takes an argument, which we’ve called product. We could have called this anything, but in this case the template is for a product in an ecommerce store so product makes sense. Inside our template we have access to the product variable, and can output data from it.
Let’s render it with a Product from our database:
//this is the product we will be rendering
var bsg = {
title : 'Battlestar Galactica DVDs',
thumbUrl : 'thumbnail.png',
imageUrl : 'image.png',
description: 'Best. Show. Evar.'
};
Jaml.render('product', bsg);
The output from rendering this template with the product looks like this:
<div class="product">
<h1>Battlestar Galactica DVDs</h1>
<p>Best. Show. Evar.</p>
<img src="thumbnail.png" />
<a href="image.png">View larger image</a>
<form>
<label for="quantity">Quantity</label>
<input type="text" name="quantity" id="quantity" value="1"></input>
<input type="submit" value="Add to Cart"></input>
</form>
</div>
Cool – we’ve got an object oriented declaration of an HTML template which is cleanly separated from our data. How about we define another template, this time for a category which will contain our products:
Jaml.register('category', function(category) {
div({cls: 'category'},
h1(category.name),
p(category.products.length + " products in this category:"),
div({cls: 'products'},
Jaml.render('product', category.products)
)
);
});
Our category template references our product template, achieving something rather like a partial in Ruby on Rails. This obviously allows us to keep our templates DRY and to easily render a hypothetical Category page like this:
//here's a second product
var snowWhite = {
title : 'Snow White',
description: 'not so great actually',
thumbUrl : 'thumbnail.png',
imageUrl : 'image.png'
};
//and a category
var category = {
name : 'Doovde',
products: [bsg, snowWhite]
}
Jaml.render('category', category);
All we’ve done is render the ‘category’ template with our ‘Doovde’ category, which contains an array of products. These were passed into the ‘product’ template to produce the following output:
<div class="category">
<h1>Doovde</h1>
<p>2 products in this category:</p>
<div class="products"><div class="product">
<h1>Battlestar Galactica DVDs</h1>
<p>Best. Show. Evar.</p>
<img src="thumbnail.png" />
<a href="image.png">View larger image</a>
<form>
<label for="quantity">Quantity</label>
<input type="text" name="quantity" id="quantity" value="1"></input>
<input type="submit" value="Add to Cart"></input>
</form>
</div>
<div class="product">
<h1>Snow White</h1>
<p>not so great actually</p>
<img src="thumbnail.png" />
<a href="image.png">View larger image</a>
<form>
<label for="quantity">Quantity</label>
<input type="text" name="quantity" id="quantity" value="1"></input>
<input type="submit" value="Add to Cart"></input>
</form>
</div>
</div>
</div>
You can see live examples of all of the above at http://edspencer.github.com/jaml.
Jaml currently sports a few hacks and is not particularly efficient. It is presented as a proof of concept, though all the output above is true output from the library. As always, all of the code is up on Github, and contributions are welcome
Jaml would be suitable for emulating a Rails-style directory structure inside a server side JavaScript framework – each Jaml template could occupy its own file, with the template name coming from the file name. This is roughly how Rails and other MVC frameworks work currently, and it eliminates the need for the Jaml.register lines. Alternatively, the templates could still be stored server side and simply pulled down and evaluated for client side rendering.
Happy rendering!