Sencha Inc. | HTML5 Apps

Blog

Countdown to Ext JS 4: Anatomy of a Model

February 08, 2011 | Ed Spencer

This article originally appeared on January 25th as part of our January Newsletter.

If you follow Ext JS, chances are you already know that we have a brand new data package for Ext JS 4. The new data package builds on the principles of the one in Ext JS 3 while adding a huge number of new capabilities. We recently introduced the new data package on our blog, and today we’re going to take a deeper look at the new Model class.

A Model represents almost any type of persistable data in an application. For example, an e-commerce application might have models for User, Product, Order and so on. Each model contains a set of fields as well as functions that enable the application to operate on its data—for example an Order model might have a ‘shipToCustomer’ function that kicks off the shipping process.

Ext JS 3.x and before had a class called Record, which was very similar to the new Model class. The difference is that whereas Record was only fields and functions, Model is much more powerful. Today, we’re going to look at four of the principal parts of Model—Fields, Proxies, Associations and Validations.

Ext.data.Model in Ext JS 4

Fields

Every model consists of one or more fields. At its simplest, a field is just a name: ‘id’, ‘email’ and ‘phone’ could all be fields on a User model. Fields can also do a lot more. They can have a type (int, float, string or auto) and even conversion functions that modify their values when set. For example, if we record our user’s height, we can have a function that converts it from inches to centimeters.

Here’s how we would set up a simple User model with three fields. For the first field, we will specify type ‘int’—e.g. it should be an integer. For the second field, we won’t specify a type, which means it will use the ‘auto’ type by default. The auto type will accept anything passed to it. For the third field, we’ll specify type ‘int’ and also provide a conversion function that takes a number of inches and converts it to centimeters:

 
Ext.regModel('User', {
    fields: [
        {name: 'id', type: 'int'},
        'name',
        {
            name: 'height',
            type: 'int',
            convert: function(inches) {
                return Math.round(inches * 2.54);
            }
        }
    ]
});
 

It’s easy to create a new User instance now and see how our field definitions affect the data we pass into them:

 
var abe = new User({
    id: 123,
    name: 'Abe Elias',
    height: 76
});
 
console.log(abe.get('id')); //logs 123 (a JavaScript Number object)
console.log(abe.get('name')); //logs 'Abe Elias' (A JavaScript String object)
console.log(abe.get('height')); //logs 193 (inches converted to centimeters)
 

Using simple instances of Models is very easy, but usually an application needs to load and save data. For this, we turn to Proxy.

Proxy

In Ext JS 4, a Proxy is responsible for loading and saving data from some source. This can be over AJAX, using HTML5 localStorage or even simply keeping data in memory. Ext JS 4 comes with a set of Proxies built in by default, but it’s easy to create your own. Let’s see how we might set up a Proxy for our User Model:

 
Ext.regModel('User', {
    fields: ['id', 'name', 'email'],
 
    proxy: {
        type: 'rest',
        url : '/users',
        reader: 'json'
    }
});
 

Defining Proxies directly on a Model is a new approach in version 4—its main benefit is that we can easily load and save Model data without creating a Store, as we did in Ext JS 3.

Above we set up the User Model to use a RestProxy. This Proxy uses AJAX calls to load and save its data from a server. It’s smart enough that it knows how to build RESTful urls automatically given a base url (‘/users’ in this case). We also set up our Proxy with a JsonReader—this takes the server response and decodes it into User Model instances. In this case, we know that our server will respond with JSON, so a JsonReader makes sense.

Let’s load up a User model now:

 
//GET /users/123
User.load(123, {
    success: function(user) {
        console.log(user.get('name'));
    }
});
 

The code above loads data from the URL /users/123, which was constructed from the ID we passed to User.load. It then uses the JsonReader to decode the response into a User object. Our server sends back a response like this, which would make the code above log “Aaron Conran”:

 
//response from GET /users/123
{
    "id": 123,
    "name": "Aaron Conran",
    "email": "aaron@sencha.com"
}
 

Easy enough, but let’s take it to the next level with associations.

Associations

We usually have many Models in an application, but none of them exist in a vacuum. There are relationships between models—each User in our system makes Orders, and each Order is composed of Order Items. Ext JS 4 gives us the ability to represent these relationships on the client side with a simple and intuitive Associations API. Let’s see how we’d set those three Models up:

 
//each User hasMany Orders
Ext.regModel('User', {
    fields: ['id', 'name', 'email'],
    proxy : {
        type: 'rest',
        url : '/users',
        reader: 'json'
    },
 
    hasMany: 'Orders'
});
 
//each Order belongsTo a User, and hasMany OrderItems
Ext.regModel('Order', {
    fields: ['id', 'user_id', 'status'],
    belongsTo: 'User',
    hasMany: 'OrderItems'
});
 
//each OrderItem belongsTo an Order
Ext.regModel('OrderItem', {
    fields: ['id', 'order_id', 'name', 'description', 'price', 'quantity'],
    belongsTo: 'Order'
});
 

Now that our application knows about its Models and their associations, let’s put them to use. Expanding on the example from the Proxy section above, let’s make our server response look like this:

 
{
    "id": 123,
    "name": "Aaron Conran",
    "email": "aaron@sencha.com",
    "orders": [
        {
            "id": 1,
            "status": "shipped",
            "orderItems": [
                {
                    "id": 2,
                    "name": "Sencha Touch",
                    "description": "The first HTML5 mobile framework",
                    "price": 0,
                    "quantity": 1
                }
            ]
        }
    ]
}
 

When we load our User data now, the associated Orders and OrderItems are loaded along with it, enabling us to interact with them in our code:

 
User.load(123, {
    success: function(user) {
        console.log(user.get('name')); //"Aaron Conran"
        console.log(user.orders().getCount()); //"1" -- there is only 1 order in the response above
 
        //we can iterate over the orders easily using the Associations API
        user.orders().each(function(order) {
            console.log(order.get('status')); //"shipped"
 
            //we can even iterate over each Order's OrderItems:
            order.orderItems().each(function(orderItem) {
                console.log(orderItem.get('title')); //"Sencha Touch"
            });
        });
    }
});
 

The Associations API is one of the most powerful new capabilities of Ext JS 4, and because the data package is shared with Sencha Touch, you can get a sneak peek at it by downloading Sencha Touch today or checking out the HasMany and BelongsTo API docs.

Validations

The last new feature we’re going to look at today is Validation. In Ext JS 3, the only place that validations could be performed was inside a form. In all of the examples above, we were manipulating data without a form, so we need to be able to validate at the Model level. Thankfully, Ext JS 4 has support for just that—let’s take a look:

 
Ext.regModel('User', {
    fields: ['id', 'name', 'email', 'height'],
 
    validations: [
        {type: 'presence', field: 'id'},
        {type: 'length', field: 'name', min: 2},
        {type: 'format', field: 'email', matcher: /[a-z]@[a-z].com/}
    ]
});
 

We set up three simple validations on our User model. First, we say that the ‘id’ field must be present, then we say that the ‘name’ field must be at least 2 characters long, and finally we use a very simple regular expression to match the format of the ‘email’ field.

We can use these validations to ensure that the data in our model is correct. Here we create a new User without a valid name and missing an ID, and ask it to validate itself:

 
var ed = new User({
    email: "ed@sencha.com"
});
 
var validation = ed.validate();
 
console.log(validation.isValid()); //false
 
//we can easily output or manipulate any validation errors
validation.each(function(error) {
    console.log(error.field + " " + error.message);
});
 
//in this case, the output looks like this:
"id must be present"
"name is the wrong length"
 

Find out more

We’ve looked at the four cornerstones of the new Model class—Fields, Proxies, Associations and Validations. Mastering these four concepts will enable you to create advanced applications using Ext JS 4 and Sencha Touch. To find out more about the data package, check out our recent blog post and browse through the extensive data package documentation in the Sencha Touch docs.

There are 35 responses. Add yours.

Jay Garcia

4 years ago

Have you guys done performance tests w/ the new association modeling?

cmwalolo

4 years ago

Does the data model get the children (only on demand) using the REST functionality as in this example ?

GET /user/123/order/1

GET /user/123/order/1/orderitems or

GET /order/1/orderitems

Alex

4 years ago

1. Is there a reason why validations are a separate property of the model, rather than part of the field definition itself (like the conversion functions)?  If the validation is tied to a single field, it just feels like it would be more natural as part of that field’s definition.

2. Can you do multi-field validations?  i.e. the valid value of field B depends on the value of field A.

John Dunlap

4 years ago

Looks awesome! You guys are definitely headed in the right direction here. However, I do have some comments that I would hope you would take as constructive criticism and not an effort to belittle your efforts because I can assure you that is not my intention!!!

Having said that, I’m wondering the same thing as cmwalolo.

Your new data package reminds me a lot of the JPA(Java Persistence API) which is a good thing in my book. However, JPA allows you to declaratively set the fetch type that you want to use to query an association. You set FetchType.LAZY on an association so that relationships between entities are not queried unless the data is actually needed or FetchType.EAGER on the association to immediately query all associated entities. It’s actually pretty important from a performance standpoint. You can bring the server to its knees pretty easily if you have a lot of entities with FetchType.EAGER associations to other entities because you end up unwittingly pulling large chunks of the database into memory with a single method call. The examples above remind me of FetchType.EAGER and I am worried that this could result in pulling a lot of data across the wire that your application may or may not have needed.

Again, I appreciate your work, your work looks awesome, and I think you’re headed in the right direction!

Cheers!
-John

Dimitris

4 years ago

I second question (2) from Alex.
One of the most complex validations I’ve ever had to work with and one of the trickiest to get right is field inter-dependent validation. Let me give you a few examples:
1) Either field A or B is mandatory.
2) Neither A or B is mandatory, but if C is filled, one of them needs to be filled too.
etc.
Of course, most of the times you end up writing a custom function for these validations, but I’d like to see a framework that handles these in an elegant and consistent manner.

Christiaan

4 years ago

In your example where you regModel User, you say hasMany: ‘Orders’ (plural). I dont see anything called Orders. I see a model called order (singular).

What’s the meaning of a hasMany property referring to something that does not exist in the example? The Sencha Touch API docs shows this (which makes more sense)
hasMany: {model: ‘Product’, name: ‘products’}

Alexander Hartmaier

4 years ago

I hope it will be also able to deal with mightHave relationships (when the foreign key column is allowed to be null).

Dmitriy Pashkevich

4 years ago

1) From the example above it seems that the convert function for a field is called automatically because of the line ‘console.log(abe.get(‘height’)); //logs 193 (inches converted to centimeters)’. Is this true or there’s an error in this example?

2) I second Christiaan’s question about hasMany with plural word as value. Maybe this is resolved automatically because there’s backward reference belongsTo in the Order model? And not specifying belongsTo in your example would break things?

3) Are the validations defined for a model only invoked manually or I can configure so that the validation is automatically performed, say, on ‘save’ action (similarly to form submit)?

daiei27

4 years ago

No wishlist here.  Just wanna say I welcome the improvements and I know you’re working hard to add more all the time.

Can’t wait to try version 4 whenever it comes out!

daiei27

4 years ago

P.S.  Thanks for these great blog entries.

John Thompson

4 years ago

Looks pretty cool - can’t wait to try it out!

Nicolas BUI

4 years ago

It sound cool, but i would like to see real world usage smile When will we able to have an preview or beta release ?

Best regards and hang on !

Francesco

4 years ago

I have the same doubt as comment #2

Martijn

4 years ago

What I do not see is ext.direct support in sencha touch how about extjs 4 and direct?
I love the approach of ext.direct….

Nicolas BUI

4 years ago

@Francesco, @cmwalolo In the example, it seem that is not “in demand” at all. Instead, you have to provided the associated data directly in the base request.

“On demand” or “lazy load” can be done but difficult to implement and would required many callback to be chained.

Andrew Peacock

4 years ago

Hi,
Looking great. Again, I’ve got the same concerns as ‘cmwalolo’ above. Being able to call just the “users” (to show a list of user data only) vs having to load “users” + “orders” + “order items”.... well, that raises some major concerns on my part. Why shuffle data around from server to client that might not be needed?
Regards,
Andy

Dmitry

4 years ago

Please fix your RSS, it floods my reader for a week already after each new post! And please, put a link on a news title.

Mike

4 years ago

I second the RSS comment.  I don’t want to unsubscribe, but may need to if I keep getting the entire feed history on every new post (using google reader)

Andreas

4 years ago

Yeah, the regular flooding with all recent posts on Sencha blog is starting to get on my nerves, too.

ibnesayeed

4 years ago

According to Microformat RESTful URL documentation given in the post, URLs should have an extension attached to them to convey the desired response format (no extension defaults to HTML).

proxy: {
  type: ‘rest’,
  url : ‘/users’,
  reader: ‘json’
}

For example, above configuration should generate following URLs.

GET /users.json
GET /users/1.json
POST /users/1.json[?_method=PUT/DELETE]

Similarly, if reader is configured for XML, then URLs should contain “.xml” suffix instead of .json.

Will ExtJS 4 support this convention automatically (or may be a configuration for this pattern?)

I am concerned about this, mainly because I use Ruby on Rails with ExtJS. And I configure my Rails application to respond in HTML, JSON and XML all three formats. HTML for SEO purposes, JSON to serve my ExtJS application and XML for cross-site web services.

Alexey

4 years ago

Do HTML 5 LocalStorage supports Associations?

Alexey

4 years ago

Does belongTo association supported inside XTemplate? Could you provide some simple example.

Alexey

4 years ago

I think current models are too coupled with proxies. In my application I use persistence.js and implementing custom proxy for it is real pain.

Also it is very bad that controls, such as DataView require to use ext store. In my case it would be much simplier to use my persistence.js models directly.

David Kaneda Sencha Employee

4 years ago

@Dmitry, @Mike, @Andreas — Very sorry for the troubles guys. We’ve had a few issues about the feeds pointed out since launch, and I get the feeling it reloads the feed each time we change the template. I don’t believe this will continue through new posts, but will keep a close eye on it.

AP

4 years ago

Looks like some great stuff. The only question I have is: will you also have a HasAndBelongsToMany relationship, where both Models can have associations to many of the other model? If this will not be there directly, will it be extensible enough for us to “add” it in?

Patrick Fraley

4 years ago

When I see this I get tears in my eyes, tears of joy.  My brain is already planing the marriage of these models with mongodb ...

You guys never cease to amaze me, every version of ext you delivered I though, awesome, can’t get better then this ...  And then the next version appears on the horizon smile  Keep up the good work ...

Looking forward to playing around with this ...

Andreas

4 years ago

@David Kaneda: Thanks, it happened again just today, so there still seems to be a problem with the RSS feed.

Andreas

4 years ago

... and it happened again, just 2 hours after my last comment. 20 new unread items in the sencha feed, all pretty familiar. *sigh*

agenzie di modelle

4 years ago

WOW ! This blog Looking soo great. Again,I love this post very much. I am aggre with you. This is your hopeful post.  I’ve got the same concerns as ‘cmwalolo’ above. Being able to call just the “users” (to show a list of user data only) vs having to load “users” + “orders” + “order items”.... well, that raises some major concerns on my part. Why shuffle data around from server to client that might not be needed?
Regards,    Thans for shering.

Jay

3 years ago

Can I use string as id? I noticed the documentation for model said it is strictly for number:
load( Number id, Object config ) : void STATIC

Real Chan

3 years ago

I’ve got the same concerns as ‘Christiaan’ above.

In your example where you regModel User, you say hasMany: ‘Orders’ (plural). I dont see anything called Orders. I see a model called order (singular).
What’s the meaning of a hasMany property referring to something that does not exist in the example? The Sencha Touch API docs shows this (which makes more sense)
hasMany: {model: ‘Product’, name: ‘products’}

I want to know how do you deal with them.

Real Chan

3 years ago

my data is xml file…

Peter

3 years ago

Regarding User data model, when I call User.load(‘123’,{..}), any idea I got the request like /users/123?_dc=12345788&id=123 rather than /users/123 ? How to remove parameter “id=123”?

Thanks,
Peter

Craig

3 years ago

I do like removing the store from the picture, but then the question pops: for something like a grid panel or docked items (toolbar etc) - they all require a store, therefore exception is thrown and no data displays (granted I am brand new to extjs all together… been a JQuery guy for some time now, so this may be a silly question) so… how do we deal with that?

xml editor

3 years ago

I agree with alex, the proxy issue is a real pain and it would be far easier to implement direct models.

Comments are Gravatar enabled. Your email address will not be shown.

Commenting is not available in this channel entry.