Tuesday, January 11, 2011

Local storage proxy with Sencha Touch

I am on my way to write my first app on sencha touch together with a friend. One of the abilities we want to have in our app is to allow the  user to save his personal settings on his device. In order to make it happen we had to teach ourselves how to use local storage proxy.

save my name

To achieve our goal we decided to build the simplest app -  get name -> save to file + display on screen + load value on the next app load.

the panels

Form panel - where we get the name and press the button for the update

Display panel - where we display the saved name

The code for the panels:

   1: Ext.setup({
   2:     icon: 'icon.png',
   3:     glossOnIcon: false,
   4:     tabletStartupScreen: 'tablet_startup.png',
   5:     phoneStartupScreen: 'phone_startup.png',
   6:     onReady: function () {
   7:  
   8:         // formpanel for the input
   9:         var formpanel = new Ext.form.FormPanel({
  10:             title: 'form',
  11:             //textfield for the name
  12:             items: [{
  13:                 xtype: 'textfield',
  14:                 label: 'name',
  15:                 name: 'name'
  16:             }],
  17:             //button for the handler (can be replaced by update listener for the text field)
  18:             dockedItems: [{
  19:                 dock: 'top',
  20:                 items: [{
  21:                     xtype: 'button',
  22:                     text: 'diplay',
  23:                     handler: function () {}
  24:                 }]
  25:             }]
  26:  
  27:         });
  28:         //panel for display
  29:         var panel = new PanelExtend({
  30:             title: 'display2'  
  31:          });
  32:         //the tab pannel definitions
  33:         new Ext.TabPanel({
  34:             fullscreen: true,
  35:             type: 'dark',
  36:             sortable: true,
  37:             items: [formpanel, panel]
  38:         });
  39:             
  40:     }
  41:  
  42: });
  43: // create a structure for the pannel so we can use it again if needed
  44: var PanelExtend = Ext.extend(Ext.Panel,{
  45:         title:'empty'
  46:         
  47: });
  48:  
  49: Ext.reg('mypanel',PanelExtend)    

In order to update the data in the second page we added a small handler to pannelExtand at the end of the code so the new declaration is:




   1: var PanelExtend = Ext.extend(Ext.Panel,{
   2:             title:'panel',
   3:             doit: function(name){
   4:                 this.update(name);
   5:             }
   6:     });


We also added the handler for the button on the form panel:



   1: var formpanel = new Ext.form.FormPanel({
   2:         title: 'form',
   3:         items: [{
   4:             xtype: 'textfield',
   5:             label: 'name',
   6:             name: 'name'
   7:         }],
   8:         dockedItems: [{
   9:             dock: 'top',
  10:             items: [{
  11:                 xtype: 'button',
  12:                 text: 'diplay',
  13:                 handler: function () {
  14:                     //get the value from the field
  15:                     var values = formpanel.getValues();
  16:  
  17:                     //call the update function
  18:                     panel.doit(values.name);
  19:                 }
  20:             }]
  21:         }]
  22:  
  23:     });

As you see in the code, in this stage all it does is to activate the handler of the display panel


defining the storage


In order to define the storage to be a local storage proxy we added the following code to the onready function of the Ext.setup:



   1: // the sructure for every row in the file
   2: var UserSettings = Ext.regModel('UserSettings', {
   3:     fields: ['id', 'name', 'value']
   4: });
   5: // the storage 
   6: var store = new Ext.data.Store({
   7:     proxy: new Ext.data.LocalStorageProxy({
   8:  
   9:         id: 'data',
  10:         //very important for the row update - basicaly, the row number is taken from the id field in the userSettings structure
  11:         proxy: {
  12:             idProperty: 'id'
  13:         }
  14:     }),
  15:     //uses the userSettings structure
  16:     model: 'UserSettings',
  17:     autoLoad: true,
  18:     autoSave: true
  19: });
  20: //sync with local file
  21: store.sync();
  22: //what to do when file is read, right now the only thing important to us us the scope
  23: store.read({
  24:     scope: this,
  25:     callback: function (records) {
  26:         //code to load the first record
  27:     }
  28: });


Most posts online show how to update a local storage file without taking care of the location of the data in the file. If you build a score record or just want to monitor the user data you do not need to have the idProperty definition but it is important if you want to update a certain row in the file. In our experiment and for the program we develop we wanted to be able to have one line for every variable in the storage, therefore we added the idProperty.


loading the data to the display pannel


Since we already loaded and synced the data we can now use our update function (pannel.doit), we added the following line as the last row of the onrReady function:



   1: panel.doit(store.getAt(0).data.value);

The getAt is pointing to the first entry of the file, knowing that it is the only entry. if you build a bigger file, it is advised to build a small function that builds the file structure( fior example: row 0 is name, row 1 is last name, etc…)


saving the data


in the handler of the button, we added the part that saves the data to the file:



   1: //read data
   2: store.read();
   3: //get the first item
   4: var item = store.getAt(0);
   5: //if not exists, build the first item (this area needs to be replaced with a function that construct a whole settings file
   6: if (!item) {
   7:     //constract item's structure
   8:     var item = new UserSettings({
   9:         id: 1, name: 'user name', value: values.name
  10:     });
  11:     //add new item to file
  12:     store.add(item);
  13:     //sync item
  14:     store.sync();
  15:  
  16: }
  17: //set value in item
  18: item.set('value', values.name);
  19: //sync items to file
  20: store.sync();

Saving the data can be done in two ways – if you want to add a row to a file every time you save all you need to do is to use store.add() and store.sync() but if you do not want to add rows every time you update you need to check if the row exists (add it if not) and then call item.set and store.sync() as it is shown in the code above.


The way we save the data in this example is by assigning a line for every property. this is not a must and for our software needs it will be replaced as a line for every user. All users data can be saved in the same row by changing the structure of the userSettings field. the only thing that I suggest to leave is the id field because it is the connection between the row number and the id of the field.


links


Using data package  - from the sencha touch website.


Offline Apps with HTML5: A case study in Solitaire – from the Ed Spencer blog, gave us the basics about storage.


sencha howto – this link was the biggest help to understand the subject of how to load the data, thanks, cipto.

3 comments:

  1. I found this helpful - but unless I'm missing something, it seems you're not actually using the local store at all? I commented out all code related to defining the model, performing the load, etc -- and it worked correctly in that the second panel was updated to reflect the value entered.

    ReplyDelete
  2. How to Update a record in sencha touch using Ext.data.store? I am able to insert a record into sqlite table, to read data into form again.But i want to update record in table from the Form ....
    Can any one please let me know how to update record...Thanks in advance...

    ReplyDelete
  3. Can I get zip code to test it on my local.

    I also want to sync my store to localstorage.
    I tried an example given on sencha touch 2 docs localstorage.Proxy.


    Can you guide for this error I am getting:
    [WARN][Ext.data.Operation#process] Unable to match the record that came back from the server. Console.js:35
    [WARN][Ext.data.Operation#process] Unable to match the record that came back from the server.

    Thanks.

    ReplyDelete