Ext JS 4: Working with Tree and Form Components

Exclusive offer: get 50% off this eBook here
Ext JS 4 First Look

Ext JS 4 First Look — Save 50%

A practical guide including examples of the new features in Ext JS 4 and tips to migrate from Ext JS 3 with this book and ebook.

£16.99    £8.50
by Loiane Groner | January 2012 | Java Open Source

In this article by Loiane Groner, author of Ext JS 4 First Look we will cover some new features and enhancements in the following Components.

  • Tree panel
  • Form panel

These Components enable faster performance and more developer flexibility.

(For more resources on JavaScript, see here.)

Tree panel

The tree component is much more simplified in Ext JS 4. Like grid, it is also a subclass of Ext.panel.Table. This means we can add most functionality of the grid in the tree as well.

Let's start declaring a simple tree in Ext JS 3:

new Ext.tree.TreePanel({ renderTo: 'tree-example', title: 'Simple Tree', width: 200, rootVisible: false, root: new Ext.tree.AsyncTreeNode({ expanded: true, children: [ { text: "Menu Option 1", leaf: true }, { text: "Menu Option 2", expanded: true, children: [ { text: "Sub Menu Option 2.1", leaf: true }, { text: "Sub Menu Option 2.2", leaf: true} ] }, { text: "Menu Option 3", leaf: true } ] }) });

Now, let's see how to declare the same tree in Ext JS:

Ext.create('Ext.tree.Panel', { title: 'Simple Tree', width: 200, store: Ext.create('Ext.data.TreeStore', { root: { expanded: true, children: [ { text: "Menu Option 1", leaf: true }, { text: "Menu Option 2", expanded: true, children: [ { text: "Sub Menu Option 2.1", leaf: true }, { text: "Sub Menu Option 2.2", leaf: true} ] }, { text: "Menu Option 3", leaf: true } ] } }), rootVisible: false, renderTo: 'tree-example' });

In Ext JS 4, we also have the title, width, and div properties, where the tree is going to be rendered, and a config store. The store config is a new element for the tree.

If we output both of the codes, we will have the same output, which is the following tree:

If we take a look at the data package, we will see three files related to tree: NodeInterface, Tree, and TreeStore.

NodeInterface applies a set of methods to the prototype of a record to decorate it with a Node API. The Tree class is used as a container of a series of nodes and TreeStore is a store implementation used by a Tree. The good thing about having TreeStore is that we can use its features, such as proxy and reader, as we do for any other Store in Ext JS 4.

Drag-and-drop and sorting

The drag-and-drop feature is very useful for rearranging the order of the nodes in the Tree class.

Adding the drag-and-drop feature is very simple. We need to add the following code into the tree declaration:

Ext.create('Ext.tree.Panel', { store: store, viewConfig: { plugins: { ptype: 'treeviewdragdrop' } }, //other properties });

And how do we handle drag-and-drop in store?

We do it in the same way as we handled the edition plugin on the Grid, using a Writer:

var store = Ext.create('Ext.data.TreeStore', { proxy: { type: 'ajax', api: { read : '../data/drag-drop.json', create : 'create.php' } }, writer: { type: 'json', writeAllFields: true, encode: false }, autoSync:true });

In the earlier versions of Ext JS 4, the autoSync config option does work. Another way of synchronizing the Store with the server is adding a listener to the Store instead of the autoSync config option, as follows:

listeners: { move: function( node, oldParent, newParent, index, options ) { this.sync(); } }

And, to add the sorting feature to the Tree class, we simply need to configure the sorters property in the TreeStore, as follows:

Ext.create('Ext.data.TreeStore', { folderSort: true, sorters: [{ property: 'text', direction: 'ASC' }] });

Check tree

To implement a check tree, we simply need to make a few changes in the data that we are going to apply to the Tree. We need to add a property called checked to each node, with a true or false value; true indicates the node is checked, and false, otherwise.

For this example, we will use the following json code:

[{ "text": "Cartesian", "cls": "folder", "expanded": true, "children": [{ "text": "Bar", "leaf": true, "checked": true },{ "text": "Column", "leaf": true, "checked": true },{ "text": "Line", "leaf": true, "checked": false }] },{ "text": "Gauge", "leaf": true, "checked": false },{ "text": "Pie", "leaf": true, "checked": true }]

And as we can see, the code is the same as that for a simple tree:

var store = Ext.create('Ext.data.TreeStore', { proxy: { type: 'ajax', url: 'data/check-nodes.json' }, sorters: [{ property: 'leaf', direction: 'ASC' }, { property: 'text', direction: 'ASC' }] }); Ext.create('Ext.tree.Panel', { store: store, rootVisible: false, useArrows: true, frame: true, title: 'Charts I have studied', renderTo: 'tree-example', width: 200, height: 250 });

The preceding code will output the following tree:

Tree grid

In Ext JS 3, the client JavaScript Component, Tree Grid, was an extension part of the ux package. In Ext JS 4, this Component is part of the native API but it is no longer an extension. To implement a Tree Grid, we are going to use the Tree Component as well; the only difference is that we are going to declare some columns inside the tree. This is the good part of Tree being a subclass of Ext.panel.Table, the same super class for Grid as well.

First, we will declare a Model and a Store, to represent the data we are going to display in the Tree Grid. We will then load the Tree Grid:

Ext.define('Book', { extend: 'Ext.data.Model', fields: [ {name: 'book', type: 'string'}, {name: 'pages', type: 'string'} ] }); var store = Ext.create('Ext.data.TreeStore', { model: 'Book', proxy: { type: 'ajax', url: 'data/treegrid.json' }, folderSort: true });

So far there is no news. We declared the variable store as any other used in a grid, except that this one is a TreeStore.

The code to implement the Component Tree Grid is declared as follows:

Ext.create('Ext.tree.Panel', { title: 'Books', width: 500, height: 300, renderTo: Ext.getBody(), collapsible: true, useArrows: true, rootVisible: false, store: store, multiSelect: true, singleExpand: true, columns: [{ xtype: 'treecolumn', text: 'Task', flex: 2, sortable: true, dataIndex: 'task' },{ text: 'Assigned To', flex: 1, dataIndex: 'user', sortable: true }] });

The most important line of code is highlighted—the columns declaration. The columns property is an array of Ext.grid.column.Column objects, as we declare in a grid.

The only thing we have to pay attention to is the column type of the first column, that is, treecolumn; this way we know that we have to render the node into the Tree Grid.

We also configured some other properties. collapsible is a Boolean property; if set to true it will allow us to collapse and expand the nodes of the tree. The useArrows is also a Boolean property, which indicates whether the arrow icon will be visible in the tree (expand/collapse icons). The property rootVisible indicates whether we want to display the root of the tree, which is a simple period (.). The property singleExpand indicates whether we want to expand a single node at a time and the multiSelect property indicates whether we want to select more than one node at once.

The preceding code will output the following tree grid:

Ext JS 4 First Look A practical guide including examples of the new features in Ext JS 4 and tips to migrate from Ext JS 3 with this book and ebook.
Published: January 2012
eBook Price: £16.99
Book Price: £27.99
See more
Select your format and quantity:

(For more resources on JavaScript, see here.)

Form

The class FormPanel provides a container for forms. We usually use a form for data management. In Ext JS 4, FormPanel consists of Fields, FieldContainer, FieldSet, Label, and Actions. We will start with an example of form fields, explaining each one of them.

Form fields

Ext JS 4 introduces the Ext.form.field package, where all the form fields belong. We will look into each one of the classes from the previous diagram, with examples. First, we will declare a form with some fields:

Ext.create('Ext.form.Panel', { frame: true, title: 'Form Fields', width: 340, bodyPadding: 5, renderTo: 'form-example', fieldDefaults: { labelAlign: 'left', labelWidth: 90, anchor: '100%' }, items: [{ xtype: 'hiddenfield', //1 name: 'hiddenfield1', value: 'Hidden field value' },{ xtype: 'displayfield', //2 name: 'displayfield1', fieldLabel: 'Display field', value: 'Display field value' },{ xtype: 'textfield', //3 name: 'textfield1', fieldLabel: 'Text field', value: 'Text field value' },{ xtype: 'textfield', //4 name: 'password1', inputType: 'password', fieldLabel: 'Password field' },{ xtype: 'textareafield', //5 name: 'textarea1', fieldLabel: 'TextArea', value: 'Textarea value' },{ xtype: 'filefield', //6 name: 'file1', fieldLabel: 'File upload' },{ xtype: 'timefield', //7 name: 'time1', fieldLabel: 'Time Field', minValue: '8:00 AM', maxValue: '5:00 PM', increment: 30 },{ xtype: 'datefield', //8 name: 'date1', fieldLabel: 'Date Field', value: new Date() },{ xtype: 'combobox', //9 fieldLabel: 'Combobox', displayField: 'name', store: Ext.create('Ext.data.Store', { fields: [ {type: 'string', name: 'name'} ], data: [ {"name":"Alabama"}, {"name":"Alaska"}, {"name":"Arizona"}, {"name":"Arkansas"}, {"name":"California"} ] }), queryMode: 'local', typeAhead: true },{ xtype: 'numberfield', name: 'numberfield1', //10 fieldLabel: 'Number field', value: 20, minValue: 0, maxValue: 50 },{ xtype: 'checkboxfield', //11 name: 'checkbox1', fieldLabel: 'Checkbox', boxLabel: 'box label' },{ xtype: 'radiofield', //12 name: 'radio1', value: 'radiovalue1', fieldLabel: 'Radio buttons', boxLabel: 'radio 1' },{ xtype: 'radiofield', //13 name: 'radio1', value: 'radiovalue2', fieldLabel: '', labelSeparator: '', hideEmptyLabel: false, boxLabel: 'radio 2' },{ xtype: 'multislider', //14 fieldLabel: 'Multi Slider', values: [25, 50, 75], increment: 5, minValue: 0, maxValue: 100 },{ xtype: 'sliderfield', //15 fieldLabel: 'Single Slider', value: 50, increment: 10, minValue: 0, maxValue: 100 }] });

In the preceding code, after we set the width and height of the form, we declared the fieldDefaults property. This property contains the configuration applied to all label instance fields (subclasses of Ext.form.field.Base or Ext.fom. FieldContainer). As all fields in the preceding form are subclasses of Ext.form. field.Base, the default config applies to all fields. In the previous example, we said that the alignment of the label should be at the left of the form; the labelWidth should be 90 pixels and all fields are going to use 100% of the available width (anchor: '100%').

The previous form will have the following output:

Now, we will look into the fields/items declarations:

  • The first field we declared is a hidden field (xtype:'hiddenfield'). This field stores hidden values, which we do not want to show to the user but want to submit to the server. We can use a hidden field to store the ID information; we do not want to display the ID in the form, but we want to send it back to the server, say to perform updates.
  • The second field we declared is a display field (xtype:'displayfield'). This field is useful when we want to display read-only information in the form.
  • The third field we declared is a text field (type:'textfield'). The text field is a simple input field, where the user can enter any information.
  • The fourth field is a text field as well (type 'password'), which means this field is going to mask the input value.
  • The fifth field is a textarea (xtype 'textareafield'). It is a multiline text input field, where the user can enter multiple lines of information. TextArea is a subclass of the Text field.
  • The sixth field is a file upload field (xtype:'filefield'), also known as File Uploader. This field contains a button used to browse for a file at the user's machine end, and the text field will display the path of the file. This field is also a subclass of the Text field.
  • Next, we have some fields from the Trigger class, which also have Text as a super class. The trigger fields contain a trigger button. The Trigger fields are Picker or Spinner. Picker contains a button, which opens a picker popup to select the value, such as the combobox, date picker, and time picker. The Spinner fields contain a spinner with up and down buttons, such as the number field.

  • The seventh field we declared is a time field (xtype: 'timefield'), which is a trigger field. In the time field, we can also configure minValue and maxValue, which are the minimum time and maximum time, set in this example as 8:00 AM and 5:00 PM, respectively. We can also set the increment interval (set to 30 minutes in this example), which means the field is going to display 8 AM, 8:30 AM, 9 AM, and so on.
  • The eighth field is a date field (xtype: 'datefield'). The date field is also a trigger field used to handle dates. In the preceding example, it is set to a default value—the current date.
  • The ninth field is a combo box, which is also a trigger field: (xtype:'combobox' or 'combo'). The combo box needs a Store to load the information that is going to populate it. In this example, we declared the config, store that loads the data from the memory. displayField is the field we are going to display in the combo box.
  • The tenth field is a number field (xtype:'numberfield'). numberfield is a spinner field with up and down buttons, used to increase and decrease the value. In the previous example, the default value is 20, the minimum value is 0, and the maximum value is 50. If we want to remove the spinner button and leave it as a number text field, we have to add the following config:

    hideTrigger: true, keyNavEnabled: false, mouseWheelEnabled: false

  • The eleventh field we declared is a checkbox (xtype: 'checkboxfield' or 'checkbox'). We have also set a value for the label.
  • The next two fields are radio fields (xtype:'radiofield' or 'radio'). The Radio class is a subclass of Checkbox, which is why the config is very similar.
  • The fourteenth field is a multi-slider field (xtype:'multislider'). To configure this field, we can set an array of default, minimum, and maximum values, and also the increment interval.
  • The same applies to the single slider field (xtype:'slider' or 'sliderfield'), but instead of multiple values, we have a single one.

Validation

Having only a form to load and update information is not useful without validating the data the user has input, correct?

Ext JS 4 also provides a validation mechanism. Let's see an example of how to validate some fields:

Ext.create('Ext.form.Panel', { frame: true, title: 'Form Fields Validation', width: 340, bodyPadding: 5, renderTo: 'form-example', fieldDefaults: { labelAlign: 'left', labelWidth: 90, anchor: '100%', msgTarget: 'under' }, items: [{ xtype: 'textfield', name: 'textfield1', fieldLabel: 'Required', allowBlank: false //1 },{ xtype: 'textfield', name: 'textfield2', fieldLabel: 'Min 2', minLength: 2 //2 },{ xtype: 'textfield', name: 'textfield3', fieldLabel: 'Max 5', maxLength: 5 //3 },{ xtype: 'textfield', name: 'textfield7', fieldLabel: 'Regex - Phone', regex: /^\d{3}-\d{3}-\d{4}$/, //4 regexText: 'Must be in the format xxx-xxx-xxxx' },{ xtype: 'textfield', name: 'textfield4', fieldLabel: 'Email', vtype: 'email' //5 },{ xtype: 'textfield', name: 'textfield5', fieldLabel: 'Alpha', vtype: 'alpha' //6 },{ xtype: 'textfield', name: 'textfield6', fieldLabel: 'AlphaNum', vtype: 'alphanum' //7 },{ xtype: 'textfield', name: 'textfield6', fieldLabel: 'Url', vtype: 'url' //8 },{ xtype: 'textfield', name: 'textfield8', fieldLabel: 'Custom: IP Address', vtype: 'IPAddress' //9 }] });

First, if we need to validate the information, we have to display something to the user if the data is not valid. To do so, we can configure msgTarget (message target location). It can take the following values: side, under, or top. In the preceding example, we configure it to be displayed under the field for all fields, but we can also configure each field separately.

The first validation (comment 1) is the allowBlank form field. This is a Boolean property, which, if set to true, will allow the user to leave the field blank. If it is set to false, the user will have to enter a value—it cannot be left blank—otherwise, the form will display an error message.

Then, we have the minLength and maxLength validations (comment 2 and comment 3), through which we can set a value for the minimum number of characters and the maximum number of characters. In the preceding example, the minLength is set to 2 and the maxLength to 5. Therefore, if the user inputs only one character or more than five characters, the form will display an error message.

We can perform another validation by creating a regular expression for the field (comment 4). In the previous example, we set a regex property. The input field value must have the following format: xxx-xxx-xxxx, where x can be any number from zero to nine. We can also configure the error message that the form will display to the user, in case the field does not match the regular expression.

The form package provides some validation types, also known as vtype. There are validations that are available, such as email, alpha, alphanum, and url.

A field with a validation type email (comment 5) must have a value in the format of an e-mail address, such as email@something.com.

A field with a validation type url (comment 6) must have a value in the format of a URL, such as http://something.com.

A field with validation type alpha (comment 7) allows the user to enter alphabetic values only, that is, from A to Z (lowercase and uppercase), and underscore (_).

A field with validation type alphanum (comment 8) allows the user to enter alphabetic and numeric values, that is, from A to Z (lowercase and uppercase), 0 to 9, and underscore (_).

We can also create a customized validation type and reuse it. Let's create a validation type, vtype, to validate an IP address:

Ext.apply(Ext.form.field.VTypes, { IPAddress: function(v) { return /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(v);); }, IPAddressText: 'Must be a numeric IP address', IPAddressMask: /[\d\.]/i });

On the IPAddress function, we applied a regular expression that validates an IP address. The regular expression is only valid when the user enters an IP address with the format xxx.xxx.xxx.xxx, where x must be a digit (number), and the user can enter one, two, or three digits. The IPs 1.1.1.1, or 1.11.111.111, or 111.111.111.111 are valid examples.

The preceding validation type is named as IPAddress (comment 9), and we used it on the last field declared in the previous form.

If we try to execute the preceding code and enter some invalid values in the form fields, we will get some errors, as shown in the following screenshot:

Form label

A label is simply text that can be displayed inside a form. We can use the following code to display a label:

Ext.create('Ext.form.Panel', { title: 'Form with Label', width: 100, bodyPadding: 10, renderTo: 'form-example', items: [{ xtype: 'label', forId: 'myFieldId', text: 'Just a Label', margins: '0 0 0 10' }] });

We can add a label in the items property of a form and use it with other form fields as well. The preceding code will output the following form:

Actions

We can handle two kinds of actions within forms: loading the data and submitting the data. We will implement an example where we have two buttons: one to load the data from the server and another to send the data to the server:

Ext.create('Ext.form.Panel', { title: 'Book Info', renderTo: 'form-example', width: 300, bodyPadding: 5, fieldDefaults: { labelAlign: 'left', labelWidth: 90, anchor: '100%' }, items: [{ xtype: 'hiddenfield', name: 'bookId' },{ xtype: 'textfield', name: 'bookName', fieldLabel: 'Title' },{ xtype: 'textfield', name: 'bookAuthor', fieldLabel: 'Author' }], buttons: [{ text: 'Load', handler: function() { var form = this.up('form').getForm(); form.load({ url: 'data/form.json', failure: function(form, action) { Ext.Msg.alert("Load failed", action.result. errorMessage); } }); } },{ text: 'Submit', handler: function() { var form = this.up('form').getForm(); form.submit({ url: 'form-submit.php', waitMsg: 'Sending the info...', success: function(fp, o) { Ext.Msg.alert('Success', 'Form submitted.'); } }); } }] });

To load data for populating the form, we need to call the load action. To do so, we need to specify the url config to load the data from; we can pass any additional parameter and we can also handle any error message.

When we load data to populate the previous form, the data should have the following format:

{ success: true, data: { bookId: 10, bookName: "Ext JS 4 First Look", bookAuthor: "Loiane Groner" } }

We have to match the name of the field with the name of the data that we are loading.

And to submit the data, we simply need to call the submit action and pass a url to send the data to. We can also handle any error message and add a 'waiting' message to be displayed while the form is being submitted.

The previous form will have the following output:

Summary

This article covered some really important changes made to the tree and form package. Related to these components, we covered some new features and API changes, using hands-on examples.


Further resources on this subject:


Ext JS 4 First Look A practical guide including examples of the new features in Ext JS 4 and tips to migrate from Ext JS 3 with this book and ebook.
Published: January 2012
eBook Price: £16.99
Book Price: £27.99
See more
Select your format and quantity:

About the Author :


Books From Packt


Mastering Joomla! 1.5 Extension and Framework Development
Mastering Joomla! 1.5 Extension and Framework Development

Drupal 6 JavaScript and jQuery
Drupal 6 JavaScript and jQuery

Joomla! 1.5 JavaScript jQuery
Joomla! 1.5 JavaScript jQuery

JavaScript Testing Beginner's Guide
JavaScript Testing Beginner's Guide

ExtGWT Rich Internet Application Cookbook
ExtGWT Rich Internet Application Cookbook

Swing Extreme Testing
Swing Extreme Testing

NetBeans Platform 6.9 Developer's Guide
NetBeans Platform 6.9 Developer's Guide

Ext GWT 2.0: Beginner's Guide
Ext GWT 2.0: Beginner's Guide


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software