Source : www.javabeat.net


Ext JS 3.0 Cookbook

Author : PacktPub
Date : Mon Nov 23rd, 2009
Topic : java books 

Ext JS 3.0 Cookbook

In the world of Rich Internet Applications (RIA) development, Ext JS stands out as a cross-browser JavaScript library that offers the applications developer a powerful toolset. With a set of customizable user interface widgets similar to those found in desktop operating systems, an effective data binding model, a comprehensive programming interface for manipulating the Document Object Model and communicating with the server, a committed development team, and an enthusiastic users' community, the Ext JS library is a great choice for today's web builders.

This book addresses many of the library's features, from the perspective of how they can be used to solve the real-world challenges faced by those that develop internet applications.

What This Book Covers

Chapter 1—The DOM and Data Types, the Ext JS way, covers the Ext JS facilities for working with different browsers, the Document Object Model (DOM), and the Ext JS data types. Its recipes will teach you how to detect browsers and platforms, manipulate the DOM, encode and decode JSON and URL data, and work with arrays, strings, numbers, and dates. In this chapter you will also learn how to augment the features of the Ext JS classes, as well as how to incorporate library features into your own JavaScript classes.

Chapter 2—Laying Out a Rich User Interface, will help you to learn how to use layouts to create user interfaces with the Ext JS widgets. This chapter explains the common uses of some of the library's native layouts, and teaches you how to combine and augment these layouts to build great-looking and functional interfaces.

Chapter 3—Load, Validate, and Submit Forms, focuses on forms processing. In this chapter you will find tips and techniques for effective field validation, details on how to load data into forms, as well as advice on how to use forms to upload files to the server. As in previous chapters, in Chapter 3 you will find examples of how to extend the library's classes, in particular, how to create custom form fields.

Chapter 4—Fun with Combo Boxes and Date Fields, is a continuation of the form fields recipes introduced in Chapter 3. Chapter 4 is loaded with examples of how to use the ComboBox and DateField form components. It will teach you how to take advantage of ComboBox features like paging and item templates, as well as how to safely capture master-details and dates range input.

Chapter 5—Using Grid Panels to Display and Edit Tabular Data, consists of recipes that encompass the display of data using Ext JS grid panels. They explain different approaches to loading, editing, and saving data, as well as looking at how to implement features like grouping and group summaries. Chapter 5 uses techniques introduced in Chapter 3 to teach you how the Ext JS GridPanel widget can be enhanced through the use of plugins.

Chapter 6—More Applications of Grid and List views, expands on Chapter 5's examples. It explains multiple ways to use the GridPanel widget to display master-details relationships, approaches to displaying tabular data more efficiently, and how to edit data with the new RowEditor class.

Chapter 7—Keeping Tabs on your Trees, explores the TabPanel and Treview widgets. Besides how to use their main features, in this chapter you will also learn how to take advantage of plugins to enhance these widgets. This chapter also teaches you how to implement usage scenarios involving drag-and-drop and master-details displays with tree views and panels.

Chapter 8—Making Progress with Menus and Toolbars, consists of recipes that examine the commonly-used menu items, as well as the different ways to set up toolbars and progress bars in your applications.

Chapter 9—Well Charted Territory, consists of recipes that explain the typical usage scenarios of the chart widget, as well as approaches to configuring and customizing the look of the slider widget.

Chapter 10—Patterns in Ext JS, provides examples of some important design patterns used to build robust and flexible applications. These examples cover techniques such as resource management using lazy instantiation, prototyping and encapsulating using code modules and pre-configured classes, dependency management with publish/subscribe models, and state preservation.

Load, Validate, and Submit Forms

You will learn the following recipes in this chapter:

  • Specifying the required fields in a form
  • Setting the minimum and maximum length allowed for a field's value
  • Changing the location where validation errors are displayed
  • Deferring field validation until form submission
  • Creating validation functions for URLs, email addresses, and other types of data
  • Confirming passwords and validating dates using relational field validation
  • Rounding up your validation strategy with server-side validation of form fields
  • Loading form data from the server
  • Serving the XML data to a form
  • Using forms for file uploads
  • Building friendlier forms using text hints

Introduction

This chapter focuses on the topic of processing forms. The journey will include client-side and server-side field validation, form loading, submission, field customization, and layout techniques that will make it a breeze to build great-looking and friendly forms.

Specifying the required fields in a form

This recipe uses a login form as an example to explain how to create required fields in a form.

How to do it...

  1. Initialize the global QuickTips instance:
    Ext.QuickTips.init();
  2. Create the login form:
    
    	var loginForm = { xtype: 'form',
    		id: 'login-form',
    		bodyStyle: 'padding:15px;background:transparent',
    		border: false,
    		url:'login.php',
    		items: [{
    			xtype: 'box',
    			autoEl: { tag: 'div',
    				html: '<div class="app-msg">
    					<img src="img/magic-wand.png" class="app-img" />
    					Log in to The Magic Forum</div>'}
    		},
    		{ xtype: 'textfield', id: 'login-user',
    			fieldLabel: 'Username',
    			allowBlank: false
    		},
    		{ xtype: 'textfield', id: 'login-pwd',
    			fieldLabel: 'Password',
    			inputType: 'password',allowBlank: false
    		}],
    		buttons: [{
    			text: 'Login',
    			handler: function() {
    				Ext.getCmp('login-form').getForm().submit();
    			}
    		},
    		{
    			text: 'Cancel',
    			handler: function() {
    				win.hide();
    			}
    		}]
    	}
    
  3. Create the window that will host the login form:
    
    	Ext.onReady(function() {
    		win = new Ext.Window({
    			layout: 'form',
    			width: 340,
    			autoHeight: true,
    			closeAction: 'hide',
    			items: [loginForm]
    		});
    		win.show();
    	});
    

How it works...

Initializing the QuickTips singleton allows the form's validation errors to be shown as tool tips. When the form is created, each required field needs to have the allowblank configuration option set to false:


	{ xtype: 'textfield', id: 'login-user', fieldLabel: 'Username',
		allowBlank: false
	},
	{ xtype: 'textfield', id: 'login-pwd', fieldLabel: 'Password',
		inputType: 'password',allowBlank: false
	}

Setting allowBlank to false activates a validation rule that requires the length of the field's value to be greater than zero.

There's more...

Use the blankText configuration option to change the error text when the blank validation fails. For example, the username field definition in the previous code snippet can be changed as shown here:


	{ xtype: 'textfield', id: 'login-user', fieldLabel: 'Username',
		allowBlank: false, blankText:'Enter your username'
	}

The resulting error is shown in the following figure:

Validation rules can be combined and even customized. Other recipes in this chapter explain how to range-check a field's length, as well as how to specify the valid format of the field's value.

See also...

  • The next recipe titled Setting the minimum and maximum length allowed for a field's value explains how to restrict the number of characters entered in a field
  • The Changing the location where validation errors are displayed recipe, covered later in this chapter, shows how to relocate a field's error icon
  • Refer to the Deferring field validation until form submission recipe, covered later in this chapter, to learn how to validate all fields at once upon form submission, instead of using the default automatic field validation
  • The Creating validation functions for URLs, email addresses, and other types of data recipe, covered later in this chapter, explains the validation functions available in Ext JS
  • The Confirming passwords and validating dates using relational field validation recipe, covered later in this chapter, explains how to perform validation when the value of one field depends on the value of another field
  • The Rounding up your validation strategy with server-side validation of form fields recipe, covered later in this chapter, explains how to perform server-side validation

Setting the minimum and maximum length allowed for a field's value

This recipe shows how to set the minimum and maximum number of characters allowed for a text field. The way to specify a custom error message for this type of validation is also explained.

The login form built in this recipe has username and password fields of a login form whose lengths are restricted:

How to do it...

  1. The first thing is to initialize the QuickTips singleton:
    Ext.QuickTips.init();
  2. Create the login form:
    
    	var loginForm = { xtype: 'form',
    		id: 'login-form',
    		bodyStyle: 'padding:15px;background:transparent',
    		border: false,
    		url:'login.php',
    		items: [
    			{ xtype: 'box', autoEl: { tag: 'div',
    					html: '<div class="app-msg">
    					<img src="img/magic-wand.png" class="app-img" />
    					Log in to The Magic Forum>/div>'
    				}
    			},
    			{ xtype: 'textfield', id: 'login-user',
    				fieldLabel: 'Username',
    				allowBlank: false, minLength: 3, maxLength: 32
    			},
    			{ xtype: 'textfield', id: 'login-pwd',
    				fieldLabel: 'Password', inputType: 'password',
    				allowBlank: false, minLength: 6, maxLength: 32,
    				minLengthText: 'Password must be at least 6 characters
    				long.'
    			}
    		],
    		buttons: [{
    			text: 'Login',
    			handler: function() {
    				Ext.getCmp('login-form').getForm().submit();
    			}
    		}, {
    			text: 'Cancel',
    			handler: function() {
    				win.hide();
    			}
    		}]
    	}
    
  3. Create the window that will host the login form:
    
    	Ext.onReady(function() {
    		win = new Ext.Window({
    			layout: 'form',
    			width: 340,
    			autoHeight: true,
    			closeAction: 'hide',
    			items: [loginForm]
    		});
    		win.show();
    	});
    

How it works...

After initializing the QuickTips singleton, which allows the form's validation errors to be shown as tool tips, the form is built.

The form is an instance of Ext.form.FormPanel. The username and password fields have their lengths restricted by the way of the minLength and maxLength configuration options:


	{ xtype: 'textfield', id: 'login-user', fieldLabel: 'Username',
		allowBlank: false, minLength: 3, maxLength: 32
	},
	{ xtype: 'textfield', id: 'login-pwd',
		fieldLabel: 'Password', inputType: 'password',
		allowBlank: false, minLength: 6, maxLength: 32,
		minLengthText: 'Password must be at least 6 characters long.'
	}

Notice how the minLengthText option is used to customize the error message that is displayed when the minimum length validation fails:


	{ xtype: 'textfield', id: 'login-pwd',
		fieldLabel: 'Password', inputType: 'password',
		allowBlank: false, minLength: 6, maxLength: 32,
		minLengthText: 'Password must be at least 6 characters long.'
	}

As a last step, the window that will host the form is created and displayed.

There's more...

You can also use the maxLengthText configuration option to specify the error message when the maximum length validation fails.

See also...

  • The previous recipe, Specifying the required fields in a form, explains how to make some form fields required
  • The next recipe, Changing the location where validation errors are displayed, shows how to relocate a field's error icon
  • Refer to the Deferring field validation until form submission recipe (covered later in this chapter) to learn how to validate all fields at once upon form submission, instead of using the default automatic field validation
  • Refer to the Creating validation functions for URLs, email addresses, and other types of data recipe (covered later in this chapter) for an explanation of the validation functions available in Ext JS
  • The Confirming passwords and validating dates using relational field validation recipe (covered later in this chapter) explains how to perform validation when the value of one field depends on the value of another field
  • The Rounding up your validation strategy with server-side validation of form fields recipe (covered later in this chapter) explains how to perform server-side validation

Changing the location where validation errors are displayed

A popular way to display validation errors is to place an error icon to the side of the invalid fields As shown in the next screenshot, this recipe uses a login form to explain how to implement this technique.

How to do it...

  1. Initialize the QuickTips singleton:
    Ext.QuickTips.init();
  2. Create the login form:

    
    	var loginForm = { xtype: 'form',
    		id: 'login-form',
    		bodyStyle: 'padding:15px;background:transparent',
    		border: false,
    		url:'login.php',
    		items: [
    			{ xtype: 'box',
    				autoEl: { tag: 'div',
    					html: '<div class="app-msg">
    						<img src="img/magic-wand.png"
    							class="app-img" />Log in to The Magic Forum</div>'
    				}
    			},
    			{ xtype: 'textfield', id: 'login-user',
    				fieldLabel: 'Username',
    				allowBlank: false, minLength: 3, maxLength: 32,
    				msgTarget:'side'
    			},
    			{ xtype: 'textfield', id: 'login-pwd',
    				fieldLabel: 'Password',
    				inputType: 'password', allowBlank: false, minLength: 6,
    				maxLength: 32, minLengthText: 'Password must be at least 6
    				characters long.',
    				msgTarget:'side'
    			}
    		],
    		buttons: [{
    			text: 'Login',
    			handler: function() {
    				Ext.getCmp('login-form').getForm().submit();
    			}
    		},
    		{ text: 'Cancel',
    			handler: function() {
    				win.hide();
    			}
    		}]
    	}
    
  3. Create the window that will host the login form:
    
    	Ext.onReady(function() {
    		win = new Ext.Window({
    			layout: 'form',
    			width: 340,
    			autoHeight: true,
    			closeAction: 'hide',
    			items: [loginForm]
    		});
    		win.show();
    	});
    

How it works...

First, initializing the QuickTips singleton allows the form's validation errors to be shown as tool tips.

Next, the form is built. To display error icons at the righthand side of the invalid fields, the msgTarget option is set to side:


	{ xtype: 'textfield', id: 'login-user', fieldLabel: 'Username',
		allowBlank: false, minLength: 3, maxLength: 32,
		msgTarget:'side'
	},
	{ xtype: 'textfield', id: 'login-pwd', fieldLabel: 'Password',
		inputType: 'password', allowBlank: false, minLength: 6,
		maxLength: 32, minLengthText: 'Password must be at least 6
		characters long.',
		msgTarget:'side'
	}

The last step consists of creating and displaying the window that hosts the form.

There's more...

The value of the msgTarget configuration option defaults to qtip, which displays a quick tip when the user hovers over the field. Besides side, used in this recipe, you can use these remaining values to change how the validation error displays:

  • title: Displays a default browser title attribute pop up.
  • under: Adds a block div beneath the field containing the error text.
  • [element id]: Adds the error text directly to the innerHTML of the specified element.

See also...

  • The Specifying the required fields in a form recipe, covered previously in this chapter, explains how to make some form fields required
  • The previous recipe, Setting the minimum and maximum length allowed for a field's value, explains how to restrict the number of characters entered in a field
  • The following recipe, Deferring field validation until form submission, explains how to validate all fields at once upon form submission, instead of using the default automatic field validation
  • Refer to the Creating validation functions for URLs, email addresses, and other types of data recipe, covered later in this chapter, for an explanation of the validation functions available in Ext JS
  • The Confirming passwords and validating dates using relational field validation recipe, covered later in this chapter, explains how to perform validation when the value of one field depends on the value of another field
  • The Rounding up your validation strategy with server-side validation of form fields recipe, covered later in this chapter, explains how to perform server-side validation

Deferring field validation until form submission

Generally, a form field is validated on the client-side when the keyup event occurs, or when the field loses focus. Occasionally, you might prefer the validation to be performed just before the form is submitted.

In this recipe, a login form is built, with the validation deferred until the form is submitted. Focus changes or the keyup event will not trigger field validation, as shown in the following screenshot:

As shown in the next screenshot, submitting the form will trigger the validation:

How to do it...

  1. Initialize the QuickTips singleton:
    Ext.QuickTips.init();
  2. Create the login form:
    
    	var loginForm = { xtype: 'form',
    		id: 'login-form',
    		bodyStyle: 'padding:15px;background:transparent',
    		border: false,
    		url:'login.php',
    		items: [
    			{ xtype: 'box', autoEl: { tag: 'div',
    					html: '<div class="app-msg">
    					<img src="img/magic-wand.png" class="app-img" />
    					Log in to The Magic Forum</div>'
    				}
    			},
    			{ xtype: 'textfield', id: 'login-user',
    				fieldLabel: 'Username',
    				allowBlank: false, minLength: 3, maxLength: 32,
    				msgTarget:'side',validationEvent:false
    			},
    			{ xtype: 'textfield', id: 'login-pwd',
    				fieldLabel: 'Password', inputType: 'password',
    				allowBlank: false, minLength: 6, maxLength: 32,
    				minLengthText: 'Password must be at least 6 characters
    				long.',
    				msgTarget:'side',validationEvent:false
    			}
    		],
    		buttons: [{
    			text: 'Login',
    			handler: function() {
    				Ext.getCmp('login-form').getForm().submit();
    			}
    		},
    		{
    			text: 'Cancel',
    			handler: function() {
    				win.hide();
    			}
    		}]
    	}
    
  3. Create the window that will host the login form:
    
    	Ext.onReady(function() {
    		win = new Ext.Window({
    			layout: 'form',
    			width: 340,
    			autoHeight: true,
    			closeAction: 'hide',
    			items: [loginForm]
    		});
    		win.show();
    

How it works...

The first step consists of initializing the QuickTips singleton so that the form's validation errors can be shown as tool tips.

Next, the form is built. In order to disable automatic validation, the validationEvent configuration option is set to false:


	{ xtype: 'textfield', id: 'login-user', fieldLabel: 'Username',
		allowBlank: false, minLength: 3, maxLength: 32,
		msgTarget:'side',validationEvent:false
	},
	{ xtype: 'textfield', id: 'login-pwd', fieldLabel: 'Password',
		inputType: 'password',allowBlank: false,
		minLength: 6, maxLength: 32,
		minLengthText: 'Password must be at least 6 characters
		long.',
		msgTarget:'side',validationEvent:false
	}

The validationEvent option indicates the event that should initiate field validation. The default value for this option is keyup. Like in the code above, when set to false, it simply disables automatic validation for the field.

The last step consists of creating and displaying the window that hosts the form.

There's more...

This recipe is a good solution in some data-intensive applications, where validating a field with every keystroke or focus change might become an annoyance for the user.

When you want to disable client-side field validation altogether and place on the server all of the responsibility for the validation, you can add the clientValidation = false configuration option to the submit() function call:


	Ext.getCmp('login-form').getForm().submit({clientValidation:false});

See also...

  • The Specifying the required fields in a form recipe, covered earlier in this chapter, explains how to make some form fields required
  • The Setting the minimum and maximum length allowed for a field's value recipe, covered earlier in this chapter, explains how to restrict the number of characters entered in a field
  • The previous recipe, Changing the location where validation errors are displayed, shows how to relocate a field's error icon
  • Refer to the next recipe, Creating validation functions for URLs, email addresses, and other types of data, for an explanation of the validation functions available in Ext JS
  • The Confirming passwords and validating dates using relational field validation recipe, covered later in this chapter, explains how to perform validation when the value of one field depends on the value of another field
  • The Rounding up your validation strategy with server-side validation of form fields recipe, covered later in this chapter, explains how to perform server-side validation

Creating validation functions for URLs, email addresses, and other types of data

Ext JS has an extensive library of validation functions. This is how it can be used to validate URLs, email addresses, and other types of data.

The following screenshot shows email address validation in action:

This screenshot displays URL validation in action:

How to do it...

  1. Initialize the QuickTips singleton:
    Ext.QuickTips.init();
  2. Create a form with fields that accept specific data formats:
    
    	Ext.onReady(function() {
    		var commentForm = new Ext.FormPanel({
    			frame: true,
    			title: 'Send your comments',
    			bodyStyle: 'padding:5px',
    			width: 550,
    			layout: 'form',
    			defaults: { msgTarget: 'side' },
    			items: [
    				{ xtype: 'textfield',
    					fieldLabel: 'Name',
    					name: 'name',
    					anchor: '95%',
    					allowBlank: false
    				}, {
    					xtype: 'textfield',
    					fieldLabel: 'Email',
    					name: 'email',
    					anchor: '95%',
    					vtype: 'email'
    				}, {
    					xtype: 'textfield',
    					fieldLabel: 'Web page',
    					name: 'webPage',
    					vtype: 'url',
    					anchor: '95%'
    				}, {
    					xtype: 'textarea',
    					fieldLabel: 'Comments',
    					name: 'comments',
    					anchor: '95%',
    					height: 150,
    					allowBlank: false
    				}],
    				buttons: [{
    					text: 'Send'
    				}, {
    					text: 'Cancel'
    				}]
    			});
    			commentForm.render(document.body);
    		});
    

How it works...

The vtype configuration option specifies which validation function will be applied to the field.

There's more...

Validation types in Ext JS include alphanumeric, numeric, URL, and email formats. You can extend this feature with custom validation functions, and virtually, any format can be validated. For example, the following code shows how you can add a validation type for JPG and PNG files:


	Ext.apply(Ext.form.VTypes, {
		Picture: function(v) {
			return /^.*\.(jpg|JPG|png|PNG)$/.test(v);
		},
		PictureText: 'Must be a JPG or PNG file';
	});

If you need to replace the default error text provided by the validation type, you can do so by using the vtypeText configuration option:


	{
		xtype: 'textfield',
		fieldLabel: 'Web page',
		name: 'webPage',
		vtype: 'url',
		vtypeText: 'I am afraid that you did not enter a URL',
		anchor: '95%'
	}

See also...

  • The Specifying the required fields in a form recipe, covered earlier in this chapter, explains how to make some form fields required.
  • The Setting the minimum and maximum length allowed for a field's value recipe, covered earlier in this chapter, explains how to restrict the number of characters entered in a field.
  • The Changing the location where validation errors are displayed recipe, covered earlier in this chapter, shows how to relocate a field's error icon.
  • Refer to the previous recipe, Deferring field validation until form submission, to know how to validate all fields at once upon form submission, instead of using the default automatic field validation.
  • The next recipe, Confirming passwords and validating dates using relational field validation, explains how to perform validation when the value of one field depends on the value of another field.
  • The Rounding up your validation strategy with server-side validation of form fields recipe (covered later in this chapter) explains how to perform server-side validation.

Confirming passwords and validating dates using relational field validation

Frequently, you face scenarios where the values of two fields need to match, or the value of one field depends on the value of another field.

Let's examine how to build a registration form that requires the user to confirm his or her password when signing up.

How to do it…

  1. Initialize the QuickTips singleton:
    Ext.QuickTips.init();
  2. Create a custom vtype to handle the relational validation of the password:
    
    	Ext.apply(Ext.form.VTypes, {
    		password: function(val, field) {
    			if (field.initialPassField) {
    				var pwd = Ext.getCmp(field.initialPassField);
    				return (val == pwd.getValue());
    			}
    			return true;
    		},
    		passwordText: 'What are you doing?<br/>The passwords entered
    		do not match!'
    	});
    
  3. Create the signup form:
    
    	var signupForm = { xtype: 'form',
    		id: 'register-form',
    		labelWidth: 125,
    		bodyStyle: 'padding:15px;background:transparent',
    		border: false,
    		url: 'signup.php',
    		items: [
    			{ xtype: 'box',
    				autoEl: { tag: 'div',
    					html: '<div class="app-msg"><img src="img/businessmanadd.
    						png" class="app-img" />
    					Register for The Magic Forum</div>'
    				}
    			},
    			{ xtype: 'textfield', id: 'email', fieldLabel: 'Email',
    				allowBlank: false, minLength: 3,
    				maxLength: 64,anchor:'90%', vtype:'email'
    			},
    			{ xtype: 'textfield', id: 'pwd', fieldLabel: 'Password',
    				inputType: 'password',allowBlank: false, minLength: 6,
    				maxLength: 32,anchor:'90%',
    				minLengthText: 'Password must be at least 6 characters
    				long.'
    			},
    			{ xtype: 'textfield', id: 'pwd-confirm',
    				fieldLabel: 'Confirm Password', inputType: 'password',
    				allowBlank: false, minLength: 6,
    				maxLength: 32,anchor:'90%',
    				minLengthText: 'Password must be at least 6 characters
    				long.',
    				vtype: 'password', initialPassField: 'pwd'
    			}
    		],
    		buttons: [{
    			text: 'Register',
    			handler: function() {
    				Ext.getCmp('register-form').getForm().submit();
    			}
    		},
    		{
    			text: 'Cancel',
    			handler: function() {
    				win.hide();
    			}
    		}]
    	}
    
  4. Create the window that will host the signup form:
    
    	Ext.onReady(function() {
    		win = new Ext.Window({
    			layout: 'form',
    			width: 340,
    			autoHeight: true,
    			closeAction: 'hide',
    			items: [signupForm]
    		});
    		win.show();
    

How it works…

The first step is to initialize the QuickTips singleton. This allows the form's validation errors to show as tool tips.

Next, a custom vtype is created to support the relational validation of the password. A vtype consists of a validation function and the text to use when there is a validation error. The password validation function compares the values of the two password fields:


	password: function(val, field) {
		if (field.initialPassField) {
			var pwd = Ext.getCmp(field.initialPassField);
			return (val == pwd.getValue());
		}
		return true;
	}

When the validation fails, the error text is used:


	passwordText: 'What are you doing?
The passwords entered do not match!'

What follows is the creation and display of the signup form. Notice how the second password field has references to the custom vtype and the first password field. This allows the validation function in the custom vtype to work:


	{ xtype: 'textfield', id: 'pwd-confirm',
		fieldLabel: 'Confirm Password', inputType: 'password',
		allowBlank: false, minLength: 6, maxLength: 32,anchor:'90%',
		minLengthText: 'Password must be at least 6 characters long.',
		vtype: 'password', initialPassField: 'pwd'
	}

There's more...

You can also use this technique for validating date fields that act as a date range, where selecting an initial date in one field sets the minimum or maximum value for the other field.

See also…

  • The Specifying the required fields in a form recipe, covered earlier in this chapter, explains how to make some form fields required.
  • The Setting the minimum and maximum length allowed for a field's value recipe (covered earlier in this chapter) explains how to restrict the number of characters entered in a field.
  • The Changing the location where validation errors are displayed recipe, covered earlier in this chapter, shows how to relocate a field's error icon.
  • Refer to the Deferring field validation until form submission recipe, covered earlier in this chapter, to know how to validate all fields at once upon form submission, instead of using the default automatic field validation.
  • Refer to the previous recipe, Creating validation functions for URLs, email addresses, and other types of data, for an explanation of the validation functions available in Ext JS.
  • The following recipe, Rounding up your validation strategy with server-side validation of form fields, explains how to perform server-side validation.

Rounding up your validation strategy with server-side validation of form fields

Server-side validation should be an important component of your validation strategy for any application. This recipe explains how to send validation codes and messages to a login form from the server-side code.

The login form built in this recipe performs client-side validation. Upon submission, the server returns an error code and a message, as shown in the next screenshot:

How to do it...

  1. Initialize the QuickTips singleton so that we can have error messages as tool tips:
    Ext.QuickTips.init();
  2. Define the login form:
    
    	var loginForm = { xtype: 'form',
    		id: 'login-form',
    		bodyStyle: 'padding:15px;background:transparent',
    		border: false,
    		url:'login.php',
    		items: [
    			{ xtype: 'box', autoEl: { tag: 'div',
    					html: '<div class="app-msg">
    					<img src="img/magic-wand.png" class="app-img" />
    					Log in to The Magic Forum</div>'
    				}
    			},
    			{ xtype: 'textfield', id: 'login-user',
    				fieldLabel: 'Username',allowBlank: false,
    				minLength: 3, maxLength: 32
    			},
    			{ xtype: 'textfield', id: 'login-pwd',
    				fieldLabel: 'Password', inputType: 'password',
    				allowBlank: false, minLength: 6, maxLength: 32,
    				minLengthText: 'Password must be at least 6 characters
    				long.'
    			}
    		],
    		buttons: [{
    			text: 'Login',
    			handler: function() {
    				Ext.getCmp('login-form').getForm().
    					submit({waitMsg: 'Please wait...' });
    				}
    			},
    			{
    				text: 'Cancel',
    				handler: function() {
    				win.hide();
    			}
    		}]
    	}
    
  3. Create the window that will host the login form:
    
    	Ext.onReady(function() {
    		win = new Ext.Window({
    			layout: 'form',
    			width: 340,
    			autoHeight: true,
    			closeAction: 'hide',
    			items: [loginForm]
    		});
    		win.show();
    
  4. Create the login.php server page:
    
    	<?php
    		echo "{success:false,errors:[{id:'login-pwd',msg:'Sorry, you have
    		to type the magic word!'}]}";
    	?>
    

How it works...

The first step is to initialize the QuickTips singleton, so that we can show the form's validation errors as tool tips.

Next, the form and the window that serves as a host for the form are built. The form is set up to perform client-side validation as explained in the previous recipes in this chapter.

The interesting part of this recipe is the server page that handles the form submission. The PHP code here illustrates which elements are necessary to notify the form that one or more fields are invalid:


	echo "{success:false,errors:[{id:'login-pwd',msg:'Sorry, you have to
		type the magic word!'}]}";

The server page returns the JSON representation of an object with two properties—success and errors. The success property is present both when there are problems with the validation (success = false), or when the submission was successful (success = true). Errors is an array containing one object for each invalid field. This object in turn contains the ID of the invalid field and the error message:


	{id:'login-pwd',msg:'Sorry, you have to type the magic word!'}

There's more...

A comprehensive validation strategy is always recommended. Even though the client-side validation facilities in Ext JS are very powerful, do not skip server-side validation. Use it to add robustness and resiliency to your applications.

See also...

  • The Specifying required fields in a form recipe, covered earlier in this chapter, explains how to make some form fields required.
  • Refer to the Deferring field validation until form submission recipe, covered earlier in this chapter, to know how to validate all fields at once upon form submission, instead of using the default automatic field validation.

Loading form data from the server

An important part of working with forms is loading the data that a form will display. Here's how to create a sample contact form and populate it with data sent from the server.

How to do it...

  1. Declare the name and company panel:
    
    	var nameAndCompany = { columnWidth: .5,
    		layout: 'form',
    		items: [
    			{ xtype: 'textfield',
    				fieldLabel: 'First Name',
    				name: 'firstName',
    				anchor: '95%'
    			},
    			{
    				xtype: 'textfield',
    				fieldLabel: 'Last Name',
    				name: 'lastName',
    				anchor: '95%'
    			},
    			{
    				xtype: 'textfield',
    				fieldLabel: 'Company',
    				name: 'company',
    				anchor: '95%'
    			},
    			{
    				xtype: 'textfield',
    				fieldLabel: 'Title',
    				name: 'title',
    				anchor: '95%'
    			}
    		]
    	}
    
  2. Declare the picture box panel:
    
    	var picBox = {
    		columnWidth: .5,
    		bodyStyle: 'padding:0px 0px 0px 40px',
    		items: [
    			{ xtype: 'box',
    				autoEl: { tag: 'div', style: 'padding-bottom:20px',
    					html: '<img id="pic"
    					src="' + Ext.BLANK_IMAGE_URL + '"
    					class="img-contact" />'
    				}
    			}, { xtype: 'button', text: 'Change Picture' }
    		]
    	}
    
  3. Define the Internet panel:
    
    	var internet = { columnWidth: .5,
    		layout: 'form',
    		items: [
    			{ xtype: 'fieldset',
    				title: 'Internet',
    				autoHeight: true,
    				defaultType: 'textfield',
    				items: [{
    					fieldLabel: 'Email',
    					name: 'email',
    					vtype: 'email',
    					anchor: '95%'
    				},
    				{ fieldLabel: 'Web page',
    					name: 'webPage',
    					vtype: 'url',
    					anchor: '95%'
    				},
    				{ fieldLabel: 'IM',
    					name: 'imAddress',
    					anchor: '95%'
    				}]
    			}]
    		}
    
  4. Declare the phone panel:
    
    	var phones = { columnWidth: .5,
    		layout: 'form',
    		items: [{ xtype: 'fieldset',
    			title: 'Phone Numbers',
    			autoHeight: true,
    			defaultType: 'textfield',
    			items: [{
    				fieldLabel: 'Home',
    				name: 'homePhone',
    				anchor: '95%'
    			},
    			{
    				fieldLabel: 'Business',
    				name: 'busPhone',
    				anchor: '95%'
    			},
    			{
    				fieldLabel: 'Mobile',
    				name: 'mobPhone',
    				anchor: '95%'
    			},
    			{
    				fieldLabel: 'Fax',
    				name: 'fax',
    				anchor: '95%'
    			}]
    		}]
    	}
    
  5. Define the business address panel:
    
    	var busAddress = { columnWidth: .5,
    		layout: 'form',
    		labelAlign: 'top',
    		defaultType: 'textarea',
    		items: [{
    			fieldLabel: 'Business',
    			labelSeparator:'',
    			name: 'bAddress',
    			anchor: '95%'
    		},
    		{
    			xtype: 'radio',
    			boxLabel: 'Mailing Address',
    			hideLabel: true,
    			name: 'mailingAddress',
    			value:'bAddress',
    			id:'mailToBAddress'
    		}]
    	}
    
  6. Define the home address panel:
    
    	var homeAddress = { columnWidth: .5,
    		layout: 'form',
    		labelAlign: 'top',
    		defaultType: 'textarea',
    		items: [{
    			fieldLabel: 'Home',
    			labelSeparator:'',
    			name: 'hAddress',
    			anchor: '95%'
    		},
    		{
    			xtype: 'radio',
    			boxLabel: 'Mailing Address',
    			hideLabel: true,
    			name: 'mailingAddress',
    			value:'hAddress',
    			id:'mailToHAddress'
    		}]
    	}
    
  7. Create the contact form:
    
    	var contactForm = new Ext.FormPanel({
    		frame: true,
    		title: 'TODO: Load title dynamically',
    		bodyStyle: 'padding:5px',
    		width: 650,
    		items: [{
    			bodyStyle: {
    				margin: '0px 0px 15px 0px'
    			},
    			items: [{
    				layout: 'column',
    				items: [nameAndCompany, picBox]
    			}]
    		},
    		{
    			items: [{
    				layout: 'column',
    				items: [phones, internet]
    			}]
    		},
    		{
    			xtype: 'fieldset',
    			title: 'Addresses',
    			autoHeight: true,
    			hideBorders: true,
    			layout: 'column',
    			items: [busAddress, homeAddress]
    		}],
    		buttons: [{
    			text: 'Save'
    		},
    		{
    			text: 'Cancel'
    		}]
    	});
    
  8. Handle the form's actioncomplete event:
    
    	contactForm.on({
    		actioncomplete: function(form, action){
    			if(action.type == 'load'){
    				var contact = action.result.data;
    				Ext.getCmp(contact.mailingAddress).setValue(true);
    				contactForm.setTitle(contact.firstName + ' ' +
    					contact.lastName);
    				Ext.getDom('pic').src = contact.pic;
    			}
    		}
    	});
    
  9. Render the form:
    
    	contactForm.render(document.body);
    
  10. Finally, load the form:
    
    	contactForm.getForm().load({ url: 'contact.php',
    	params:{id:'contact1'},waitMsg: 'Loading'});
    

How it works...

The contact form's building sequence consists of defining each of the contained panels, and then defining a form panel that will serve as a host. The following screenshot shows the resulting form, with the placement of each of the panels pinpointed:

Moving on to how the form is populated, the JSON-encoded response to a request to provide form data has a structure similar to this:


	{success:true,
		data:{id:'1',firstName:'Jorge',lastName:'Ramon',
		company:'MiamiCoder',title:'Mr',pic:'img/jorger.jpg',
		email:'ramonj@miamicoder.net',webPage:'http://www.miamicoder.com',
		imAddress:'',homePhone:'',busPhone:'555 555-5555',
		mobPhone:'',fax:'',
		bAddress:'123 Acme Rd \#001\\nMiami, FL 33133',
		hAddress:'',mailingAddress:'mailToBAddress'}}

The success property indicates whether the request has succeeded or not. If the request succeeds, success is accompanied by a data property, which contains the contact's information. Although some fields are automatically populated after a call to load(), the form's title, the contact's picture, and the mailing address radio button require further processing. This can be done in the handler for the actioncomplete event:


	contactForm.on({
		actioncomplete: function(form, action){
			if(action.type == 'load'){
			}
		}
	});

As already mentioned, the contact's information arrives in the data property of the action's result:


	var contact = action.result.data;

The default mailing address comes in the contact's mailingAddress property. Hence, the radio button for the default mailing address is set as shown in the following line of code:


	Ext.getCmp(contact.mailingAddress).setValue(true);

The source for the contact's photo is the value of contact.pic:


	Ext.getDom('pic').src = contact.pic;

And finally, the title of the form:


	contactForm.setTitle(contact.firstName + ' ' + contact.lastName);

There's more...

Although this recipe's focus is on loading form data, you should also pay attention to the layout techniques used—multiple rows, multiple columns, fieldsets—that allow you to achieve rich and fl exible user interfaces for your forms.

See also...

  • The next recipe, Serving the XML data to a form, explains how to use a form to load the XML data sent from the server

Serving the XML data to a form

XML is an information interchange format that can be used to provide the data that a form will display. This recipe shows how to create a contact form that feeds off the XML data that is sent from the server. This is how the contact form looks like after receiving the XML data:

How to do it...

  1. Build the name, company, picture box, internet, phone, business address, and home address panels as explained in the first six steps of the recipe Loading form data from the server.
  2. Create the contact form and assign it an XmlReader as its data reader:
    
    	var contactForm = new Ext.FormPanel({
    		frame: true,
    		title: 'TODO: Load title dynamically',
    		bodyStyle: 'padding:5px',
    		width: 650,
    		url: 'contact-xml.php',
    		reader: new Ext.data.XmlReader({
    			record: 'contact',
    			success: '@success'
    			},
    			['id', 'firstName', 'lastName', 'company', 'title',
    			'pic','email', 'webPage', 'imAddress', 'homePhone',
    			'busPhone','mobPhone', 'fax', 'bAddress', 'hAddress',
    			'mailingAddress']
    		),
    		items: [{
    			bodyStyle: {
    				margin: '0px 0px 15px 0px'
    			},
    				items: [{
    					layout: 'column',
    					items: [nameAndCompany, picBox]
    				}]
    			}, {
    			items: [{
    				layout: 'column',
    				items: [phones, internet]
    			}]
    		}, {
    			xtype: 'fieldset',
    			title: 'Addresses',
    			autoHeight: true,
    			hideBorders: true,
    			layout: 'column',
    			items: [busAddress, homeAddress]
    		}],
    		buttons: [{
    			text: 'Save'
    		},
    		{
    			text: 'Cancel'
    		}]
    	});
    
  3. Handle the form's actioncomplete event:
    
    	contactForm.on({
    		actioncomplete: function(form, action){
    			if(action.type == 'load'){
    				var contact = action.result.data;
    				Ext.getCmp(contact.mailingAddress).setValue(true);
    				contactForm.setTitle(contact.firstName + ' ' +
    				contact.lastName);
    				Ext.getDom('pic').src = contact.pic;
    			}
    		}
    	});
    
  4. Render the form:
    contactForm.render(document.body);
  5. Finally, load the form:
    
    	contactForm.getForm().load({ method:'get',
    	params: { id: 'contact1' }, waitMsg: 'Loading' });
    

How it works...

The contact form's building sequence is similar to the one described in the recipe Loading form data from the server. This is the resulting form, with the placement of each of the panels pinpointed:

Although a real-world application will most likely do some processing to generate the XML information, for this example the server page will generate sample data as simply as this:


	<?php
		header("content-type: text/xml");
		echo "<?xml version=\"1.0\" encoding=\"utf-8\" ?>";
		echo "<message success=\"true\">";
		echo " <contact>";
		echo " <id>contact1</id>";
		echo " <firstName>Jorge</firstName>";
		echo " <lastName>Ramon</lastName>";
		echo " <company>MiamiCoder</company>";
		echo " <title>Mr</title>";
		echo " <pic>img/jorger.jpg</pic>";
		echo " <email>ramonj@miamicoder.com</email>";
		echo " <webPage>http://www.miamicoder.com</webPage>";
		echo " <imAddress></imAddress>";
		echo " <homePhone></homePhone>";
		echo " <busPhone>555 555-5555</busPhone>";
		echo " <mobPhone></mobPhone>";
		echo " <fax></fax>";
		echo " <bAddress>123 Acme Rd #001 Miami, FL 33133</bAddress>";
		echo " <hAddress></hAddress>";
		echo " <mailingAddress>mailToBAddress</mailingAddress>";
		echo " </contact>";
		echo "</message>";
	?>

The data consists of a message entity with a success attribute, which signals whether the request succeeded, and a contact entity for the contact information.

This data can be read by the form, thanks to the use of an XmlReader instance:


	reader: new Ext.data.XmlReader({
		record: 'contact',
		success: '@success'
	}, ['id', 'firstName', 'lastName', 'company', 'title', 'pic','email',
		'webPage', 'imAddress', 'homePhone', 'busPhone','mobPhone',
		'fax', 'bAddress', 'hAddress', 'mailingAddress']
	)

The handler for the actioncomplete event is used to process the results of the load or submit actions:


	contactForm.on({
		actioncomplete: function(form, action){
			if(action.type == 'load'){
				.
				.
				.
			}
			if(action.type == 'submit'){
				.
				.
				.
			}
		}
	});

When the handler processes the load action, it sets the default mailing address, the form's title, and the contact's picture.

As mentioned, the contact's information arrives in the data property of the action's result:


	var contact = action.result.data;

The default mailing address comes in the contact's mailingAddress property, and the radio button for the default mailing address is set as shown in the following code:


	Ext.getCmp(contact.mailingAddress).setValue(true);

The source for the contact's photo is the value of contact.pic:


	Ext.getDom('pic').src = contact.pic;

And finally, the title of the form:


	contactForm.setTitle(contact.firstName + ' ' + contact.lastName);

There's more...

XML is powerful and popular, and it is likely that you will encounter situations where it's appropriate to create forms that are able to read XML data. As you saw, this can be accomplished by using an XmlReader through the reader configuration option, instead of using the form's built-in support for processing JSON.

See also...

  • The previous recipe, Loading form data from the server, explains how to populate a form with data sent from the server

Using forms for file uploads

The Change Picture form is a great example of how to implement file uploads in your Ext JS application. This form is also a complement to the contact form examined in the Loading form data from the server and Serving XML data to a form recipes.

Let's see how it's done.

How to do it…

  1. Create the component that will display the contact's picture:
    
    	var picBox = {
    		columnWidth: '100 px',
    		bodyStyle: 'padding:10px',
    		items: [
    			{ xtype: 'box',
    				autoEl: { tag: 'div',
    					html: '<img id="pic" src="' + Ext.BLANK_IMAGE_URL + '"
    					class="img-contact" />'
    				}
    			}
    		]
    	}
    
  2. Create a panel with text boxes that show the file names for the current picture and the picture to be uploaded:
    
    	var picFiles = {
    		columnWidth: .65,
    		layout: 'form',
    		labelAlign:'top',
    		items: [{
    			xtype: 'textfield',
    			fieldLabel: 'Current',
    			labelSeparator: '',
    			name: 'currPic',
    			id:'currPic',
    			readOnly: true,
    			disabled:true,
    			anchor:'100%'
    		},
    		{
    			xtype: 'textfield',
    			fieldLabel: 'New (JPG or PNG only)',
    			labelSeparator: '',
    			name: 'newPic',
    			id:'newPic',
    			style:'width: 300px',
    			inputType: 'file',
    			allowBlank: false
    		}]
    	}
    
  3. Define a custom validation function that will be used to validate that the file to upload is either a JPG or PNG file:
    
    	function validateFileExtension(fileName) {
    		var exp = /^.*\.(jpg|JPG|png|PNG)$/;
    		return exp.test(fileName);
    	}
    
  4. Now, create the form and define a handler for the Upload Picture button:
    
    	var pictUploadForm = new Ext.FormPanel({
    		frame: true,
    		title: 'Change Picture',
    		bodyStyle: 'padding:5px',
    		width: 420,
    		layout: 'column',
    		url: 'contact-picture.aspx',
    		method: 'POST',
    		fileUpload: true,
    		items: [picBox, picFiles],
    		buttons: [{
    			text: 'Upload Picture',
    			handler: function() {
    				var theForm = pictUploadForm.getForm();
    				if (!theForm.isValid()) {
    					Ext.MessageBox.alert('Change Picture',
    					'Please select a picture');
    					return;
    				}
    				if (!validateFileExtension(Ext.getDom('newPic').value)) {
    					Ext.MessageBox.alert('Change Picture',
    					'Only JPG or PNG, please.');
    				return;
    				}
    				theForm.submit({
    					params: { act: 'setPicture', id: 'contact1' },
    					waitMsg: 'Uploading picture'
    				})
    			}
    		},
    		{
    			text: 'Cancel'
    		}]
    	});
    
  5. Define a handler for the actioncomplete event:
    
    	pictUploadForm.on({
    		actioncomplete: function(form, action) {
    			if (action.type == 'load') {
    				var pic = action.result.data;
    				Ext.getDom('pic').src = pic.file;
    				Ext.getCmp('currPic').setValue(pic.file);
    			}
    			if (action.type == 'submit') {
    				var pic = action.result.data;
    				Ext.getDom('pic').src = pic.file;
    				Ext.getCmp('currPic').setValue(pic.file);
    				Ext.getDom('newPic').value = '';
    			}
    		}
    	});
    
  6. Render the form and load the existing picture for the contact:
    
    	pictUploadForm.render(document.body);
    	pictUploadForm.getForm().load({ params: { act: 'getPicture',
    		id: 'contact1' }, waitMsg: 'Loading' });
    

How it works…

The component that will display the contact's picture is created first, followed by the text boxes that show the filenames for the current and new picture. To provide the ability to browse for the appropriate file, the new picture text box uses the inputType = 'file' config option, which creates the equivalent HTML element consisting of a text box and a Browse button.

A first layer of validation is provided by the validateFileExtensions function. This function uses a regular expression test in order to make sure the selected file has a PNG or JPG extension:


	function validateFileExtension(fileName) {
		var exp = /^.*\.(jpg|JPG|png|PNG|txt|TXT)$/;
		return (exp.test(fileName));
	}

I recommend that you complement this function with server-side validation. This will guarantee that the uploaded file is actually a picture.

Carrying out the validation, as well as the submission, is the responsibility of the click handler for the Upload picture button. Notice the use of a request parameter (act: 'setPicture') to signal the server page that a new picture is being uploaded:


	buttons: [{
		text: 'Upload Picture',
		handler: function() {
			var theForm = pictUploadForm.getForm();
			if (!theForm.isValid()) {
				Ext.MessageBox.alert('Change Picture',
				'Please select a picture');
				return;
			}
			if (!validateFileExtension(Ext.getDom('newPic').value)) {
				Ext.MessageBox.alert('Change Picture',
				'Only JPG or PNG, please.');
				return;
			}
			theForm.submit({
				params: { act: 'setPicture', id: 'contact1' },
				waitMsg: 'Uploading picture'
			})
		}
	}

The actioncomplete handler is in charge of displaying the contact's picture. Upon a form load, the contact's current picture is displayed. After the form is submitted, the just-uploaded picture is shown:


	if (action.type == 'load') {
		var pic = action.result.data;
		Ext.getDom('pic').src = pic.file;
		Ext.getCmp('currPic').setValue(pic.file);
	}
	if (action.type == 'submit') {
		var pic = action.result.data;
		Ext.getDom('pic').src = pic.file;
		Ext.getCmp('currPic').setValue(pic.file);
		Ext.getDom('newPic').value = '';
	}

Notice that after a successful upload, the JSON-encoded response will have the following structure:


	"{success:true,data:{contactId:'contact id',file:'[picture path]'}}

There's more...

This approach is a good foundation for implementing file uploading code in Ext JS. You can easily modify it to accomplish multiple file uploads as well. More complex implementations are also possible through the use of plugins or extensions.

See also…

  • File upload panel at http://filetree.extjs.eu

Building friendlier forms using text hints

Great usability is often attained with simple UI modifications. This recipe explains how you can make your forms easier to use by adding text hints to their contained fields. The next screenshot shows a form that contains text hints:

How to do it...

Create a form and use the emptyText configuration option to add text hints to the fields:


	Ext.onReady(function() {
		var commentForm = new Ext.FormPanel({
			frame: true,
			title: 'Send your comments',
			bodyStyle: 'padding:5px',
			width: 550,
			layout: 'form',
			items: [{
				xtype: 'textfield',
				fieldLabel: 'Name',
				name: 'name',
				anchor: '98%',
				allowBlank:false,
				emptyText:'Your name here'
			},
			{
				xtype: 'textfield',
				fieldLabel: 'Email',
				name: 'email',
				anchor: '98%',
				vtype:'email'
			},
			{
				xtype: 'textarea',
				fieldLabel: 'Comments',
				name: 'comments',
				anchor: '98%',
				height:200,
				allowBlank: false,
				emptyText: 'Enter your comments'
			}],
			buttons: [{
				text: 'Send'
			},
			{
				text: 'Cancel'
			}]
		});
	commentForm.render(document.body);

There's more...

When using emptyText to specify the default text to place into an empty field, be aware that this value will be submitted to the server if the field is enabled and configured with a name.

See also...

  • The Specifying required fields in a form recipe, covered earlier in this chapter, explains how to make some form fields required
  • The recipe titled Setting the minimum and maximum length allowed for a field's value, covered earlier in this chapter, explains how to restrict the number of characters entered in a field
  • The Changing the location where validation errors are displayed recipe, covered earlier in this chapter, shows how to relocate a field's error icon

Source : www.javabeat.net