ExtJS4: Form validation via Model binding


Some days ago Sencha released ExtJS 4, so it’s a good time to think about the whole programming of your application and maybe do a rewrite to get your application to a higher level.

At least thats what happened in our company. Well, the reflection about what’s good and what’s not so good on DiLoc|Rail started a while ago and we had many ideas for improvements.
One of them is to use the new model-classes of ExtJS.

There is a possibility to load model-records in forms but we thought would be nice to validate the forms also with the model validations and not by the vtypes. With the first release of ExtJS 4.0.0 this isn’t really supported out of the box so we decided to implement our own ux extensions:
the Ext.ux.form.ModelValidatedPanel and Ext.ux.form.ModelValidatedBasic.

Here is some example code:

form.js

Ext.require([
	'Ext.form.*',
	'Ext.layout.container.Column',
	'Ext.tab.Panel'
]);
 
Ext.onReady(function(){
	Ext.QuickTips.init();
	var bd = Ext.getBody();
 
	/*
	 * define a User model
	 */
	Ext.define('User', {
		extend: 'Ext.data.Model',
		fields: [
			{name: 'name', type: 'string'},
			{name: 'age', type: 'int'},
			{name: 'gender', type: 'string'},
			{name: 'username', type: 'string'},
			{name: 'alive', type: 'boolean', defaultValue: true}
		],
		validations: [
			{type: 'presence', field: 'age'},
			{type: 'length', field: 'name', min: 2},
			{type: 'inclusion', field: 'gender', list: ['Male', 'Female']},
			{type: 'exclusion', field: 'username', list: ['Admin', 'Operator']},
			{type: 'format', field: 'username', matcher: /([a-z]+)[0-9]{2,3}/}
		]
	});
 
	/**
	 * define a handler to echo some response in console.log
	 */
	var testHandler = function() {
		console.log("Save Button Clicked - FormValid?", simpleForm.getForm().isValid());
		console.log("ModelRecord in form", simpleForm.getModelRecord());
	};
 
	/**
	 * define a simple user form
	 */
	var simpleForm = Ext.create('Ext.ux.form.ModelValidatedPanel', {
		frame:true,
		title: 'User Form',
		bodyStyle:'padding:5px 5px 0',
		width: 350,
		fieldDefaults: {
			msgTarget: 'side',
			labelWidth: 75
		},
		defaultType: 'textfield',
		defaults: {
			anchor: '100%'
		},
		model: "User",
		items: [{
			fieldLabel: 'Name',
			name: 'name'
		},{
			fieldLabel: 'Age',
			name: 'age',
			xtype: 'numberfield'
		},{
			fieldLabel: 'Gender',
			name: 'gender'
		}, {
			fieldLabel: 'Username',
			name: 'username'
		}, {
			fieldLabel: 'Alive',
			name: 'alive'
		}],
		buttons: [{
			text: 'Save',
			handler: testHandler,
			formBind: true,
			scope: this
		},{
			text: 'Cancel'
		}]
	});
	simpleForm.render(document.body);
 
	/**
	 * define a user record and fill the form
	 */
	var user = Ext.ModelManager.create({
		name : 'Max',
		age  : 24,
		gender: 'Male',
		username: 'con42'
	}, 'User');
	simpleForm.setModelRecord(user);
 
});

I declare a “User”-model and set up a simple form.
Note: I give the model-type into the form panel and that’s it! I can user the formBind functionality of the button because the model-validated-form handles nearly like the normal Ext form.
I have also the opportunity to fill the form with a user-record by simply use setModelRecord(object), a function the new Ext classes provide for config variables.

What happens under the hood?

The Ext.ux.form.ModelValidatedPanel class extends Ext.form.Panel and just sets up the basic functionality as create a empty model record, turn off the normal field validation via the vtype, creates the model validated basic form and forwards the model record to the form if it’s set.

Link to ModelValidatedPanel.js

The ModelValidateBasic class extends the Ext.form.Basic and provides a new method validateFieldByModel(_field). This is called on change events of the fields. It’s saving the value in the model-record and validates it with the model.validate() function.
To just change the validity of the changed field I clear the error object to just contain the associated error. I also rebuild the validitychange event behavior of the fields so that the bindForm functionality works as used.
At the end (or in the middle) I overwrite the hasInvalidField() and isValid() functions of the form to validate the form/record with the model and behave in the same way as used (marking and not marking fields).

Link to ModelValidatedBasic.js

So at this point of development I have just tested it with some test forms and not in our application, so maybe there is a little more work to do on it, but for my test situations it works really fine.
If you can use it or have any suggestions to improve, you’re welcome to comment.

Information and Links

Join the fray by commenting, tracking what others have to say, or linking to it from your blog.


Other Posts

Write a Comment

Take a moment to comment and tell us what you think. Some basic HTML is allowed for formatting.

Reader Comments

That is some real nifty work pal. Thanks for letting us in on it! :)

Hello, I’m using you form extention in a project and i noticed that is seems there is a problem wuth the getter of the modelRecord property. If a print the result from the getter is not the same as the property. I will investigate further and let you know.

It is quite surprising that extjs4 has grid binding to stores but not form binding to models. You have a good start here.

Wouldn’t it be nice if the properties grid could do this too?

I wonder why the widget field validation is more powerful than the model validation.

Using your example it seems the formBind attribute of the form is ignored. The button is not disabled when there are validation errors and you are able to save the record. Any idea how to fix this?

Sorry for not responding so far. We have been and are very busy while porting our DiLoc application to ExtJS4.
During this porting we now use the ModelValidatedPanel and form in our application and not just for some feasibility study. And we found also some more bugs and problems while using it. We fixed many of them and i’ll write a blog post with the changes as soon as there is time free for that ;)