Building a data-driven image carousel with Sencha Touch 2
February 11, 2012 by Ed Spencer · 5 Comments
This evening I embarked on a little stellar voyage that I’d like to share with you all. Most people with great taste love astronomy and Sencha Touch 2, so why not combine them in a fun evening’s web app building?
NASA has been running a small site called APOD (Astronomy Picture Of the Day) for a long time now, as you can probably tell by the awesome web design of that page. Despite its 1998-era styling, this site incorporates some pretty stunning images of the universe and is begging for a mobile app interpretation.
We’re not going to go crazy, in fact this whole thing only took about an hour to create, but hopefully it’s a useful look at how to put something like this together. In this case, we’re just going to write a quick app that pulls down the last 20 pictures and shows them in a carousel with an optional title.
Here’s what it looks like live. You’ll need a webkit browser (Chrome or Safari) to see this, alternatively load up http://code.edspencer.net/apod on a phone or tablet device:
The full source code for the app is up on github, and we’ll go through it bit by bit below.
The App
Our app consists of 5 files:
index.html, which includes our JavaScript files and a little CSS
app.js, which boots our application up
app/model/Picture.js, which represents a single APOD picture
app/view/Picture.js, which shows a picture on the page
app/store/Pictures.js, which fetches the pictures from the APOD RSS feed
The whole thing is up on github and you can see a live demo at http://code.edspencer.net/apod. To see what it’s doing tap that link on your phone or tablet, and to really feel it add it to your homescreen to get rid of that browser chrome.
The Code
Most of the action happens in app.js, which for your enjoyment is more documentation than code. Here’s the gist of it:
/* * This app uses a Carousel and a JSON-P proxy so make sure they're loaded first */Ext.require([ 'Ext.carousel.Carousel', 'Ext.data.proxy.JsonP']);
/** * Our app is pretty simple - it just grabs the latest images from NASA's Astronomy Picture Of the Day * (http://apod.nasa.gov/apod/astropix.html) and displays them in a Carousel. This file drives most of * the application, but there's also: * * * A Store - app/store/Pictures.js - that fetches the data from the APOD RSS feed * * A Model - app/model/Picture.js - that represents a single image from the feed * * A View - app/view/Picture.js - that displays each image * * Our application's launch function is called automatically when everything is loaded. */Ext.application({ name: 'apod', models: ['Picture'], stores: ['Pictures'], views: ['Picture'], launch: function() { var titleVisible = false, info, carousel; /** * The main carousel that drives our app. We're just telling it to use the Pictures store and * to update the info bar whenever a new image is swiped to */ carousel = Ext.create('Ext.Carousel', { store: 'Pictures', direction: 'horizontal', listeners: { activeitemchange: function(carousel, item) { info.setHtml(item.getPicture().get('title')); } } }); /** * This is just a reusable Component that we pin to the top of the page. This is hidden by default * and appears when the user taps on the screen. The activeitemchange listener above updates the * content of this Component whenever a new image is swiped to */ info = Ext.create('Ext.Component', { cls: 'apod-title', top: 0, left: 0, right: 0 }); //add both of our views to the Viewport so they're rendered and visible Ext.Viewport.add(carousel); Ext.Viewport.add(info); /** * The Pictures store (see app/store/Pictures.js) is set to not load automatically, so we load it * manually now. This loads data from the APOD RSS feed and calls our callback function once it's * loaded. * * All we do here is iterate over all of the data, creating an apodimage Component for each item. * Then we just add those items to the Carousel and set the first item active. */ Ext.getStore('Pictures').load(function(pictures) { var items = []; Ext.each(pictures, function(picture) { if (!picture.get('image')) { return; } items.push({ xtype: 'apodimage', picture: picture }); }); carousel.setItems(items); carousel.setActiveItem(0); }); /** * The final thing is to add a tap listener that is called whenever the user taps on the screen. * We do a quick check to make sure they're not tapping on the carousel indicators (tapping on * those indicators moves you between items so we don't want to override that), then either hide * or show the info Component. * * Note that to hide or show this Component we're adding or removing the apod-title-visible class. * If you look at index.html you'll see the CSS rules style the info bar and also cause it to fade * in and out when you tap. */ Ext.Viewport.element.on('tap', function(e) { if (!e.getTarget('.x-carousel-indicator')) { if (titleVisible) { info.element.removeCls('apod-title-visible'); titleVisible = false; } else { info.element.addCls('apod-title-visible'); titleVisible = true; } } }); }});This is pretty simple stuff and you can probably just follow the comments to see what’s going on. Basically though the app.js is responsible for launching our application, creating the Carousel and info Components, and setting up a couple of convenient event listeners.
We also had a few other files:
Picture Model
Found in app/model/Picture.js, our model is mostly just a list of fields sent back in the RSS feed. There is one that’s somewhat more complicated than the rest though – the ‘image’ field. Ideally, the RSS feed would have sent back the url of the image in a separate field and we could just pull it out like any other, but alas it is embedded inside the main content.
To get around this, we just specify a convert function that grabs the content field, finds the first image url inside of it and pulls it out. To make sure it looks good on any device we also pass it through Sencha IO src, which resizes the image to fit the screen size of whatever device we happen to be viewing it on:
/** * Simple Model that represents an image from NASA's Astronomy Picture Of the Day. The only remarkable * thing about this model is the 'image' field, which uses a regular expression to pull its value out * of the main content of the RSS feed. Ideally the image url would have been presented in its own field * in the RSS response, but as it wasn't we had to use this approach to parse it out */Ext.define('apod.model.Picture', { extend: 'Ext.data.Model', config: { fields: [ 'id', 'title', 'link', 'author', 'content', { name: 'image', type: 'string', convert: function(value, record) { var content = record.get('content'), regex = /img src=\"([a-zA-Z0-9\_\.\/\:]*)\"/, match = content.match(regex), src = match[1];
if (src != "" && !src.match(/\.gif$/)) { src = "http://src.sencha.io/screen.width/" + src; } return src; } } ] }});Pictures Store
Our Store is even simpler than our Model. All it does is load the APOD RSS feed over JSON-P (via Google’s RSS Feed API) and decode the data with a very simple JSON Reader. This automatically pulls down the images and runs them through our Model’s convert function:
/** * Grabs the APOD RSS feed from Google's Feed API, passes the data to our Model to decode */Ext.define('apod.store.Pictures', { extend: 'Ext.data.Store', config: { model: 'apod.model.Picture', proxy: { type: 'jsonp', url: 'https://ajax.googleapis.com/ajax/services/feed/load?v=1.0&q=http://www.acme.com/jef/apod/rss.xml&num=20', reader: { type: 'json', rootProperty: 'responseData.feed.entries' } } }});Tying it all together
Our app.js loads our Model and Store, plus a really simple Picture view that is basically just an Ext.Img. All it does then is render the Carousel and Info Component to the screen and tie up a couple of listeners.
In case you weren’t paying attention before, the info component is just an Ext.Component that we rendered up in app.js as a place to render the title of the image you’re currently looking at. When you swipe between items in the carousel the activeitemchange event is fired, which we listen to near the top of app.js. All our activeitemchange listener does is update the HTML of the info component to the title of the image we just swiped to.
But what about the info component itself? Well at the bottom of app.js we added a tap listener on Ext.Viewport that hides or shows the info Component whenever you tap anywhere on the screen (except if you tap on the Carousel indicator icons). With a little CSS transition loveliness we get a nice fade in/out transition when we tap the screen to reveal the image title. Here’s that tap listener again:
/** * The final thing is to add a tap listener that is called whenever the user taps on the screen. * We do a quick check to make sure they're not tapping on the carousel indicators (tapping on * those indicators moves you between items so we don't want to override that), then either hide * or show the info Component. */Ext.Viewport.element.on('tap', function(e) { if (!e.getTarget('.x-carousel-indicator')) { if (titleVisible) { info.element.removeCls('apod-title-visible'); titleVisible = false; } else { info.element.addCls('apod-title-visible'); titleVisible = true; } }});The End of the Beginning
This was a really simple app that shows how easy it is to put these things together with Sencha Touch 2. Like with most stories though there’s more to come so keep an eye out for parts 2 and 3 of this intergalactic adventure.
Proxies in Ext JS 4
February 2, 2011 by Ed Spencer · 39 Comments
One of the classes that has a lot more prominence in Ext JS 4 is the data Proxy. Proxies are responsible for all of the loading and saving of data in an Ext JS 4 or Sencha Touch application. Whenever you’re creating, updating, deleting or loading any type of data in your app, you’re almost certainly doing it via an Ext.data.Proxy.
If you’ve seen January’s Sencha newsletter you may have read an article called Anatomy of a Model, which introduces the most commonly-used Proxies. All a Proxy really needs is four functions – create, read, update and destroy. For an AjaxProxy, each of these will result in an Ajax request being made. For a LocalStorageProxy, the functions will create, read, update or delete records from HTML5 localStorage.
Because Proxies all implement the same interface they’re completely interchangeable, so you can swap out your data source – at design time or run time – without changing any other code. Although the local Proxies like LocalStorageProxy and MemoryProxy are self-contained, the remote Proxies like AjaxProxy and ScriptTagProxy make use of Readers and Writers to encode and decode their data when communicating with the server.

Whether we are reading data from a server or preparing data to be sent back, usually we format it as either JSON or XML. Both of our frameworks come with JSON and XML Readers and Writers which handle all of this for you with a very simple API.
Using a Proxy with a Model
Proxies are usually used along with either a Model or a Store. The simplest setup is just with a model:
var User = Ext.regModel('User', {
fields: ['id', 'name', 'email'],
proxy: {
type: 'rest',
url : '/users',
reader: {
type: 'json',
root: 'users'
}
}
});
Here we’ve created a User model with a RestProxy. RestProxy is a special form of AjaxProxy that can automatically figure out Restful urls for our models. The Proxy that we set up features a JsonReader to decode any server responses – check out the recent data package post on the Sencha blog to see Readers in action.
When we use the following functions on the new User model, the Proxy is called behind the scenes:
var user = new User({name: 'Ed Spencer'});
//CREATE: calls the RestProxy's create function because the user has never been saved
user.save();
//UPDATE: calls the RestProxy's update function because it has been saved before
user.set('email', 'ed@sencha.com');
//DESTROY: calls the RestProxy's destroy function
user.destroy();
//READ: calls the RestProxy's read function
User.load(123, {
success: function(user) {
console.log(user);
}
});
We were able to perform all four CRUD operations just by specifying a Proxy for our Model. Notice that the first 3 calls are instance methods whereas the fourth (User.load) is static on the User model. Note also that you can create a Model without a Proxy, you just won’t be able to persist it.
Usage with Stores
In Ext JS 3.x, most of the data manipulation was done via Stores. A chief purpose of a Store is to be a local subset of some data plus delta. For example, you might have 1000 products in your database and have 25 of them loaded into a Store on the client side (the local subset). While operating on that subset, your user may have added, updated or deleted some of the Products. Until these changes are synchronized with the server they are known as a delta.
In order to read data from and sync to the server, Stores also need to be able to call those CRUD operations. We can give a Store a Proxy in the same way:
var store = new Ext.data.Store({
model: 'User',
proxy: {
type: 'rest',
url : '/users',
reader: {
type: 'json',
root: 'users'
}
}
});
We created the exact same Proxy for the Store because that’s how our server side is set up to deliver data. Because we’ll usually want to use the same Proxy mechanism for all User manipulations, it’s usually best to just define the Proxy once on the Model and then simply tell the Store which Model to use. This automatically picks up the User model’s Proxy:
//no need to define proxy - this will reuse the User's Proxy
var store = new Ext.data.Store({
model: 'User'
});
Store invokes the CRUD operations via its load and sync functions. Calling load uses the Proxy’s read operation, which sync utilizes one or more of create, update and destroy depending on the current Store delta.
//CREATE: calls the RestProxy's create function to create the Tommy record on the server
store.add({name: 'Tommy Maintz'});
store.sync();
//UPDATE: calls the RestProxy's update function to update the Tommy record on the server
store.getAt(1).set('email', 'tommy@sencha.com');
store.sync();
//DESTROY: calls the RestProxy's destroy function
store.remove(store.getAt(1));
store.sync();
//READ: calls the RestProxy's read function
store.load();
Store has used the exact same CRUD operations on the shared Proxy. In all of the examples above we have used the exact same RestProxy instance from three different places: statically on our Model (User.load), as a Model instance method (user.save, user.destroy) and via a Store instance (store.load, store.sync):

Of course, most Proxies have their own private methods to do the actual work, but all a Proxy needs to do is implement those four functions to be usable with Ext JS 4 and Sencha Touch. This means it’s easy to create new Proxies, as James Pearce did in a recent Sencha Touch example where he needed to read address book data from a mobile phone. Everything he does to set up his Proxy in the article (about 1/3rd of the way down) works the same way for Ext JS 4 too.
Ext JS 4: The Class Definition Pipeline
January 25, 2011 by Ed Spencer · 21 Comments
Last time, we looked at some of the features of the new class system in Ext JS 4, and explored some of the code that makes it work. Today we’re going to dig a little deeper and look at the class definition pipeline – the framework responsible for creating every class in Ext JS 4.
As I mentioned last time, every class in Ext JS 4 is an instance of Ext.Class. When an Ext.Class is constructed, it hands itself off to a pipeline populated by small, focused processors, each of which handles one part of the class definition process. We ship a number of these processors out of the box – there are processors for handling mixins, setting up configuration functions and handling class extension.
The pipeline is probably best explained with a picture. Think of your class starting its definition journey at the bottom left, working its way up the preprocessors on the left hand side and then down the postprocessors on the right, until finally it reaches the end, where it signals its readiness to a callback function:

The distinction between preprocessors and postprocessors is that a class is considered ‘ready’ (e.g. can be instantiated) after the preprocessors have all been executed. Postprocessors typically perform functions like aliasing the class name to an xtype or back to a legacy class name – things that don’t affect the class’ behavior.
Each processor runs asynchronously, calling back to the Ext.Class constructor when it is ready – this is what enables us to extend classes that don’t exist on the page yet. The first preprocessor is the Loader, which checks to see if all of the new Class’ dependencies are available. If they are not, the Loader can dynamically load those dependencies before calling back to Ext.Class and allowing the next preprocessor to run. We’ll take another look at the Loader in another post.
After running the Loader, the new Class is set up to inherit from the declared superclass by the Extend preprocessor. The Mixins preprocessor takes care of copying all of the functions from each of our mixins, and the Config preprocessor handles the creation of the 4 config functions we saw last time (e.g. getTitle, setTitle, resetTitle, applyTitle – check out yesterday’s post to see how the Configs processor helps out).
Finally, the Statics preprocessor looks for any static functions that we set up on our new class and makes them available statically on the class. The processors that are run are completely customizable, and it’s easy to add custom processors at any point. Let’s take a look at that Statics preprocessor as an example:
//Each processor is passed three arguments - the class under construction,
//the configuration for that class and a callback function to call when the processor has finished
Ext.Class.registerPreprocessor('statics', function(cls, data, callback) {
if (Ext.isObject(data.statics)) {
var statics = data.statics,
name;
//here we just copy each static function onto the new Class
for (name in statics) {
if (statics.hasOwnProperty(name)) {
cls[name] = statics[name];
}
}
}
delete data.statics;
//Once the processor's work is done, we just call the callback function to kick off the next processor
if (callback) {
callback.call(this, cls, data);
}
});
//Changing the order that the preprocessors are called in is easy too - this is the default
Ext.Class.setDefaultPreprocessors(['extend', 'mixins', 'config', 'statics']);
What happens above is pretty straightforward. We’re registering a preprocessor called ’statics’ with Ext.Class. The function we provide is called whenever the ’statics’ preprocessor is invoked, and is passed the new Ext.Class instance, the configuration for that class, and a callback to call when the preprocessor has finished its work.
The actual work that this preprocessor does is trivial – it just looks to see if we declared a ’statics’ property in our class configuration and if so copies it onto the new class. For example, let’s say we want to create a static getNextId function on a class:
Ext.define('MyClass', {
statics: {
idSeed: 1000,
getNextId: function() {
return this.idSeed++;
}
}
});
Because of the Statics preprocessor, we can now call the function statically on the Class (e.g. without creating an instance of MyClass):
MyClass.getNextId(); //1000 MyClass.getNextId(); //1001 MyClass.getNextId(); //1002 ... etc
Finally, let’s come back to that callback at the bottom of the picture above. If we supply one, a callback function is run after all of the processors have run. At this point the new class is completely ready for use in your application. Here we create an instance of MyClass using the callback function, guaranteeing that the dependency on Ext.Window has been honored:
Ext.define('MyClass', {
extend: 'Ext.Window'
}, function() {
//this callback is called when MyClass is ready for use
var cls = new MyClass();
cls.setTitle('Everything is ready');
cls.show();
});
That’s it for today. Next time we’ll look at some of the new features in the part of Ext JS 4 that is closest to my heart – the data package.
Sencha Touch tech talk at Pivotal Labs
September 27, 2010 by Ed Spencer · 1 Comment
I recently gave an introduction to Sencha Touch talk up at Pivotal Labs in San Francisco. The guys at Pivotal were kind enough to record this short talk and share it with the world – it’s under 30 minutes and serves as a nice, short introduction to Sencha Touch:
The slides are available on slideshare and include the code snippets I presented. The Dribbble example used in the talk is very similar to the Kiva example that ships with the Sencha Touch SDK, so I recommend checking that out if you want to dive in further.
Using the Ext JS PivotGrid
July 29, 2010 by Ed Spencer · 6 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.
