原文:http://www.sencha.com/learn/using-validations-and-associations-in-sencha-touch
This Tutorial is most relevant to Sencha Touch, 1.x.
Ext.ns('MyApp');
MyApp.Product = Ext.regModel('Product', {
fields: [
{name: 'id', type: 'int'},
{name: 'user_id', type: 'int'},
{name: 'name', type: 'string'},
{name: 'price', type: 'float'},
{name: 'sku', type: 'string'}
],
validations: [
{type: 'presence', name: 'name'},
{type: 'format', name: 'sku', matcher: /[0-9]{4}[A-Z]+/},
{type: 'length', name: 'name', min: 3}
],
proxy: {
type: 'localstorage',
id : 'products'
}
});
The model definition above is largely self-explanatory: we're defining a model called Product with four fields - id, name, price and sku, plus several validation rules about those fields. The field definitions have always been present in Sencha Touch and validations follow the same format. In each case, we specify a field and a type of validation. Some validations take additional optional configuration - for example the length validation can take min and max properties, format can take a matcher, etc. There are five validations built into Sencha Touch and adding custom rules is easy. First, let's meet the ones built right in:
-
presence
- simply ensures that the field has a value. Zero counts as a valid value but empty strings do not. length
- ensures that a string is between a min and max length. Both constraints are optional. format
- ensures that a string matches a regular expression format. In the example above we ensure that the sku is 4 numbers followed by at least one letter. inclusion
- ensures that a value is within a specific set of values (e.g. ensuring gender is either male or female). exclusion
- ensures that a value is not one of the specific set of values (e.g. blacklisting usernames like 'admin').
var product = new MyApp.Product({
name : 'Sencha Touch',
sku : 'not a valid sku',
price: 99
});
var errors = product.validate();
errors.isValid()) //returns 'false' as there were validation errors
errors.items; //returns the array of all errors found on this model instance
errors.forField('sku'); //returns the errors for the sku field
The key function here is validate(), which runs all of the configured validations and returns an Ext.data.Errors object. This simple object is just a collection of any errors that were found, plus some convenience methods such as isValid() - which returns true if there were no errors on any field - and forField(), which returns all errors for a given field. In our e-commerce system each Product is created by a User, so let's set up the User model now:
MyApp.User = Ext.regModel('User', {
fields: [
{name: 'id', type: 'int'},
{name: 'name', type: 'string'},
{name: 'gender', type: 'string'},
{name: 'username', type: 'string'}
],
validations: [
{type: 'inclusion', name: 'gender', list: ['male', 'female']},
{type: 'exclusion', name: 'username', list: ['admin']}
],
associations: [
{type: 'hasMany', model: 'Product', name: 'products'}
],
proxy: {
type: 'localstorage',
id : 'users'
}
});
Defining the User follows the same pattern as we used for defining the Product - we set up the fields and a couple of validations. In this case, our validations are expecting the gender field to be either male or female, and the username to be anything but 'admin'. This time, however, we've also added an association to the model. There are two main types of association in Sencha Touch - hasMany and belongsTo. In our application each User creates many Products, so we create a hasMany association linking User to Product. Associations give us a powerful way to manipulate related data. For example, loading all of the Products for a particular User is very easy:
var user = new MyApp.User({id: 10});
//loads all products where user_id = 10
user.products().load({
callback: function(records, operation) {
alert(records.length);
}
});
Let's break down what the code above is actually doing. The association that we defined have created a new method on the User object called products(). This method returns an Ext.data.Store that is automatically filtered to only load Products where the user_id is equal to the User instance's id (which is 10 in this case). Because we're in the browser and a long way from the database, all loading and saving operations are asynchronous, so we have to pass a callback function to the generated Products store's load method. This callback is given the records that are loaded as well as the Ext.data.Operation object that is used to load them. Associations aren't just helpful for loading data - they're useful for creating new records too:
user.products().add({
name: 'Ext JS 4.0',
sku : '1234A'
});
user.products().sync();
Here we instantiate a new Product, which is automatically given the User's id in the user_id field. Calling sync() saves the new Product via its configured Proxy - this, again, is an asynchronous operation to which you can pass a callback if you want to be notified when the operation completed. It's usually useful to have both sides of a relationship know about the association, so let's update our Product model definition:
MyApp.Product = Ext.regModel('Product', {
//same fields and validations as before
...
associations: [
{type: 'belongsTo', model: 'User'}
]
});
The belongsTo association also generates new methods on the model, here's how we can use those:
var product = new MyApp.Product({id: 100});
product.getUser(function(user) {
//do something with the loaded user model
});
product.setUser(100, {
callback: function(product, operation) {
if (operation.wasSuccessful()) {
alert('Product user updated');
} else {
alert('Product user could not be updated');
}
}
});
Once more, the loading function (getUser) is asynchronous and requires a callback function to get at the user instance. The setUser method simply updates the foreign_key (user_id in this case) to 100 and saves the Product model. As usual, callbacks can be passed in that will be triggered when the save operation has completed - whether successful or not. The best way to find out more about validations and associations is to
check out the updated Model documentation
, as well as the
docs on the hasMany
and
belongsTo associations
. Finally, be aware that since we're still in beta, the API for this new functionality may change slightly before version 1.0.