Home » Sencha Touch » Sencha touch store and model

Sencha touch store and model

Welcome to the fourth module of this series. In this module we will learn about Sencha touch store and models,how store and model are related to each other, how to use proxy, how to do server interaction. Moving forward we will learn about sorting and filtering model’s data, and lastly how can we use navigation view or list view to display the model data.

Below are the topics that we will cover in this module:

  • Define store
  • Create a Listview.
  • Sort, filter , group
  • Models
  • Customizing fields
  • Validations
  • Data Model Proxy
  • Data Model association

In Sencha touch there are three major components in loading and saving data:

  • Model: model is a collection of fields which each record comprise of. For example a user model may have firstname, lastname, age, email fields.
  • Store: store is a collection of same type of models. Store can have logic to sort, filter and group models. For example you can have number of users for which we can create user store and we can write logic to sort, filter and group users.
  • Proxy: proxy is responsible to get data from server and load/save it in store.

 

Define store

Store can be created by using Ext.data.store(), below is the sample code to create the store

var studentStore = Ext.create('Ext.data.Store',{
   storeID:'id_studentStore',
   data:[
      {id:1,firstName:'Marcus',              lastName:'Allen',              grade:10, img:'img1', email:'mallen@gmail.com',     userName:'mallen'},
      {id:2,firstName:'Jesse',  lastName:'James',           grade:8,  img:'img2', email:'jjames@gmail.com',     userName:'jjames'},
      {id:3,firstName:'Clark',   lastName:'Kent',              grade:10, img:'img3', email:'ckent@gmail.com',      userName:'ckent'},
      {id:4,firstName:'Ossian',               lastName:'Mills',               grade:10, img:'img4', email:'omills@gmail.com',     userName:'omills'},
      {id:5,firstName:'Sammy',              lastName:'Sossa',             grade:8,  img:'img5', email:'ssossa@gmail.com',     userName:'ssossa'},
      {id:6,firstName:'Himanshu', lastName:'Chahar', grade:9,  img:'img6', email:'hchahar@gmail.com',    userName:'hchahar'},
      {id:7,firstName:'Tinny',  lastName:'Tim',                 grade:10, img:'img7', email:'ttim@gmail.com',       userName:'ttim'},
      {id:8,firstName:'John',                   lastName:'Williams',grade:9,  img:'img8', email:'jwilliams@gmail.com',  userName:'jwilliams'},
      {id:9,firstName:'Richard',              lastName:'Nixon',            grade:10, img:'img9', email:'rnixon@gmail.com',                          userName:'rnixon'},
      {id:10,firstName:'Lynn', lastName:'Swann',          grade:9,  img:'img10',email:'lswann@gmail.com',                 userName:'lswann'}
   ]
});

In above example I have created a very simple store called studentStore. I have two  main configs; “storeID” config which is the unique id of store and “data” config which is the  collection of objects.

If we run above code, nothing will be displayed in an output window.

Let’s check the length of items that this store holds.

console.log(studentStore.getCount()); // 10

store.getCount() will return store item counts. So above statement will return 10 because we have 10 objects in store.

 

Create a Listview

In above section we have created a concrete store but if you run the above code nothing will be display, to display the data we should have some placeholder which will take the data and display it to the user, in our case, the best component to display the store is the listView. So let see how can we create a list and populate data in it.

var studentList = Ext.create('Ext.List',{
   fullScreen:true,
   itemTpl:[
      '<ul class="student-list-style clearfix">',
         '<li><img src="images/avatar_male.jpg"></li>',
         '<li><div class="model-name-style">{firstName}</div><div class="email-style">{email}</div></li>',
         '<li><div class="grade-style">Grade: {grade}</div></li>',
      '</ul>'

   ],
   store:studentStore
});

Style:

.student-list-style li{
   float:left;
}

.student-list-style li:nth-child(1){  }
.student-list-style li:nth-child(2){  }
.student-list-style li:nth-child(3){
   float:right;
   padding-top: 15px;
}

.model-name-style{
   font-size: 1.1em;
   color: black;
   font-family: serif;
}

.email-style{
   font-size: .90em;
   color: gray;
}

.grade-style{
   font-size: .775em;
   color: grey;
}

Above I have created a list, each item in the list will float image on right side, firstName and email in middle and grade at left.

There are configs that i have used in the list, below is the description of each of these configs:

fullscreen: this config will make the list to occupy fullscreen space.

itemTpl: this is a important config, which defines the template for each item in the list.

Now we need to populate this list with data, for that we should pass our store name value to store config. In above code i am passing studentStore to store config, which will take the data from this store and populate it in list.

To apply styling you can add class to html element as I am doing in above code.

 

Sort, filter , group

So far we have store which will hold our application data and we have list to populate that data. So let’s move forward and perform some operation on that data like sorting the data based on some attribute or filtering the data for a particular user or grouping the users which have some common attribute value.

  • sort: to sort the data we can use store.sort() method.
<span style="font-size: medium;">studentStore.sort('grade','DESC');</span>

Here we are sorting the data based on “grade” field and that too in descending order. So student with highest grade will be the first in the list and student with the lowest grade will the last in the list.

  • filter: filter is used to filter the data based on some criteria, for example filtering the data according to grade.

studentStore.filter(‘grade’,’9′);

Here we are filtering the data based on grade, so above snippet will return all the students who have graded value 9.

So the complete code would be:

studentStore.sort('grade','DESC');
studentStore.filter('grade','9');
var studentList = Ext.create('Ext.List',{
   fullScreen:true,
   itemTpl:[
      '<ul class="student-list-style clearfix">',
         '<li><img src="images/avatar_male.jpg"></li>',
         '<li><div class="model-name-style">{firstName}</div><div class="email-style">{email}</div></li>',
         '<li><div class="grade-style">Grade: {grade}</div></li>',
      '</ul>'
   ],
   store:studentStore
});

There is no need to update the list; it would be updated automatically whenever its associated store data will change.

 

  • Group

Sencha touch list also supports grouping the items based on given field, for example you can group items of same grade.

So let’s take a look at this:

To group the items we use grouper config of store. Grouper config calls groupFn() function which is called for each item and determines the group it belongs to.

Below code group our data based on cost

grouper:{
   groupFn:function(record){
      return record.get('cost');
   }
}

To make it work we should set grouped config in our list to true.

var studentStore = Ext.create('Ext.data.Store',{
   storeID:'id_studentStore',
   data:[
      {id:1,firstName:'Marcus',              lastName:'Allen',              grade:10, img:'img1', email:'mallen@gmail.com',     userName:'mallen'},
      {id:2,firstName:'Jesse',  lastName:'James',           grade:8,  img:'img2', email:'jjames@gmail.com',     userName:'jjames'},
      {id:3,firstName:'Clark',   lastName:'Kent',              grade:10, img:'img3', email:'ckent@gmail.com',      userName:'ckent'},
      {id:4,firstName:'Ossian',               lastName:'Mills',               grade:10, img:'img4', email:'omills@gmail.com',     userName:'omills'},
      {id:5,firstName:'Sammy',              lastName:'Sossa',             grade:8,  img:'img5', email:'ssossa@gmail.com',     userName:'ssossa'},
      {id:6,firstName:'Himanshu', lastName:'Chahar', grade:9,  img:'img6', email:'hchahar@gmail.com',    userName:'hchahar'},
      {id:7,firstName:'Tinny',  lastName:'Tim',                 grade:10, img:'img7', email:'ttim@gmail.com',       userName:'ttim'},
      {id:8,firstName:'John',                   lastName:'Williams',grade:9,  img:'img8', email:'jwilliams@gmail.com',  userName:'jwilliams'},
      {id:9,firstName:'Richard',              lastName:'Nixon',            grade:10, img:'img9', email:'rnixon@gmail.com',                          userName:'rnixon'},
      {id:10,firstName:'Lynn', lastName:'Swann',          grade:9,  img:'img10',email:'lswann@gmail.com',                 userName:'lswann'}
   ],
   sorters:['firstName'],
   grouper:{
      groupFn:function(record){
         return record.get('grade');
      }
   }
});

var studentList = Ext.create('Ext.List',{
   fullScreen:true,
   itemTpl:[
      '<ul class="student-list-style clearfix">',
        '<li><img src="images/avatar_male.jpg"></li>',
        '<li><div class="model-name-style">{firstName}</div><div class="email-style">{email}</div></li>',
        '<li><div class="grade-style">Grade: {grade}</div></li>',
      '</ul>'
   ],
   store:studentStore,
   grouped:true
});

This is the complete code to group the items according to cost. Notice I am using sorters config in store; this is another way of sorting the data.

When we use grouper with sorter we should always remember that sorting will be applied on each group and not on the complete list. So for example if we have 4 groups each having  5 items, then sorting will be applied on each group (i.e. on 5 items) and not on the complete list. In our code, items in each group will be sorted based on firstName (alphabetically).

If you run the above code you will see that the list is displaying plain number in group header, and it’s looking odd. In next section we will see how can we tweak little bit of code to change the header text.

 

Models

In previous section we saw how can we define store and use it in our application, now let’s define a model to have more precise code.

Model can be defined by using Ext.data.Model, below it concrete code to define model

Ext.define('StudentModel',{
   extend:'Ext.data.Model',
   config:{
      fields:['id','firstName','lastName',{name:'grade',type:'int'},'img','email','username']
   }
});

Above code will create a simple model “StudentModel”. In config I have defined fields that are there in our store. In Sencha touch we can either have fields without defining type and Sencha touch will take care to typecast them or we can define type also as we did for grade field.

Later we will see how can we define custom fields also.

After defining the model the next step is to associate this model with our existing store. To do that just add “model” config in store.

model:'StudentModel'

 

Customizing fields

In Sencha touch models can have custom fields also. For example our store has two different fields, firtName and lastName, so we can create a single field in model which will merge these two fields and return combined result.

Ext.define('StudentModel',{
   extend:'Ext.data.Model',
   config:{
      fields:['id','firstName',{name:'grade',type:'int'},'lastName','img','email','username',{
      name:'fullName',
      convert:function(value,record){
         return record.get('lastName') + ", "+ record.get('firstName');
      }
   },{
      name:'gradeValue',
      mapping:'grade',
      convert:function(grade){
         return grade+"th Grade"
      }
   }]
  }
});

var studentList = Ext.create('Ext.List',{
   fullScreen:true,
   itemTpl:[
     '<ul class="student-list-style clearfix">',
        '<li><img src="images/avatar_male.jpg"></li>',
        '<li><div class="model-name-style">{fullName}</div><div class="email-style">{email}</div></li>',
        '<li><div class="grade-style">Grade: {grade}</div></li>',
    '</ul>'
   ],
   store:studentStore,
   grouped:true
});

In above code I have created two custom fields, fullName and GradeValue.

I am using convert function to get the modified result, in our case “fullName“ will take lastName and  firstName field and merge them together to return the full name

GradeValue custom fields will take grade field and append the text to it, so instead of displaying just the plain grade numbers on header it will now display grade+text

 

Validations

Validations are important because the stop the user to send false data on the server. In sencha touch, model provides the mechanism to validate the data before saving it.

There are 6 types of validations supported by Sencha touch:

  • Presence:

Presence validation checks whether the field has a value.

  • length:

Length validation checks whether the string is between a given min and max length.

  • format:

format validation checks whether the value matches the given regular expression or not.

  • inclusion:

inclusion checks whether the value is within the given set of values

  • exclusion:

exclusion is oppostie of inclusion, it checks whether the value is not within given set of values

  • email:

email checks whether the value is a valid email address or not.

Let’s apply some validation on our StudentModel.

Ext.define('StudentModel',{
   extend:'Ext.data.Model',
   config:{
      fields:['id','firstName',{name:'grade',type:'int'},'lastName','img','email','userName',{
   name:'fullName',
   convert:function(value,record){
      return record.get('lastName') + ", "+ record.get('firstName');
   }
   },{
   name:'gradeValue',
   mapping:'grade',
   convert:function(grade){
      return grade+"th Grade"
   }
   }],
   validations:[
      { type: 'presence', field: 'email' },
      { type: 'length', field: 'userName', max: 10 },
      { type: 'format', field: 'grade', matcher: /^[0-9]+$/ }, 
      { type: 'inclusion', field: 'grade', list: [8,9,10] },
      { type: 'exclusion', field: 'grade', list: [6,7] },
      { type: 'email', field: 'email'}
   ]
 }
});

In above code I have added validations config in model. Below is the list of validations that i have applied on this model

  • email is a required field.
  • userName should not be more than 10 characters.
  • grade should only contain numbers.
  • grade should have only 8,9,10.
  • grade should not have 6 and 7.
  • email should have proper email format.

Now create an object for StudentModel.

var studentData = Ext.create('StudentModel',{
   id:11,
   firstName:'Morale1',
   lastName:'Dixon1',
   grade:6,
   img:'img1',
   userName:'Morale1Dixon1',
   email:'hchahar'
});

I have create a model which have all the wrong values according to our validations criteria. Now let’s apply validation on this model.

if(!studentData.isValid()){ // check if model is valid
   var errors = studentData.validate(); // retruns error object
   errors.each(function (record) { // iterate through each item in error object
      console.log(record.getField() + " === " + record.getMessage());
   });
}

To apply the validation, you should first check whether model object is valid or not, model.isValid() do that check.

If model is not valid then validate the current data against all of its configured validations, model.validate() do that and returns the error object.

and then iterate through each error item and display the result.

//console: email === must be present (because email field is missing in studentData)
//console: userName === is the wrong length (because userName is more than 10 characters)
//console: grade === is not included in the list of acceptable values (because grade has value 6 which is not in acceptable list)
//console: grade === is not an acceptable value (because grade has value 6 which is not acceptable)
//console: email === is not a valid email address

Now try to pass some string for grade and we will get an exception because grade should only be a number.

//console: grade === is the wrong format (because grade has a string value and grade should only be a number)

 

Data Model Proxy

Proxies are used to get and load the data. There are two types of proxies: server-side and client-side. Server-side proxy include Ajax, jsonp and Rest services and client-side proxy includes localstorage and MemoryProxy.
We will only discuss about ajax proxy in this tutorial, to read more about proxies please refer Sencha touch documentation.

Proxy config is used to add the proxy in model or in store. But the good practice is to define proxy in model so we do not need to call store again and again to load the data, we can directly do that in model.

proxy: {
   type: 'ajax',
   url: '/StudentDB.json',
   reader: {
      type: 'json',
      rootProperty: 'StudentData'
   }
}

In above code I have added Ajax proxy to fetch the data from url.

“type” defines the type of proxy we want to use.
“url” defines the url to get the data, it can be a local file url as well as server url. In above code I am passing local file url.
“reader” is used to decode the server’s response.
“type” config under reader define the type of response we will get from server.
“rootProperty” config under reader takes the root-node name of data.

We will see this in action later in this module.

 

Data Model association

Data model is the simplest yet the powerful way of associating models with each other. It helps to define the relationship of one model with other models. Single model can be associated with n number of models.

  • Example:

Below example will cover almost all the topics we discussed so far in model; grouping, sorting, association, proxy etc. In this example we will also learn how to create the navigation view and use it to display the data.

 

// 1st Checkpoint

Ext.define('StudentViva',{
   extend:'Ext.data.Model',
   config:{
   fields:['id','subject','time','date','duration',{
      name:'notification',
      convert:function(value,record){
         return record.get('subject')+" s viva is on "+record.get('date');
      }
    }],
  }
});

// 2nd Checkpoint
Ext.define('StudentModel',{
   extend:'Ext.data.Model',
   requires: [
     'Ext.data.association.HasMany'
   ],
   config:{
      proxy: {
          type: 'ajax',
          url: '/StudentDB.json',
          reader: {
            type: 'json',
            rootProperty: 'StudentData'
          }
       },
       fields:['id','firstName',{name:'grade',type:'int'},'lastName','img','email','userName',{
       name:'fullName',
       convert:function(value,record){
           return record.get('lastName') + ", "+ record.get('firstName');
       }
    },{
    name:'gradeValue',
    mapping:'grade',
    convert:function(grade){
       return grade+"th Grade"
      }
    }],
    hasMany: {
      associationKey: 'viva',
      model: 'StudentViva',
      name: 'viva'
    }
  }
});

// 3rd Checkpoint
var studentStore = Ext.create('Ext.data.Store',{
   storeID:'id_studentStore',
   model:'StudentModel',
   sorters:['lastName'],
   grouper:{
      groupFn:function(record){
         return record.get('gradeValue');
      }
   }
});

// 4th Checkpoint
var studentList = Ext.create('Ext.NavigationView',{
   items:[{
      xtype:'list',
      title:'Students Information',
      fullscreen:true,
      itemTpl:[
        '<ul class="student-list-style clearfix">',
          '<li><img src="images/avatar_male.jpg"></li>',
          '<li><div class="model-name-style">{fullName}</div><div class="email-style">{email}</div></li>',
          '<li><div class="grade-style">Grade: {grade}</div></li>',
        '</ul>'
      ],
      store:studentStore,
      grouped:true,
      listeners:{
        select:function(value,record){
          var vivaData = record.getData().viva;
          vivaNotification = "";
          for (var i=0; i < vivaData.length; i++) {
            vivaNotification+=vivaData[i].notification+ '<br/>';
          }
          studentList.push({
            title:record.get('fullName'),
            html:vivaNotification
          });
      }
     }
   }]
});

// 5th Checkpoint

Ext.Viewport.add(studentList);

Let’s go through each checkpoint in above code:

You must have noticed in “checkpoint 2″ we have proxy config in which we are referring local file url. Below is the JSON data that I have in studentDB.json file.

 

{
  "StudentData":[{
      id:1,firstName:'Marcus',lastName:'Allen',grade:10, img:'img1.png', email:'mallen@gmail.com',userName:'mallen',viva:[
    {
       id:'101',
       subject:'Computers',
       time:'11:00',
       date:'12/1/2014',
       duration:2
     },{
        id:'102',
        subject:'Physics',
        time:'11:00',
        date:'13/1/2014',
        duration:3
     }]
  },...
 ]
}

 

1st Checkpoint

We have defined “studentViva” for viva object. Each student will have viva object(as shown in JSON object)

Under config we have defined fields for this model: id, subject, time, date, duration and custom field “notification” which will return notification text.

 

2nd Checkpoint

We have defined “StudentModel”; this is the same model we defined in previous examples. The only difference is we have added “hasMany” config.

In “hasMany” config we have associationKey, you should pass the the JSON object keyword to this config. It should be same as the name in JSON object. In our case viva is the JSON object keyword(check the studentDB.json)

model: modelName of associated Model.

name: name of this association. Use of this value will be shown in “checkPoint 4″

hasMany config tells that “StudentModel” has Many “studentViva” models.

 

3rd Checkpoint

We have defined “studentStore”, which is same as we defined in previous examples. You can previous examples of this module to get a better understanding of this checkpoint.

 

4th Checkpoint

In previous examples we have created a list to populate the data. But now we want to display list, with title. And when user clicks on list item, user should be navigated to next page.

The simplest way to do this is to create a navigation view. Navigation view is same as card layout. It display one item at a time, additionally it allows to push and pop views dynamically.

So we have a navigation view, we have added xtype:list (short form of Ext.list) as a child item of navigationView and its title will be “Students Information”.

To add click event on list we have added listeners config. In listeners config we have “select” event which will be called when user selects list item.

When user selects the item we want to fetch the viva object. To do that we are using:

var vivaData = record.getData().viva;

viva is the value that we defined in hasMany config for “name” (In checkpoint 2). For example if we have below hasMany config:

hasMany: {
   associationKey: 'viva',
   model: 'StudentViva',
   name: 'vivaRecord' // changes this property name
},

Then we will have to use record.getData().vivaRecord; to fetch the viva object

Now to navigate to next page we are using navigation view’s push() method which will push another view. So when user clicks on item, select event will be called and new view will be pushed.

New view will have Student name as title, and vivaNotification as its html content.

 

5th Checkpoint

At last we are adding “studentList” to viewPort.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>