Chapter 6. Getting Data In

One of the key aspects of any application is the handling of data—getting data into the application so that you can manipulate and store it, and then getting it out again for display. We will spend the next two chapters covering data handling in Sencha Touch. This first chapter on data will focus on getting data into your application.

We will start with a discussion of the data models that are used to describe your data. We will then talk about readers that gather the data and the stores used to hold the data for use in our application. Once we have a grasp on where the data goes, we will cover how to use forms to get it there. We will look at how to validate your data and provide you with some examples of form submission. We will finish up with a look at getting the data back into a form for editing. This will serve as our starting point for the next chapter on data, which will cover getting data back for display.

This chapter covers the following topics:

  • Data models

  • Data formats

  • Data stores

  • Using forms and data stores

Models

The first step in working with data in a Sencha Touch application is to create a model of the data. If you are used to database-driven applications, it's helpful to think of the model as being a database schema: it's a construct that defines the data we are going to store, including the datatype, validations, and structure. This provides the rest of our application a common map for understanding the data being passed back and forth.

The basic model

At its most basic, the model describes the data fields using Ext.regModel(), such as:

Ext.regModel('User', {
  fields: [
    {name: 'firstname', type: 'string'},
    {name: 'lastname', type: 'string'},
    {name: 'username', type: 'string'},
    {name: 'age', type: 'int'},
    {name: 'email', type: 'string'},
    {name: 'active', type: 'boolean', defaultValue: true},
  ]
}

The first line declares that we have named our new model User. We then describe our data fields as an array of fields with a name, a type, and an optional default value. The name is simply how we want to refer to the data in our code. The valid datatypes are:

  • auto: A default value that just accepts the raw data without conversion

  • string: Converts the data into a string

  • int: Converts the data into an integer

  • float: Converts the data into a floating point integer

  • boolean : Converts the data into a true or false Boolean value

  • date: Converts the data into a JavaScript Date object

The default value can be used to set a standard value to be used, if no data is received for that field. In our example, we set the value of active to true. We can use this when creating a new user instance with Ext.ModelMgr.create():

var newUser = Ext.ModelMgr.create({
  firstname: 'Nigel',
  lastname: 'Tufnel',
  username: 'goes211',
  age: 39,
  email: '[email protected]'
}, 'User'),

Notice that we did not provide a value for active in our new user instance, so it just uses our default value. This can also help when the user doesn't remember to enter in a value. We can also double-check the information our user enters by using validations.

Model validations

Model validations ensure that we are getting the data we think we are getting. These validations serve two functions. The first is to provide the guidelines for how data is entered. For example, we would typically want a username to consist only of letters and numbers; the validation can enforce this constraint and inform the user when they use the wrong character.

The second is security. Malicious users can also use the form field to send information that might potentially be harmful to our database. For example, sending DELETE * FROM users; as your username can cause problems if the database is not properly secured. It is always a good idea to validate data, just in case.

We can declare validations as part of our data model, in much the same way that we declare our fields. For example, we can add the following to our User model:

Ext.regModel('User', {
  fields: [
    {name: 'firstname', type: 'string'},
    {name: 'lastname', type: 'string'},
    {name: 'age', type: 'int'},
    {name: 'username', type: 'string'},
    {name: 'email', type: 'string'},
    {name: 'active', type: 'boolean', defaultValue: true},
  ],
  validations: [
    {type: 'presence',  field: 'age'},
    {type: 'exclusion', field: 'username', list: ['Admin', 'Root']},
     {type: 'length', field: 'username', min: 3},
    {type: 'format', field: 'username', matcher: /([a-z]+)[0-9]{2,3}/}
  ]
}

In our example, we have added four validations. The first one tests for the presence of an age value. If there is no value for age, we get an error. The second validator, exclusion, tests for things we don't want to see as a value for this field. In this case, we have a list of two items for username that we don't want to see: Admin and Root. The third validator tests to make sure that our value for username is at least three characters long. The final validator checks the format of our username using a regular expression.

Tip

Regular expressions

Regular expressions (also called RegEx) are an extremely powerful tool for matching the structure of a string. You can use RegEx to search for particular characters, words, or patterns, within a string. A discussion of regular expressions would require its own book, but there are a number of good online resources available.

We can test our validations by using the validate method on our new User instance:

var newUser = Ext.ModelMgr.create({
  firstname: 'Nigel',
  lastname: 'Tufnel',
  username: 'goes211',
  email: '[email protected]'
}, 'User'),

var errors = newUser.validate();
console.log(errors);

Notice that we intentionally dropped the age off this time, to give us an error. If we take a look at our console, we can see the error object that we get back:

Model validations

This is the console output for our errors object. The errors object includes a method called isValid() , which will return true or false. We can use this method to test for errors and return a message to the user, using something as follows:

  if(!errors.isValid()) {
    alert("The field: "+errors.items[0].field+ " returned an error: "+errors.items[0].message);
  }

Here, we test for the length of our errors object. This will be zero if there are no errors. In this case, our errors object has a length of 1, so we grab the field that returned the error and the message it generated. These are included in the items list of the errors object. If there were more than one error, we would need to loop through the items list, to grab al of the errors.

We can also change the default error message by setting additional configuration options on the validations for:

  • exclusionMessage: Used when we get an excluded value for a field

  • formatMessage: Used when we get an improperly formatted value for a field

  • inclusionMessage: Used when we do not get an included value for a field

  • lengthMessage: Used when we get a value for a field that does not meet our required length

  • presenceMessage: Used when we do not reserve a required value for a field

Customizing these errors will help the user understand exactly what went wrong and what needs to be done to correct the problem.

Model methods

Our models can also contain methods that can be called on any instance of our model. For example, we can add a method called deactivate to our model, by adding the following to our User model, after the fields list:

deactivate: function() {
 if(this.get('active')) {
  this.set('active', false);
 }
}

This function tests to see if our current value of active is true. If it is, we set it to false. Once we create our newUser, as we did previously, we can then call the function as follows:

newUser.deactivate();

These model methods provide a great way to implement common functions in your model.

Tip

CRUD

While model methods might look like a good place for adding functions to save our model, you really don't need to. These types of functions—Create , Read , Update , and Destroy—are often referred to by the unattractive acronym CRUD, and they are handled automatically by Sencha Touch. We will go over these functions a bit later in this chapter.

Now that we have our model's fields, validations, and functions defined, we need a way to pass data to and from the model for the storing and retrieving of our users. This is where the proxy and reader come in.

Proxies and readers

In the model, the proxy and reader form a partnership to store and retrieve data for use by the model. The proxy tells a model where its data will be stored, and the reader tells the model what format is being used to store the data.

There are two main types of proxies: local and remote. A local proxy stores its data locally on the device with one of three proxy types:

  • LocalStorageProxy - Saves the data to local storage via the browser. This data is persistent across sessions, unless deleted by the user.

  • SessionsStorageProxy - Saves its data to session storage via the browser. This data is removed when the session ends.

  • MemoryProxy - This holds the data in local memory. When the page is refreshed, the data is deleted.

The remote proxy has two basic types:

  • AjaxProxy: Sends requests to a server within the current domain

  • ScriptTagProxy: Sends requests to a server on a different domain

For this chapter and the next, we will be dealing mostly with local proxies. We will cover remote proxies and synchronizing data in Chapter 9, Advanced Topics.

The proxy can be declared as part of the model, shown as follows:

proxy: {
  type: 'localstorage'
  id: 'userProxy',
}

All proxies require a type (local storage, session storage, and so on.), and some require a unique ID, so it's a good idea to just get into the habit of giving all of your proxies an ID.

We can also add a reader to this proxy configuration. The reader's job is to tell our proxy which format to use for sending and receiving data. The reader understands the following formats:

  • Array: A simple JavaScript array.

  • XML: Extensible Markup Language format.

  • JSON: JavaScript Object Notation format.

  • JSONP: JSON with padding. Typically used for communication with a remote server.

The reader gets declared as part of the proxy:

proxy: {
  type: 'localstorage',
  id: 'userProxy',
  reader: {
    type: 'json'
  }
}

Tip

Proxies and readers

Please note that the proxies and readers can also be declared as part of the data store and should ideally be declared in both places.

Introduction to data formats

Before we move on to data stores, we need to take a brief look at data formats. The three currently supported by Sencha Touch are Array, XML, and JSON. For each example, we will take a look at how the data would appear for a simple contact model with three fields: an ID, a name, and an e-mail.

Arrays

An ArrayStore data format uses a standard JavaScript array, which would look something such as this, for our contact example:

[ 
  [1, 'David', '[email protected]'],
  [2, 'Nancy', '[email protected]'],
  [3, 'Henry', '[email protected]']
]

One of the first things we notice about this type of array is that there are no field names included as part of a JavaScript array. This means if we want to refer to the fields by name in our template, we have to set up our model to understand where these fields should be mapped, by using the mapping configuration option:

Ext.regModel('Contact', {
    fields: [
        'id',
        {name: 'name', mapping: 1},
        {name: 'email', mapping: 2}
    ],
    proxy: {
      type: 'memory',
      reader: {
        type: 'array'
      }
    }
});

This sets up our id field as index 0 of our data, which is our default. We then use the mapping configuration to set name and email as index 1 and 2, respectively, of the items in our data array. We can then set the template values for the display component using the configuration:

itemTpl: '{name}: {email}'

While arrays are typically used for simple data sets, a larger or nested data set can become very unwieldy using the simple JavaScript array structure. This is where our other formats come in.

XML

XML or Extensible Markup Language should be a familiar looking format to anyone who has worked with HTML web pages in the past. XML consists of data nested within a series of tags that identify the name of each part of the dataset. If we put our previous example into XML format, it would look as follows:

<?xml version="1.0" encoding="UTF-8"?>
<contact>
<id>1</id>
<name>David</name>
<email>[email protected]</email>
</contact>
<contact>
<id>2</id>
<name>Nancy</name>
<email>[email protected]</email>
</contact>
<contact>
<id>3</id>
<name>Henry</name>
<email>[email protected]</email>
</contact>

Notice that XML always begins with a version and encoding line. If this line is not set, the browser will not interpret the XML correctly and the request will fail.

We also include tags for defining the individual contacts. One advantage of this is that we can now nest data as shown:

<?xml version="1.0" encoding="UTF-8"?>
<total>25</total>
<success>true</success>
<contacts>
<contact>
<id>1</id>
<name>David</name>
<email>[email protected]</email>
</contact>
<contact>
<id>2</id>
<name>Nancy</name>
<email>[email protected]</email>
</contact>
<contact>
<id>3</id>
<name>Henry</name>
<email>[email protected]</email>
</contact>
</contacts>

In this nested example, we have each individual contact tag nested inside a contacts tag. We also have tags for our total and success values.

Since we have a nested data structure, we will also need to let the reader know where to look for the pieces we need.

reader: {
    type: 'xml',
    root: 'contacts',
    totalProperty  : 'total',
    successProperty: 'success'
}

The root property tells the reader where to start looking for our individual contacts. We also set a value outside of our contacts list for totalProperty. This tells the store that there are a total of 25 contacts, even though the store only receives the first three. The totalProperty property is used for paging through the data (that is, showing three of 25).

The other property outside of our contacts list is successProperty. This tells the store where to look to see if the request was successful.

The only disadvantage of XML is that it's not a native JavaScript format, so it adds a little bit of overhead when it's parsed by the system. Typically, this is only noticeable in very large or deeply nested arrays, but it can be an issue for some applications.

Fortunately for us, we can also use JSON.

JSON

JSON or JavaScript Object Notation has all of the advantages of XML, but as a native JavaScript construct, it has less overhead associated with parsing. If we look at our data set as JSON, we would see the following:

[
  {
    "id": 1,
    "name": "David",
    "email": "[email protected]"
  },
  {
    "id": 2,
    "name": "Nancy",
    "email": "[email protected]"
  },
  {
    "id": 3,
    "name": "Henry",
    "email": "[email protected]"
  }
]

We can als nest JSON in much the same way we do with XML:

{ 
  "total": 25,
  "success": true,
  "contacts": [
   {
    "id": 1,
    "name": "David",
    "email": "[email protected]"
   },
   {
    "id": 2,
    "name": "Nancy",
    "email": "[email protected]"
   },
   {
    "id": 3,
    "name": "Henry",
    "email": "[email protected]"
   }
  ]
}

The reader would then be set up just as our XML reader, but with the type listed as JSON:

reader: {
    type: 'json',
    root: 'contacts',
    totalProperty  : 'total',
    successProperty: 'success'
}

As before, we set properties for both totalProperty and successProperty. We also provide the reader with a place to start looking for our contacts list.

JSONP

JSON also has an alternate format called JSONP, or JSON with padding. This format is used when you need to retrieve data from a remote server. We need this option because most browsers follow a strict same origin policy when handling JavaScript requests.

The same origin policy means that a web browser will permit JavaScript on the page to run as long as the JavaScript is running on the same server as the web page. This will prevent a number of potential JavaScript security issues.

However, there are times when you will have a legitimate reason for making a request from a remote server, say querying an API from a web service such as Flickr. Because your app isn't likely to be running on flickr.com, you'll need to use JSONP, which simply tells the remote server to encapsulate the JSON response in a function call.

Luckily, Sencha Touch handles all of that for us. When you set up your proxy and reader, set the proxy type to scripttag, and set your reader up like you would a regular JSON reader. This tells Sencha Touch to use Ext.data.ScriptTagProxy to do the cross-domain request, and Sencha Touch takes care of the rest.

Note

If you'd like to see JSONP and Ext.data.ScriptTagProxy in action, we use both to build the Flickr Finder application in Chapter 8, The Flickr Finder Application.

While we have a number of formats to choose from; we will be using the JSON format for all of our examples, moving forward, as we talk about data stores.

Introduction to stores

Stores, as the name implies, are used to store data. As we have seen in previous chapters, list components require a store in order to display data, but we can also use a store to grab information from forms and hold it for use anywhere in our application.

The store, in combination with the model and proxy, works in much the same way as a traditional database. The model provides the structure for our data (say a schema in a traditional database), and the proxy provides the communication layer to get the data in and out of the store. The store itself holds the data and provides a powerful component interface for sorting, filtering, saving, and editing data.

The store can also be bound to a number of components, such as lists, nested lists, select fields, and panels, to provide data for display.

We will cover display, sorting, and filtering in Chapter 7, Getting Data Out, but for now, we are going to look at saving and editing data with the store.

A simple store

As this chapter is concerned with getting data into the store, we are going to start out with a very simple local store for our example:

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

This example sets up the model for the store and then tells the proxy to store all the data as part of HTML5's local storage capability. We also set the store to autoLoad, which means that it will load the data as soon as the store is created.

We also need to set up our model correctly, in order to use this store. Even though we have the proxy listed as part of the store, it's a good idea to have it on the model as well. There will be times where we need to directly manipulate (update) the model, without getting the store first:

Ext.regModel('Contact', {
    fields: [
        {name: 'id', type:'int'},
        {name: 'name', type: 'string'},
        {name: 'email',  type: 'string'}
    ],
    proxy: {
        type: 'localstorage',
        id: 'myContacts',
        reader: {
          type: 'json'
        }
    },

});

This is our simple model with three items: an ID, a name, and an e-mail. We would then create a new contact as we did before:

  var newContact = Ext.ModelMgr.create({
    name: 'David',
    email: '[email protected]'
  }, 'Contact'),

Notice that we don't set the ID this time. We want the store to set that for us (similar to the way auto-increment works in a typical database). We can then add this new contact to the store and save it as this:

var addedUser = contactStore.add(newContact);
contactStore.sync();

The first line adds the user to the store, and the second line saves the contents of the store. By splitting out the add and sync functionalities, you can add multiple users to the store and then perform a single save, as the following:

  var newContact1 = Ext.ModelMgr.create({
    name: 'David',
    email: '[email protected]'
  }, 'Contact'),

  var newContact2 = Ext.ModelMgr.create({
    name: 'Bill',
    email: '[email protected]'
  }, 'Contact'),

var addedContacts = contactStore.add(newContact1, newContact2);
contactStore.sync();

In both cases, when we add contacts to the store, we set up a return variable to grab the return value of the add method. This method returns an array of contacts, which will now have a unique ID as part of each contact object. We can take a look at these values by adding a couple of console logs after our sync:

console.log(addedContacts);
console.log(addedContacts[0].data.name+': '+addedContacts[0].data.id);
console.log(addedContacts[1].data.name+': '+addedContacts[1].data.id);

This will show that two contact objects in an array are returned. It also shows how to get at the data we need from those objects, by using the index number of the specific contact in the array. We can then drill down into the data for a name and the new ID that was assigned when we synced.

A simple store

Now that we have a general idea of how to get data into a store, let's take a look at how to do it with a form.

Forms and stores

For this example, we are going to use the same store and model as our previous example, but we will add a list and a form, so that we can add new contacts and see what we have added. Let's start with the list:

this.viewport = new Ext.Panel({
    fullscreen: true,
    layout: 'fit',
    dockedItems: [{
        xtype: 'toolbar',
        dock: 'top',
        items: [{
            text: 'Add',
            handler: function() {
              addNewContact.show()
            }
        }]
    }],
    items: [
    {
      xtype: 'list',
      itemTpl: '{name}: {email}',
      store: contactStore
    }]
});
Forms and stores

Most of the code here is pretty familiar from previous examples. We have a single panel with a list component. Our list has a template (itemTpl) that uses the same field names as our contact model and arranges how those will be displayed. We have also added a docked toolbar with our new Add button. The button has a very simple function that will show the addNewContact sheet, which we will create next:

var addNewContact = new Ext.Sheet({
  height: 250,
  layout: 'fit',
  stretchX: true,
  enter: 'top',
  exit: 'top',
  items: […]
});

This gives us our new sheet that will appear when we click the Add button. Now, we need to add our form fields to the items section of the sheet we just created:

{
  xtype: 'formpanel',
  padding: 10,
  id: 'contactForm',
  items: [
    {
     xtype: 'textfield',
     name : 'name',
     label: 'Full Name'
    },
    {
     xtype: 'emailfield',
     name : 'email',
     label: 'Email Address'
   }
  ]
}

We start by creating our formpanel component and then adding textfield and emailfield to the items list of formpanel. Make sure you include an id configuration on the form. This will allow us to get to it when we need to.

Specialty text fields

Sencha Touch uses specialty text fields, such as emailfield, urlfield, and numberfield, to control which keyboard is used by the mobile device, as in these iPhone examples:

Specialty text fields
  • The URL Keyboard replaces the traditional Space bar with keys for dot (.), slash (/), and .com

  • The Email Keyboard shortens the Space bar and makes room for @ and dot (.)

  • The Number Keyboard initially presents the numeric keyboard instead of the standard QWERTY keyboard

These specialty fields do not automatically validate the data the user enters. Those kinds of validations are handled through model validations.

Tip

Specialty keyboards

Please note that Android and iOS have slightly different special keyboards, so you may find some variation between the two. It is usually helpful to run your application through both the Android and iOS simulators to ensure that the correct keyboard type is being used.

Mapping fields to the model

You will also notice that the name of each field in our form matches the name used by our contact model; this will allow us to easily create our contacts and add them to the store. However, before we get there, we need to add two buttons (Save and Cancel) to tell the form what to do.

After the emailfield object in our form, we need to add the following:

{
  xtype: 'button',
  height: 20,
  text: 'Save',
id: 'saveButton'
  margin: 10,
  handler: function() {
    this.up('sheet').hide();
  }
  }, {
  xtype: 'button',
  height: 20,
  margin: 10,
  text: 'Cancel',
  handler: function() {
    this.up('sheet').hide();
  }
}

This gives us two buttons at the bottom of our form. Right now, both our Save button and our Cancel button do the same thing: they call a function to hide the sheet that holds our form. This is a good starting point, but we need a bit more to get our Save button to save our data.

Mapping fields to the model

Since we were good little coders and named our fields to match our model, we can just use the following code in our button handler to add our form to our store:

handler: function() {
  var form = this.up('form'),
  var record = Ext.ModelMgr.create(form.getValues(), 'Contact'),
  contactStore.add(record);
  contactStore.sync();
  form.reset();
  this.up('sheet').hide();
 }

The first line uses the up method to grab the form that surrounds the button. Our second line uses form.getValues() and pipes the output directly into a new Contact model, using the create() method from our previous examples. We can then add the new contact to the store and sync, as we did before.

The last bit of cleanup we need to do is to clear all of the form values by using form.reset() and then hide the sheet, as before. If we don't reset the fields, the data would still be there the next time we showed the form.

The list connected to the store will refresh, when we sync the store, and our new contact will appear.

Mapping fields to the model

Since this store uses local storage for holding the data, our list will stay in place, even after we quit the Safari browser. This can be a bit of a pain when you are testing an application, so let's take a look at how to clear out the store.

Clearing store data

Local and session storage saves information on our local machine. Since we plan on doing lots of testing as we code, it's a good idea to know how to clear out this kind of data without removing other data that you might still need. To clear out the data for your local or session store, take the following steps:

  1. Open up Web Inspector from the Develop menu and select the Resources tab.

    Clearing store data
  2. In the Local Storage or Session Storage section (depending on which method you use), you should see your application's database. Once you select the database, you can delete specific records or empty out the database completely. Just select the records on the right side of the screen, and click the X at the bottom to delete the record.

  3. You can also reset the value for the counter by double-clicking on it and then changing the number. Be careful that you do not create multiple records with the same number. This will cause big problems.

  4. Once you are finished in the Resources section, let's move on to editing data with our forms.

Editing with forms

Now that we have taken a look at the basics of getting data into a store, let's look at how to edit that data, using a few modifications to our current form.

The first thing we want to add is an itemTap listener on our list. This will let us tap an item in the list and bring up the form, with the selected entry included in the fields for us to edit. The listener looks like the following:

listeners: {
 itemTap: {
  fn: function(list,index){
   var rec = list.getStore().getAt(index);
   var form = Ext.getCmp('contactForm'),
   form.load(rec);
   addNewContact.show()
  }
 }
}

Our itemTap listener will automatically get back a copy of the list and the index of the item that got tapped. We can then grab the store behind our list using list.getStore() and grab the tapped item using getAt() and the index value that was passed to us.

It is often useful to chain functions together in this fashion, especially if the piece you need only has to be used once. For example, we could have done:

var store = list.getStore();
var rec = store.getAt(index);

This would also let us use that store variable in a number of places within the function. Since we only need it to grab the record, we can do both of these lines as a single line:

var rec = list.getStore().getAt(index);

After we get the data record, we grab our form by using the ID of the form component and the Ext.getCmp() function. Now that we have the form, we can load the record and show the addNewContact sheet that contains our form.

As before, since we only use the form to do one thing, we could shorten the loading of the data record to the following:

Ext.getCmp('contactForm').load(rec);

We have included it here as two lines, just to show that either will work.

Now that the code is in place, you can tap any item in your list and see the edit form. This form looks exactly the same as before, but it now has the data, for the contact we clicked, filled in.

Editing with forms

There's still one more problem to be dealt with; our Save button is hard coded to add a new record to the store. If we tap Save right now, we will just end up with multiple copies of the same contact. We need to make a change to our form, to let us switch what the Save button does, depending on whether we are editing or a creating new contact.

Switching handlers

In order to change the handler the button fires to save our contact, we need to separate the bulk of code from the button itself. To begin, locate the handler for our Save button, and copy the current function to your clipboard. Next, we want to replace that function with the name of an external function:

handler: addContact

Now, we have to create the new addContact function for this handler to use. In our JavaScript file, right before where we create our addNewContact sheet, add a new function called addContact, and paste in the code from our old handler function. It should look as follows:

var addContact = function() {
  var form = this.up('form'),
  var record = Ext.ModelMgr.create(form.getValues(), 'Contact'),
  contactStore.add(record);
  contactStore.sync();
  form.reset();
  this.up('sheet').hide();
};

This is the same old form-saving function we used on our button before, and it will work just fine for adding new contacts. Now, we need to create a similar function to update our contacts when we click on them in the list.

Up above our addContact function, add the following code:

var updateContact = function() {
  var form = this.up('form'),
  var record = contactStore.getById(form.record.data.id);
  form.updateRecord(record);
  contactStore.sync();
  form.reset();
  this.up('sheet').hide();
};

This does almost the exact same thing as our other function. However, instead of grabbing the form fields and creating a new record, we grab the record from the store using contactStore.getById(). This record is the one we need to update with our new information.

We can find the ID for the record by looking at the form. Since we loaded the record into our form before we started editing, we can grab the ID we need with form.record.data.id.

Our record variable is now set to the old information from the data store. We can then pass that record to form.updateRecord();, which will overwrite the old information in the store record with our current form values. The ID will stay the same as we do not pass a new value for that.

After we update the record, we just sync, reset, and hide, as before.

Now that the code for our two functions is in place, we need to switch the handler for our Save button based on if the user clicked the Add button at the top of our list or selected an item in the list.

Let's start with the Add button. Locate the handler for our Add button at the top of our list object. We need to add some code to this button that will change the handler on the Save button:

handler: function() {
 var button = Ext.getCmp('saveButton'),
 button.setHandler(addContact);
 button.setText('Create'),
 addNewContact.show()
}

As our form button has a unique ID of id: 'saveButton', we can grab it with Ext.getCmp() and make a few changes. The first is to update the handler to see our new addContact function, and the second is to change the text of the button to Create. We can then call addNewContact.show(), as before.

Our Add button is now set to show the form and change the text and handler for the button.

Now, we need to do something similar to the tap hander on our list:

itemTap: {
 fn: function(list,index){
  var rec = list.getStore().getAt(index);
  var form = Ext.getCmp('contactForm'),
  form.load(rec);
  var button = Ext.getCmp('saveButton'),
  button.setHandler(updateContact);
  button.setText('Update'),
  addNewContact.show();
 }
}

Here, we still need to grab our data record and load it into the form, but now, we grab our saveButton method and make changes to the handler and text as well. The changes point the Save button to our updateContact function and change the text to update.

Switching handlers

Deleting from the Data Store

If you remember earlier, when we talked about CRUD functions, you can see that we have successfully covered Create, Read, and Update. These are all handled automatically by the store with very little code required. What about Delete?

As it turns out, Delete is just as simple as our other store methods. We can use either of two methods: the first is remove()—it takes a record as its argument—and the second is removeAt, which takes an index to determine which record to remove. We could implement either of these as part of our edit form, by adding a new button at the bottom of the form, as this:

{
    xtype: 'button',
    height: 20,
    margin: 10,
    text: 'Delete',
    ui: 'decline',
    handler: function() {
      var form = this.up('form'),
      contactStore.remove(form.record);
      this.up('sheet').hide();
    }}

Using removeAt requires the index of the store record, so we could do the same thing by changing the remove line to:

contactStore.removeAt(form.record.data.id);

That takes care of all of our basic Create, Read, Edit, and Delete functions. As long as you remember to set up your model and match your field names, the store will handle most of the basics automatically.

Note

Further Information:

Sencha has a number of good tutorials on using forms and stores, including a video presentation located at http://docs.sencha.com/touch/1-1/#!/video/26784522.

You should also check out Using the Data Package in Sencha Touch at http://www.sencha.com/learn/using-the-data-package-in-sencha-touch/.

Summary

In this chapter, we covered the data model that forms the basic structure for all of our data in Sencha Touch. We looked at the proxy and reader, which handle communications between the data store and our other components. We also talked about the data store, which holds all of our data in Sencha Touch. Finally, we took a look at how you can use forms to get data in and out of the stores, as well as at how to delete the data when it is no longer needed.

In our next chapter, we will take a look at all of the other things we can do with data once we get it out of the store.

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

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