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
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.
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 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.
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.
Good tutorials are available at: http://www.zytrax.com/tech/web/regex.htm
A searchable database of regular expressions can be found at: http://regexlib.com
A wonderful regular expression tester is also available at: http://www.rexv.org/
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:
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.
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.
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.
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:
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:
The reader gets declared as part of the proxy:
proxy: { type: 'localstorage', id: 'userProxy', reader: { type: 'json' } }
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.
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 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 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.
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.
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.
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.
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.
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.
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 }] });
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.
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:
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.
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.
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.
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.
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:
Open up Web Inspector from the Develop menu and select the Resources tab.
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.
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.
Once you are finished in the Resources section, let's move on to editing data with our 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.
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.
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
.
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.
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/.
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.