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...
- Initialize the global QuickTips instance:
Ext.QuickTips.init();
- 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();
}
}]
}
- 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...
- The first thing is to initialize the QuickTips singleton:
Ext.QuickTips.init();
- 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();
}
}]
}
- 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...
- Initialize the QuickTips singleton:
Ext.QuickTips.init();
- 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();
}
}]
}
- 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...
- Initialize the QuickTips singleton:
Ext.QuickTips.init();
- 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();
}
}]
}
- 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...
- Initialize the QuickTips singleton:
Ext.QuickTips.init();
- 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…
- Initialize the QuickTips singleton:
Ext.QuickTips.init();
- 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!'
});
- 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();
}
}]
}
- 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...
- Initialize the QuickTips singleton so that we can have error messages as tool tips:
Ext.QuickTips.init();
- 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();
}
}]
}
- 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();
- 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...
- 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%'
}
]
}
- 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' }
]
}
- 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%'
}]
}]
}
- 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%'
}]
}]
}
- 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'
}]
}
- 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'
}]
}
- 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'
}]
});
- 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;
}
}
});
- Render the form:
contactForm.render(document.body);
- 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...
- 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.
- 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'
}]
});
- 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;
}
}
});
- Render the form:
contactForm.render(document.body);
- 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…
- 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" />'
}
}
]
}
- 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
}]
}
- 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);
}
- 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'
}]
});
- 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 = '';
}
}
});
- 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
|