Ext JS Forms — Input Fields, Validation, and Data Binding
In this tutorial, you will learn about Ext JS Forms. We cover key concepts, practical examples, and best practices to help you master this topic.
Ext JS Forms provide a structured way to collect and validate user input with field types, built-in validators, layout management, and automatic binding to data models.
What You'll Learn
- Configuring form fields and layouts
- Built-in and custom field validation
- Binding forms to Models and Stores
- Handling form submission and reset
- Dynamic form manipulation
Why It Matters
Enterprise applications heavily depend on forms for data entry — user registration, order creation, settings panels, and search filters. Ext JS Forms handle client-side validation, layout consistency, and server communication without manual wiring.
Real-World Use
A customer support ticket form with text fields for subject and description, a combo box for priority, radio buttons for category, checkboxes for tags, and date picker for deadline — all validated before submission.
Form Architecture
flowchart TD
A[Form Panel] --> B[Field Container]
A --> C[Fieldset]
A --> D[Buttons]
B --> E[TextField]
B --> F[ComboBox]
B --> G[Checkbox]
B --> H[NumberField]
B --> I[DateField]
A --> J[Model Binding]
J --> K[Load Record]
J --> L[Update Record]
style A fill:#e6f3ff,stroke:#4a90d9,stroke-width:2px
Basic Form Configuration
Ext.create('Ext.form.Panel', {
title: 'User Registration',
width: 450,
bodyPadding: 15,
url: '/api/users',
defaults: {
anchor: '100%',
labelWidth: 120
},
items: [{
xtype: 'textfield',
fieldLabel: 'Full Name',
name: 'name',
allowBlank: false
}, {
xtype: 'textfield',
fieldLabel: 'Email',
name: 'email',
vtype: 'email',
allowBlank: false
}, {
xtype: 'numberfield',
fieldLabel: 'Age',
name: 'age',
minValue: 1,
maxValue: 150
}, {
xtype: 'combobox',
fieldLabel: 'Country',
name: 'country',
store: ['USA', 'Canada', 'UK', 'Australia'],
queryMode: 'local',
typeAhead: true
}, {
xtype: 'checkboxgroup',
fieldLabel: 'Interests',
columns: 2,
items: [
{ boxLabel: 'Technology', name: 'interest-tech', inputValue: 'tech' },
{ boxLabel: 'Science', name: 'interest-science', inputValue: 'science' },
{ boxLabel: 'Sports', name: 'interest-sports', inputValue: 'sports' },
{ boxLabel: 'Music', name: 'interest-music', inputValue: 'music' }
]
}],
buttons: [{
text: 'Submit',
handler: function() {
var form = this.up('form').getForm();
if (form.isValid()) {
form.submit({
success: function() { Ext.Msg.alert('Success', 'User created'); },
failure: function() { Ext.Msg.alert('Error', 'Submission failed'); }
});
}
}
}, {
text: 'Reset',
handler: function() {
this.up('form').getForm().reset();
}
}],
renderTo: Ext.getBody()
});
Expected output: A form panel with text fields, combo box, number field, and checkbox group. Submit validates all fields before sending to the server. Reset clears all values.
Field Validation
Ext.create('Ext.form.Panel', {
items: [{
xtype: 'textfield',
fieldLabel: 'Username',
name: 'username',
allowBlank: false,
minLength: 3,
maxLength: 20,
regex: /^[a-zA-Z0-9_]+$/,
regexText: 'Only letters, numbers, and underscores allowed'
}, {
xtype: 'textfield',
fieldLabel: 'Email',
name: 'email',
vtype: 'email' // Built-in email validation
}, {
xtype: 'textfield',
fieldLabel: 'Confirm Email',
name: 'confirmEmail',
vtype: 'email',
validator: function(value) {
var email = this.up('form').down('[name=email]').getValue();
return value === email ? true : 'Emails must match';
}
}, {
xtype: 'textfield',
fieldLabel: 'Password',
inputType: 'password',
name: 'password',
minLength: 8,
// Custom vtype
vtype: 'passwordStrength'
}]
});
// Register custom vtype
Ext.apply(Ext.form.field.VTypes, {
passwordStrength: function(val) {
return /(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/.test(val);
},
passwordStrengthText: 'Must contain uppercase, lowercase, and number'
});
Expected output: Fields validate on blur and on form submit. The username enforces pattern, email uses built-in vtype, confirm email runs a custom validator, and password uses a custom vtype for strength.
Form Binding to a Model
Ext.define('MyApp.model.Product', {
extend: 'Ext.data.Model',
fields: [
{ name: 'id', type: 'int' },
{ name: 'name', type: 'string' },
{ name: 'price', type: 'float' },
{ name: 'category', type: 'string' },
{ name: 'active', type: 'bool' }
]
});
// Create the form with loadRecord / updateRecord
var formPanel = Ext.create('Ext.form.Panel', {
title: 'Edit Product',
bodyPadding: 15,
items: [{
xtype: 'textfield',
fieldLabel: 'Product Name',
name: 'name'
}, {
xtype: 'numberfield',
fieldLabel: 'Price',
name: 'price',
step: 0.01
}, {
xtype: 'combobox',
fieldLabel: 'Category',
name: 'category',
store: ['Electronics', 'Clothing', 'Food']
}, {
xtype: 'checkbox',
fieldLabel: 'Active',
name: 'active'
}],
buttons: [{
text: 'Load',
handler: function() {
var record = Ext.create('MyApp.model.Product', {
id: 1, name: 'Laptop', price: 999.99, category: 'Electronics', active: true
});
this.up('form').getForm().loadRecord(record);
}
}, {
text: 'Save',
handler: function() {
var form = this.up('form').getForm();
var record = form.getRecord() || Ext.create('MyApp.model.Product');
form.updateRecord(record);
console.log(record.getData());
}
}]
});
Expected output: Clicking Load populates the form fields from the model record. Clicking Save writes form values back to the model and logs the data object.
Dynamic Form Manipulation
var form = Ext.create('Ext.form.Panel', {
title: 'Dynamic Form',
bodyPadding: 15,
items: [{
xtype: 'combobox',
fieldLabel: 'Type',
name: 'type',
store: ['Personal', 'Business'],
listeners: {
change: function(combo, newValue) {
var form = combo.up('form');
var field = form.down('[name=companyName]');
if (newValue === 'Business') {
field.show();
field.allowBlank = false;
} else {
field.hide();
field.allowBlank = true;
}
form.updateLayout();
}
}
}, {
xtype: 'textfield',
fieldLabel: 'Company Name',
name: 'companyName',
hidden: true
}]
});
Expected output: Selecting "Business" in the Type combo reveals the Company Name field and makes it required. Selecting "Personal" hides it again.
Common Mistakes
Not calling form.isValid() before submit - Always validate the form first. Submit will still send, but the server will reject invalid data.
Confusing form.submit() and form.updateRecord() - submit() sends data via Ajax to the server. updateRecord() writes form values to an Ext JS Model without a server request.
Forgetting to set name property - Field values are keyed by name when getting values, submitting, or binding to a model. Without name, the value is not accessible.
Not handling form destruction - When removing a form panel, call form.destroy() to clean up field listeners and prevent memory leaks in long-lived applications.
Overriding field values without validation - Calling setValue() programmatically bypasses the field's validation. Use setValue() with the raw parameter set to true or manually call field.validate() after.
Practice Questions
- How do you check if all fields in a form are valid?
- What is the difference between vtype and a custom validator function?
- How do you load form data from an existing Model record?
- What happens when you call form.submit() without setting the url property?
- How do you dynamically show and hide fields based on user input?
Challenge: Build a product registration form with conditional fields (different fields for physical vs digital products), cross-field validation (end date must be after start date), combo boxes with remote Stores, and a grid showing already-submitted products below the form.
FAQ
Mini Project
Build a customer registration form with: text fields for name, email, phone, and address; combo box for country with remote loading; date field for date of birth; radio group for gender; checkbox group for notification preferences; a grid showing registered customers below; and full create/update/delete with server sync via a shared Store.
What's Next
Forms collect data. Learn how Ext JS Tree Panels display hierarchical data with expandable nodes, drag-and-drop, and Lazy Loading.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro