Chapter 7. Getting Data Out

In the last chapter, we looked at how you can get data into a Sencha Touch data store. In this chapter, we will look at:

  • Using data stores for display

  • Binding, sorting, filtering, paging, and loading data stores

  • Working with XTemplates

  • Looping through data in an XTemplate

  • Conditional display and inline functions in XTemplates

  • Inline JavaScript and member functions in XTemplates

  • Using Sencha Touch Charts to display store data

Using data stores for display

Being able to store data in your application is only half the battle. You need to be able to easily get the data back out and present it in a meaningful way to the user. Lists, panels, and other data-capable components in Sencha Touch offer three configuration options to help you accomplish this task: store, data, and tpl.

Directly binding a store

Dataviews, lists, nested lists, form select fields, and index bars are all designed to display multiple data records. Each of these components can be configured with a data store from which to pull these records. We introduced this practice earlier on in the book:

new Ext.Application({
name: 'TouchStart',
launch: function() {

Ext.regModel('Contact', {
fields: [
      {name: 'first', type: 'string'},
      {name: 'last', type: 'string'},
      {name: 'admin', type: 'boolean'}
    ]
});

this.viewport = new Ext.Panel({
fullscreen: true,
layout: 'fit',
items: [
    {
xtype: 'list',
itemTpl: '{last}, {first}',
store: new Ext.data.Store({
model: 'Contact',
storeId: 'contactStore',
proxy: {
type: 'localstorage',
id: 'myContacts',
reader: {
type: 'json'
        }
       },
autoLoad: true
      })
    }]
});

}
});

The store configuration takes model, storeId, and proxy components as part of its setup. This will grab all of the store's data and pull it into the list for display. This is pretty familiar to us now, but what if we only want some of the data, or if we need the data in a specific order?

As it turns out, Sencha Touch stores can be sorted and filtered both when they are first created and later, if we need to change the filtering or sorting in response to the user.

Sorters and filters

Sorters and filters can be used in a number of ways. The first way is to set up a default configuration on the store as part of its creation.

var myStore = new Ext.data.Store({
model: 'Contact',
  storeId: 'contactstore',
sorters: [
        {
property : 'lastLogin',
direction: 'DESC'
        },
        {
property :'first',
direction: 'ASC'
        }
    ],

filters: [
        {
property: 'admin',
value: true
        }
    ]
});

Our sorters component is set as an array of property and direction values. These are executed in order, so our example sorts first by lastLogin (most recent first); within lastLogin, we sort by name (alphabetically ascending).

Our filters are listed as property and value pairs. In our example, we want the store to show us admin only. The store might actually store non-admins as well, but here we are requesting that those be filtered out initilly.

Sorters and filters can also be modified after the initial load-in by using one of the following methods:

  • clearFilter : Clears all filters on the store, giving you the full content of the store.

  • filter: Takes a filter object, just like the one in our previous configuration example, and uses it to limit the data as requested.

  • filterBy: Allows you to declare a function that is run on each item in the store. If your function returns true, the item is included. If it returns false, then the item is filtered out.

  • sort: Takes a sort object just like the ones in our configuration example and uses it to sort the data as requested.

If we use our previous example store, changing the sort order would look like this:

myStore.sort( {
property : 'last',
direction: 'ASC'
});

Filtering has to take into account any previous filters on the store. In our current store example, we are set to filter out anyone without an admin value of true. If we try the following code, we will not get back anything in the list, because we have effectively told the store to filter by both the new (admin = false) and previous (admin = tr e) filter:

myStore.filter( {
property : 'admin',
value: false
});

As admin is a Boolean value, we get back nothing. We have to clear out the old filter first:

myStore.clearFilter();
myStore.filter( {
property : 'admin',
value: false
});

This example will clear the old 'admin only' filter from the store and return a list of everyone who is not an admin.

Sorting and filters provide a powerful tool for manipulating data inside the data store. However, there are a few other situations we should also take a look at. What do you do when you have too much data, and what do you do when you need to reload the data store?

Paging a data store

In some cases, you will end up with more data than your application can comfortably manage in a single bite. For example, if you have an application with 300 contacts, the initial load-in time might be more than you really want. One way to handle this is with paging in the data store.

Paging allows us to grab the data in chunks and send the next or previous chunk of data, as the user needs it. We can set up paging using the pageSize configuration:

var myStore = new Ext.data.Store({
model: 'Contact',
storeId: 'contactStore',
proxy: {
type: 'localstorage',
id: 'myContacts',
reader: {
type: 'json'
        }
       },
autoLoad: true
      })

We can then move through the data using the paging functions:

myStore.nextPage();
myStore.PreviousPage();
myStore.loadPage(5);

This code moves forward one page, back one page, and then jumps to page five.

If we jump to page five and it doesn't exist, things will probably go poorly for our application (that is, it will go kaboom!). This means we need a good way to figure out how many pages we actually have, which means we need to know the total number of records in our data store.

We could try using the getCount() method for the data store, but this only returns the number of currently cached records in the store. Since we are paging through the data and not loading everything available, this would be the same as our maximum page size of 40. We need to set up our stores' reader to get this information.

We can set a configuration on the reader for totalProperty, such as this:

var myStore = new Ext.data.Store({
model: 'Contact',
storeId: 'contactStore',
    pageSize: 40,
proxy: {
type: 'localstorage',
id: 'myContacts',
reader: {
type: 'json'
        }
       },
autoLoad: true
      });

This tells our reader to look for an extra property, called totalContacts, in the data it collects. Our data that we pull into the store will also have to be set up to include this new property as part of the data string. How this is done will be determined largely by how your data is created and stored, but in a JSON data array, the format would look something like the following:

{
"totalContacts: 300,
  "contacts":[…]
}

All of our actual contacts would appear within the brackets, and the totalContacts property would be in the root of our array.

Once our data is set up in this fashion, we can grab the total contacts, as follows:

var total = myStore.getProxy().getReader().totalContacts

We can then divide by myStore.pageSize, to determine the total number of pages in our data. We can also grab the current page with myStore.currentPage. These two variables will allow us to display the users' current locations in the pages (that is, page five of eight).

One thing to be aware of, is that this total is not the number of records currently in the store. Instead, it is the total number of records available from the server. To find the total number of records in the store, you would use the following:

myStore.getCount();

Also, if you filter your stores' data, the number returned by getCount() will be the number of records that matched the filter, not the total number of records in the store.

Now, we need to account for what happens when the data behind our store changes.

Loading changes in a store

When we use a data store to pull from an external source, such as a file, a website, or a database, there is always the chance that the data will change at the external source. This will leave us with stale data in the store.

Fortunately, there is an easy way to deal with this using the load() function on the store. The load() function works as follows:

myStore.load({
scope: this,
callback: function(records, operation, success) {
console.log(records);
    }
});

The scope and callback functions are both optional. However, callback offers us an opportunity to do a number of interesting things, such as compare our old and new records, or alert the user visually once the new records are loaded.

Another consideration, when loading data stores, is whether to auto load the store as part of its creation or load it later. A good rule of thumb is to only auto load the data stores that you know will initially be displayed. Any subsequent stores can be set to load when the component they are bound to is shown.

For example, let's say we have a list of system users that will only be accessed occasionally within the program. We can add a listener to the component list itself, shown as follows:

listeners: {
show: {
fn: function(){ this.getStore().load(); }
  }
}

This code will load the store only if the list component is actually shown. Loading stores in this fashion saves us time when launching our application and saves memory.

We can also save time and memory by using the store to feed multiple components, such as a list of data and a details panel.

Data stores and panels

Unlike lists, where a number of records can be displayed, a panel typically displays a single record. However, we can still grab this information from our data store in the same way we do for a list.

Let's start with a variation of our contacts example from the beginning of the chapter; we will build a list of names using first and last, and then add a details panel that shows the full name, e-mail address, and phone number for the selected name.

We start with our model first:

Ext.regModel('Contact', {
fields: [
        {name: 'first', type: 'string'},
        {name: 'last', type: 'string'},
        {name: 'address', type: 'string'},
        {name: 'city', type: 'string'},
        {name: 'state', type: 'string'},
        {name: 'zip', type: 'string'},
        {name: 'email', type: 'string'},
        {name: 'birthday', type: 'date'}
    ]
});

This gives us our first and last values, which we will use for our initial list, and the email, birthday, and address information which we will use for the details.

Our list component stays basically the same as before. Since list uses the template itemTpl: '{last}, {first}', it simply ignores the values for address, city, state, zip, email, and birthday. However, since these values are still part of the data record, we can still grab them and use them in our panel to display details.

Before we can add our panel, we need to change our viewport method over to use a card layout. This will let us switch between the list and the details with a single tap:

this.viewport = new Ext.Panel({
fullscreen: true,
layout: 'card',
id: 'cardStack',
activeItem: 0,
items: [
    {
xtype: 'list',
itemTpl: '{last}, {first}',
store: new Ext.data.Store({
model: 'Contact',
storeId: 'contactStore',
proxy: {
type: 'ajax',
url: 'api/contacts.json',
reader: {
type: 'json',
root: 'children'
        }
       },
autoLoad: true
      })
    }]
});

In this code, we have changed our original example to set the viewport method to a card layout, with the activeItem component as 0. In this case, item 0 is our list. We also added an id , so we can grab the panel and change the active item later.

Sharp-eyed readers will also notice that we have changed our store to use AJAX as the proxy with a URL of api/contacts.json. This means that, when the store loads, it will look for a local file in the api folder, called contacts.json. This file will contain some test data we have thrown together, which looks something like the following:

{
  "children":[
    {
        "first":"Ila",
        "last":"Noel",
        "email":"[email protected]",
        "address":"754-6686 Elit, Rd.",
        "city":"Hunstanton",
        "state":"NY",
        "zip":34897,
        "birthday":"Tue, 16 Oct 1979 04:27:45 -0700"
    }, …
  ]
}

By setting this store to look at a local text file. This lets us add data quickly for testing, by adding additional new children to the text file.

Tip

Test data is your friend

Whenever you put together an application and test it, you will probably need some data in order to make sure things are working correctly. It's often very tedious to enter this information into a text file manually, or enter it in data forms over and over again. Fortunately, there is a website at http://www.generatedata.com/ that will generate random data in a number of formats. Just provide the field names and types, and then tell it how many records you need. Click the button, and you get back random data, ready for testing. Best of all, it's free.

Once you have your data and the new viewport set up, load the page to make sure things are working correctly.

Data stores and panels

Now, we need to add data in our detailsPanel component. Let's start simple for this first part and add a new panel item after our list:

{
xtype: 'panel',
id: 'detailsPanel',
tpl: '{first} {last}<br>{address}<br>{city}, {state} {zip}<br>{email}<br>{birthday}',
dockedItems: [{
xtype: 'toolbar',
dock: 'top',
items: [{
text: 'Back',
ui: 'back',
handler: function() {
Ext.getCmp('cardStack').setActiveItem(0);
}
}]
}]
}

Here, we just set id so we can grab the panel when we need to. We also add a simple template. We include some HTML line breaks to lay out the data better. Finally, we add a Back button which will bring us back to our main list.

The last thing we need to do is add a listener to our list to load the data into the panel:

listeners: {
itemTap: {
fn: function(list,index){
var record = list.getStore().getAt(index);
Ext.getCmp('detailsPanel').update(record.data);
Ext.getCmp('cardStack').setActiveItem(1);
  }
 }
}

The nice thing about this is that we don't really need to load anything new. The list already has access to all of the extra data through the data store. We just grab the store and use the index object that gets passed as part of our itemTap component. Then, we take the data from the record variable and pass it to the panel as part of the update function. Finally, we set the active item to our detailsPanel component. The result looks as follows, when we tap an item in the list:

Data stores and panels

The detailsPanel component includes not only the first and last name from our list, but the address, e-mail, and birthday data, as well. All of this data comes from the same data store; we simply use the templates to choose which pieces to display.

Speaking of templates, ours looks a little bit dull, and the birthday is a bit more specific than we really need. There must be something we can do to dress this up a bit.

XTemplates

As we have seen from a number of previous examples, the XTemplate is a structure that contains HTML layout information and placeholders for our data.

So far, we have only created very basic templates for our list and panel, using the data values and a bit of HTML. We have also created them as part of the component itself, as a single string. This could become unwieldy very quickly. However, we can also set these templates up as separate components:

var myTemplate = new Ext.XTemplate(
  '{first} {last}<br>',
  '{address}<br>',
  '{city}, {state} {zip}<br>',
  '{email}<br>',
  '{birthday}'
);

This would create a template that looks exactly like what we had before. It's just a lot easier to read and maintain in this configuration. Here, we can have as many lines as we want, enclosed in quotes and separated by commas.

We can then add it to our panel with tpl: myTemplate.

This allows us to easily create something a bit more pretty than our old template:

var myTemplate = new Ext.XTemplate(
  '<div style="padding:10px;"><b>{first} {last}</b><br>',
  '{address}<br>',
  '{city}, {state} {zip}<br>',
  '<a href="mailto:{email}">{email}</a><br>',
  '{birthday}</div>'
);

This makes our display look a bit better.

XTemplates

We can also use these same types of XTemplates with our main list to give it a bit more style. For example, adding the following as the itemTpl component for our list will place an adorable kitten picture nxt to each name in the list:

var listTemplate = new Ext.XTemplate(
    '<div class="contact-wrap" id="{first}-{last}">',
    '<div class="thumb" style= "float: left;"><img src="http://placekitten.com/36/36" title="{first}"></div>',
    '<span class="contact-name">{first} {last}</span></div>'
);

For this example, we just added some HTML to lay out each line of data and then used a random image generation service to place a 36 x 36 random kitten picture, which will line up next to our names on the left. (You can also use this to display the contact's picture).

XTemplates

At this point, we are still just playing with basic HTML, but XTemplates aremuch more powerful than that.

Data manipulation

Since XTemplates are components in Sencha Touch, they allow us to directly manipulate the data within the template in a number of ways. The first thing we can do is clean up that ugly birthday.

Since the birthday is listed in our model as being a date object, we can treat it like one, in the template. We can replace the current birthday line of our template with the following:

  'Birthday: {birthday:date("n/j/Y")}</div>'

This will use our value of birthday and the format function date. date uses the string "n/j/Y" to convert birthday into a more readable format. These format strings can be found on the date page of the Sencha Touch API.

Data manipulation

Sencha Touch includes a number of formatting functions that can be used in this fashion. Some of the functions include:

  • date: Formats a date object using the specified formatting string (the format strings can be found on the date page of the Sencha Touch API).

  • ellipsis: Truncates the string to a specified length and adds to the end (note that the is considered to be part of the total length).

  • htmlEncode and htmlDecode: Converts HTML characters (&, <, >, and ') to and from HTML.

  • leftPad: Pads the left side of the string with a specified character (good for padding numbers with leading zeros).

  • toggle: A utility function that switches between two alternating values.

  • trim: Removes any white space from the beginning and end of the string. It leaves spaces within the string intact.

The basic functions can be used inside the HTML of our XTemplate to format our data. However, the XTemplate has a few additional tricks up its sleeve.

Looping through data

In a list view, the XTemplate for the itemTpl component is automatically applied to each item in the list. However, you can also loop through your data manually, using the following syntax:

'<tpl for=".">',
'{name}</br>',
'</tpl>' 

When you use the <tpl> tag, it tells the XTemplate we are exiting the realm of HTML and making some decisions within the template. In this case, <tpl for="."> tells the code to start a loop and use the root node of our data. The closing </tpl> tells the loop to stop.

Since we can have complex nested data with both XML and JSON, it can also be helpful to loop the data in places besides the root node. For example, let's say we have an array of states, and each state contains an array of cities. We could loop through this data as follows:

'<tpl for=".">',
'{name}</br>',

'<tpl for="cities">',
'{name}</br>',
'</tpl>' 

'</tpl>' 

Our first <tpl> tag begins looping through our states, printing the name. After the name is printed, it looks for a child array within the individual state, called cities.

This time, when we use the variable {name}, it's inside of our child loop, so it prints the name of each city in the state before moving on to the next state in the loop.

Note

Notice that, when we use a field name inside our <tpl> tags, we do not use the curly braces like this: {cities}. Since we are outside of the HTML piece of our template, Sencha Touch assumes "cities" is a variable.

We can even access an array nested in each city, for example postal codes, by adding another loop:

'<tpl for=".">',
'{name}</br>',

'<tpl for="cities">',
'{name}</br>',

'<tpl for="cities.postal">',
'{code}</br>',
'</tpl>' 

'</tpl>' 

'</tpl>' 

In this case, we have used <tpl for="cities.postal"> to indicate that we will loop through the postal codes data array within the cities data array. Our other array loops execute as before.

Numbering within the loop

When you are working inside a loop, it's often helpful to be able to count the cycles in the loop. You can do this by using {#} in your XTemplate:

'<tpl for=".">',
'{#} {name}</br>',
'</tpl>' 

This will print the current loop number next to each name in the loop. This would work in a similar fashion for nested data:

'<tpl for=".">',
'{#} {name}</br>',

'<tpl for="cities">',
'{#} {name}</br>',
'</tpl>' 

'</tpl>' 

The first {#} will display where we are in the main loop and the second {#} will display where we are in the cities loop.

Parent data in the loop

In cases where we have nested data, it can also be helpful to be able to get to the parent properties from within the child loop. You can do this by using the parent object. Using our nested example with states, cities, and counties, this would look as follows:

'<tpl for=".">',
'{name}</br>',

'<tpl for="cities">',
'{parent.name} - {name}</br>',

'<tpl for="cities.postal">',
'{parent.name} - {code}</br>',
'</tpl>' 


'</tpl>' 

'</tpl>' 

While inside our cities loop, {parent.name} would display the state name for that city. When we are inside our cities.postal loop, {parent.name} would display the city name associated with that postal code.

Using this {parent.fieldname} syntax, we can get to any of the parent's values from within the current child item.

Conditional display

In addition to looping, XTemplates offer some limited conditional logic for use in your template. This is limited because, instead of the familiar programming concept of if…else…then, Sencha Touch only offers if…then. For example, we could use the if statement in our states and cities to only display cities with a population above 2,000:

'<tpl for=".">',
  '{name}</br>',
  '<tpl for="cities">',
    '<tpl if="population &gt; 2000">',
      '{name}</br>',
    '</tpl>',
  '</tpl>',
'</tpl>'

If we wanted to color code our cites based on whether they are over or under our population target, then we couldn't use if… else… then. We would have to do it as two opposite if statements:

'<tpl for=".">',
  '{name}</br>',
  '<tpl for="cities">',
    '<tpl if="population &gt; 2000">',
      '<div class="blue">{name}</div>',
    '</tpl>',
    '<tpl if="population &lt; 2000">',
      '<div class="red">{name}</div>',
    '</tpl>',
  '</tpl>',
'</tpl>'
Conditional display

Now, you are probably already asking yourself why we are using &gt; and &lt; instead of > and <. The reason is because anything in our conditional statement needs to be HTML-encoded, in order for the XTemplate to correctly parse it. This can be a bit confusing at first, but the key things to remember are as follows:

  • Use &gt; instead of >.

  • Use &lt; instead of <.

  • Use equals as normal == However, if you are comparing a string value, you have to escape the single quotes such as this: '<tpl if="state == 'PA'">'.

  • You will need to encode ", if it is part of your conditional. So if you are searching for the word "spam" including the quotes, you would have to encode this as&quot;spam&quot;.

Arithmetic

In addition to conditional logic, the XTemplates also support basic math functionality for the following:

  • Addition (+)

  • Subtraction (-)

  • Multiplication (*)

  • Division (/)

  • Modulus—the remainder of one number divided by another (%)

For example:

'<tpl for=".">',
  '{name}</br>',
  '<tpl for="cities">',
      '{name}</br>',
  'Population: {population}</br>',
  'Projected Population for next year: {population * 1.15}</br>',
  '</tpl>',
'</tpl>'

This would give us our initial population value followed by a projected population of 1.15 times the current population. The math functions are included within the curly braces around our variable.

Inline JavaScript

We can also execute arbitrary inline code as part of our Xtemplate. We can do this by placing the code within a combination of brackets and curly braces: {[…]}. There are also a few special attributes we can access within this code:

  • values: The values in the current scope

  • parent: The values of the current parent object

  • xindex: The current index of the loop you are on

  • xcount: The total number of items in the current loop

For example, we can make sure our state and city names are uppercase, and alternate colors on our list of cities with the following XTemplate:

'<tpl for=".">',
  '{[values.name.toUpperCase()]}</br>',
  '<tpl for="cities">',
  '<div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
      '{[values.name.toUpperCase()]}</br>',
  '</div>',
  '</tpl>',
'</tpl>'

In this case, we use {[values.name.toUpperCase()]} to force the name of the state and the city to be uppercase. We also use {[xindex % 2 === 0 ? "even" : "odd"]} to alternate our row colors, based on the remainder of the current count divided by 2 (the modulus).

Even with the ability to write inline JavaScript, there are a number of cases where you might require something a bit more robust. This is where the XTemplate member functions come into play.

XTemplate member functions

An XTemplate member function allows you to attach a JavaScript function to your XTemplate and then execute it inside the template by calling this.function_name.

The functions are added to the end of the template and a template can include multiple member functions. These member functions are wrapped in a set of curly braces in a fashion similar to listeners:

{
myTemplateFunction: function(myVariable) {
  …
 },
myOtherTemplateFunction: function() {
  …
 }
}

We can use these member functions to make up for the lack of a native if…then…else option within the template. Let's use our previous states and cities example, and expand our color coding a bit.

'<tpl for=".">',
  '{name}</br>',
  '<tpl for="cities">',
      '<div class="{[this.setPopulationStyle(values.population)]}">{name}</div>',
    '</tpl>',
  '</tpl>',
'</tpl>',
{
setPopulationStyle: function(population) {
if(population >= 4000) {
return 'red';
  } else if(population <= 3999 && population >= 2000) {
return 'orange';
  } else if(population <= 1999&& population >= 1000) {
return 'blue';
  } else {
return 'grey';
  }
 }
}

For this example, we have created a member function called setPopulationStyle, that we pass our population variable into. As our function can execute any JavaScript we desire, we can use our if…then…else logic here, setting a class for our population in the template. We can then call the function inside our template with {[this.setPopulationStyle(values.population)]}, which will print out our class name, based on the value of our population.

XTemplate member functions

We can also use our member functions to help us test for the presence or absence of data. This comes in very handy for controlling your template. For example, let's start with a contacts template with a name, address, and e-mail, such as the following:

var myTemplate = new Ext.XTemplate(
  '<div style="padding:10px;"><b>{first} {last}</b><br>',
  '{address}<br>',
  '{city}, {state} {zip}<br>',
  '<a href="mailto:{email}">{email}</a><br>',
  'Birthday: {birthday:date("n/j/Y")}</div>'
);

If we have no data for the address, city, and state, we will end up with some empty lines and a stray comma. Since our zip variable is an integer according to our model, it will show up as 0 if we don't have a value stored for it.

XTemplate member functions

We need a way to check and see if we have data for these items before we print it out.

isEmpty

As it turns out, native JavaScript is very problematic when it comes to detecting an empty value. Depending on the function, JavaScript might return the following:

  • null

  • undefined

  • an empty array

  • an empty string

For most of us, these are pretty much the same thing; we didn't get back anything. However, to JavaScript, these return values are very different. If we try to test for data with if(myVar == '') and we get back null, undefined, or an empty array, JavaScript will return false.

Fortunately, Sencha Touch has a handy little function called isEmpty(). This function will test for null, undefined, empty arrays, and empty strings, all in one function. However, Sencha Touch does not have an opposite function for has data, which is what we really want to test for. Thanks to template member functions, we can write our own.

var myTemplate = new Ext.XTemplate(
  '<div style="padding:10px;"><b>{first} {last}</b><br>',
  '<tpl if="!Ext.isEmpty(address)">',
  '{address}<br>',
  '{city}, {state} {zip}<br>',
  '</tpl>',
  '<a href="mailto:{email}">{email}</a><br>',
  'Birthday: {birthday:date("n/j/Y")}</div>'

We don't even need a member function for this data check. We can add <tpl if="!Ext.isEmpty(address)"> to our template and check for the address in line with our template. The Ext.isEmptyfunction class takes the address data and checks to make sure it is not ! (empty). If the address is not empty, we print the address and if it is empty, we do nothing.

Changing a panel's content with XTemplate.overwrite

In our previous examples, we have declared our XTemplate as part of our panel or list, using tpl or itemtpl. However, it can also be helpful to overwrite a template programmatically, after the list or panel is displayed. You can do this by declaring a new template and then using the panel's (or list's) overwrite command to combine the template and the data, and overwrite the content area of your panel or list.

var myTemplate = new Ext.XTemplate(
'<tpl for=".">',
  '{name}</br>',
  '<tpl for="cities">',
      '- {name}<br>',
    '</tpl>',
  '</tpl>',
'</tpl>'
);

myTemplate.overwrite(panel.body, data);

Our overwrite function takes an element (Ext or HTML) as the first argument. So, instead of just using panel, we need to use the body element of the panel as panel.body. We can then supply a record from a data store or an array of values, as our second argument for the new template to use.

While XTemplates are an extremely powerful way to display our data, they are still very text heavy. What if we want to display data as something a bit more colorful? Let's take a look at Sencha Touch Charts.

Sencha Touch Charts

So far, we have only looked at data stores and records as a way to display text data, but with the release of Sencha Touch Charts, we are a now able to display complex graphical data as part of our applications.

Sencha Touch Charts

These new components use data stores to display a wide range of chart and graph types, including the following:

  • pie

  • bar

  • line

  • scatter

  • series

  • treemap

  • worldmap

While a full exploration of the chart components would be worthy of a book by itself, we want to provide an overview of how these components interact with the data store and, hopefully, peak your curiosity.

Installing Touch Charts

Sencha Touch Charts are a separate download from the main Sencha Touch framework, and you can find them at http://www.sencha.com/products/touch/charts/.

You will need to unzip the touch-charts folder and move it into your folder, much in the same way we set up our Sencha Touch framework in Chapter 2, Creating a Simple Application. You will also need to include the touch-charts-debug.js and touch-charts-demo.css files in your main index.html file (both files are in the touch-charts folder). Follow the previous instructions in Chapter 2, Creating a Simple Application, for including JavaScript files.

A simple pie chart

Once the files are included, we can start a new JavaScript file for our Charts example. We will start with a data store:

Ext.setup({
onReady: function() {
var mystore = new Ext.data.JsonStore({
fields: ['month', 'sales'],
data: [
        {'month': 'June', 'sales': 500},
        {'month': 'July', 'sales': 350},
        {'month': 'August', 'sales': 200},
        {'month': 'September', 'sales': 770},
        {'month': 'October', 'sales': 170}
      ]
    });
 }
});

Our store declares two field types, month and sales, and our data array holds five sets of month and sales values. This will feed into our pie chart:

var chartPanel = new Ext.chart.Panel({
title: 'Pie Chart',
fullscreen: true,
items: {
cls: 'pie1',
theme: 'Demo',
store: mystore,
insetPadding: 20,
legend: {
position: {
portrait: 'bottom',
landscape: 'left'
              }
          },
series: [{
type: 'pie',
field: 'sales',
showInLegend: true,
label: {
field: 'month'
              }
          }]
        }
    });

Much like our other panel components, an Ext.chart.Panel class takes configurations for title and fullscreen. It also takes a list of items to include in the body of the panel. In the case of a chart panel, this item will be a single chart component.

The chart component takes configurations options for cls (a CSS class), a theme, a data store component, and insetPadding component to keep the chart from bumping up against the top and sides of the page.

Next, we have a configuration for our legend chart. This provides a color-coded reference for all of our chart values. We can use a position configuration to designate how the legend should appear in both portrait and landscape modes.

The final piece is our series configuration. In our example, we have set the type of chart we will see, which field the chart uses to draw the pie slices, whether to show the legend or not, and lastly, the label we will use for our legend.

Tip

The series configuration

The series configuration controls most of the look and feel of our charts: shadows, animation handling, gradients, as well as the actual type of chart we want (pie, column, bar, scatter, and so on). A series will be composed of an array of items that control the positioning of each of our chart elements as well as the value from our store that we are using for that element. This also means that each type of chart will have a slightly different set of requirements and options for its series data. Consult the API Drawing and Charting documentation to see examples of the different types of series configurations: http://docs.sencha.com/touch-charts/1-0/#!/guide/drawing_and_charting.

When we load it all up, our chart looks as follows:

A simple pie chart

If you click on any of the months on the legend, you can turn them on and off in the chart. This functionality happens automatically, without any additional code.

A pie chart works well for very simple, single-series data, but what happens if we have data for several years? Let's see how a bar chart might work to display this kind of data.

A bar chart

For our bar chart, let's replace our chart data store with this one:

var mystore = new Ext.data.JsonStore({
fields: ['month', 'data'],
data: [
  {'month': 'June', '2008': 500, '2009': 400, '2010': 570},
  {'month': 'July', '2008': 350, '2009': 430, '2010': 270},
  {'month': 'August', '2008': 200, '2009': 300, '2010': 320},
  {'month': 'September', '2008': 770, '2009': 390, '2010': 670},
  {'month': 'October', '2008': 170, '2009': 220, '2010': 360}
 ]
});

This data set has multiple series of data we need to display (five months, with three years for each month). An effective bar chart will need to display a row for each month and separate bars within the month for each of our years.

We can start by changing the title of our chart panel to bar chart. Then, we can replace our chart items, as follows:

items: {
cls: 'bar1',
theme: 'Demo',
store: mystore,
animate: true,
legend: {
position: {
portrait: 'right',
landscape: 'top'
      },
labelFont: '17px Arial'
  },
axes: [{
type: 'Numeric',
position: 'bottom',
fields: ['2008', '2009', '2010'],
title: 'Sales',
minimum: 0
  },
  {
type: 'Category',
position: 'left',
fields: ['month'],
title: 'Month of the Year'
  }],
series: [{
type: 'bar',
xField: 'month',
yField: ['2008', '2009', '2010'],
axis: 'bottom',
showInLegend: true
  }]
}

Like our pie chart, the bar chart component takes configurations options for cls (a CSS class), a theme, a data store, and new option called animate. This option will make our bars animate as we turn on and off different items in the legend.

We then have our legend as before, followed by a new configuration option called axes. Since a bar chart operates along an X and a Y axis, we need to specify which of our data points should feed each axis.

First up is our sales data for each year. The data is numeric, positioned along the bottom and given a title of sales. We also specify the fields that will be used for this access and what our minimum value should be (this is the number that will appear on the far left of our bar chart and will usually be zero).

The next axis is our category data (which will also be used for our legend). In this case, our position is left, our field is month, and our title is Month of the Year. This closes out our axes configuration.

Finally, we have our series configuration, which sets this up as a bar graph. Unlike our previous pie chart example, which only tracked sales data, the bar chart is tracking sales data for two separate points (month and year), so we need to assign our xField and yField variables and declare an axis location. This location should match the axis where you are displaying numerical data (in our case, the data is on the Y axis, which is on the bottom). We close out by using showInLegend to display our legend.

The final chart should look as follows:

A bar chart

Note

Charts are an incredibly robust way to use stores to display data, and we don't really have time to go through them all here, but you can explore all of the capabilities of Sencha Charts at http://www.sencha.com/products/touch/charts/.

You can also find examples of each kind of chart at http://dev.sencha.com/deploy/touch-charts-1.0.0/examples/. Remember that you need to view these examples with a WebKit browser (Safari or Chrome).

The full Touch Charts API documentation is available at http://docs.sencha.com/touch-charts/1-0/.

Summary

In this chapter, we have explored the way data stores can be used to display both simple and complex data. We talked about binding, sorting, paging, and loading data stores. We then walked through using data stores with both lists and panels.

We covered how to lay out your application by using XTemplates to control how the data from stores and records will appear. We explored how to manipulate and loop through our data inside an XTemplate as well as how to use conditional logic, arithmetic, and inline JavaScript. We finished up our conversation on XTemplates by discussing member functions and some of their uses.

We closed out our chapter with a look at using the Sencha Touch Charts package to display our store data graphically.

In our next chapter, we will explore putting all of the information from our previous chapters together into a full-scale application.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset