Ext JS - Learning Center

Tutorial:Using Ext grid form dialog to achieve paging list, create, edit, delete function

From Learn About the Ext JavaScript Library

Jump to: navigation, search
Summary: This article will walk you through using the grid, form and dialog in Ext to achieve list, create, update, delete, search and paging functions
Author: Fenqiang Zhuang
Published: June 16, 2007
Ext Version: 1.1
Languages: en.png English

In web applications, most of the pages can be cataloged as: List, Create, Read, Update, Delete. The Ext documentation center provides a good example for an inline editing grid. However, in many cases, an inline editing grid is not enough and a pop-up dialog is needed to achieve different kinds of fields in our form. Below is one of my simple examples to show you how to create/update a dialog form based on a user action in the grid. Download Source - for Ext 1.1 version

Demo updated with Ext 2.0 (www.feyasoft.com) -> click "Grid form dialog demo" icon on desktop

Source Code for Ext 2.0 -> click "Feyasoft source code" icon on desktop (navigate to javascript->feyasoft->demouser folder)

Contents

List function

List Account
List Account
Update Account
Update Account
Delete Account
Delete Account
Create Account
Create Account

This is the list accounts page, it includes paging, search/filter function, add and delete function.

Add filter/search drop-down menu in the Grid head panel.

/************************************************************
    * create header panel 
    * add filter field - search function
    ************************************************************/
	var gridHead = grid.getView().getHeaderPanel(true);
	var tb = new Ext.Toolbar(gridHead);
 
	filterButton = new Ext.Toolbar.MenuButton({
	    icon: 'public/image/list-items.gif',
	    cls: 'x-btn-text-icon',
		text: 'Choose Filter',
		tooltip: 'Select one of filter',
		menu: {items: [
			new Ext.menu.CheckItem({ text: 'First Name', value: 'firstname', checked: true, group: 'filter',checkHandler:onItemCheck }),
			new Ext.menu.CheckItem({ text: 'Last Name', value: 'lastname', checked: false, group: 'filter', checkHandler: onItemCheck }),
			new Ext.menu.CheckItem({ text: 'Username', value: 'username', checked: false, group: 'filter', checkHandler: onItemCheck })	
		]},
		minWidth: 105
	});
	tb.add(filterButton);
 
	// Create the filter field
	var filter = Ext.get(tb.addDom({ // add a DomHelper config to the toolbar and return a reference to it
	     tag: 'input', 
	     type: 'text', 
	     size: '30', 
	     value: '', 
		 style: 'background: #F0F0F9;'
	}).el);			
 
    // press enter keyboard
    filter.on('keypress', function(e) { // setup an onkeypress event handler
      if(e.getKey() == e.ENTER && this.getValue().length > 0) {// listen for the ENTER key
          ds.load({params:{start:0, limit:myPageSize}});
      }
    });

Add paging, add and delete button in the Grid footer panel.

/************************************************************
    * create footer panel 
    *    actions and paging
    ************************************************************/ 
    var gridFoot = grid.getView().getFooterPanel(true);
 
    // add a paging toolbar to the grid's footer
    var paging = new Ext.PagingToolbar(gridFoot, ds, {
        pageSize: myPageSize,
        displayInfo: true,
        displayMsg: 'total {2} results found. Current shows {0} - {1}',
        emptyMsg: "not result to display"
    });
    // add the detailed add button
    paging.add('-', {
        pressed: true,
        enableToggle:true,
        text: 'Add',
        cls: '',
        toggleHandler: doAdd
    });    
    // add the detailed delete button
    paging.add('-', {
        pressed: true,
        enableToggle:true,
        text: 'Delete',
        cls: '',
        toggleHandler: doDel
    });

On paging function, you need send filterTxt and value to server before loading data.

/************************************************************
    * load parameter to backend 
    *    have beforelaod function
    ************************************************************/
    ds.on('beforeload', function() {
	  ds.baseParams = { // modify the baseParams setting for this request
	    filterValue: filter.getValue(),// retrieve the value of the filter input and assign it to a property named filter 
	    filterTxt: filterButton.getText()
	  };
     });
    // trigger the data store load
    ds.load({params:{start:0, limit:myPageSize}});

List result get JSON data from server-side:

({"totalCount":52,"results":[{"lastname":"Zhuang","password":"12","firstname":"Fenqiang"
,"enterDate":"Jun 18, 2007","username":"ffzhuang","id":40},{"lastname":"gh","password":"gh","firstname":"gh-last","enterDate":"Jun 18, 2007","username":"gh","id":11}]})

Delete function

Delete function will get the selected id(s) and create JSON data and send JSON data to Java server-side for handle.

/************************************************************
    * Action - delete
    *   start to handle delete function
    *   need confirm to delete
    ************************************************************/	
    function doDel(){
        var m = grid.getSelections();
        if(m.length > 0)
        {
        	Ext.MessageBox.confirm('Message', 'Do you really want to delete it?' , doDel2);	
        }
        else
        {
        	Ext.MessageBox.alert('Message', 'Please select at least one item to delete');
        }
    }     
 
    function doDel2(btn)
	{
       if(btn == 'yes')
       {	
			var m = grid.getSelections();
			var jsonData = "[";
	        for(var i = 0, len = m.length; i < len; i++){        		
				var ss = "{\"id\":\"" + m[i].get("id") + "\"}";
				//alert(ss);
				if(i==0)
	           		jsonData = jsonData + ss ;
			   	else
					jsonData = jsonData + "," + ss;	
				ds.remove(m[i]);								
	        }	
			jsonData = jsonData + "]";
			ds.load({params:{start:0, limit:myPageSize, delData:jsonData}});		
		}
	}

And delete parameter to server side with JSON data like this: delData=[{"id":"5"},{"id":"6"}]

Add function

Add function will pop-up a form dialog to let client enter information for save.

/************************************************************
    *  To create add new account dialog now....
    ************************************************************/   
    function doAdd(){
        var aAddInstanceDlg;
 
        if (!aAddInstanceDlg) {
            aAddInstanceDlg = createNewDialog("a-addInstance-dlg");       
            aAddInstanceDlg.addButton('Reset', resetForm, aAddInstanceDlg);
            aAddInstanceDlg.addButton('Save', function() {       
                // validation now
		    	if (password_tf.getValue()!=cPassword_tf.getValue()) {
		    	    password_tf.markInvalid("passwords not match");
		    		password_tf.focus();
		    		return; 
		    	}
 
		    	// submit now... all the form information are submit to the server
		    	// handle response after that...
		    	if (form_instance_create.isValid()) {
				    form_instance_create.submit({
						waitMsg:'Creating new account now...',
						reset: false,
						failure: function(form_instance_create, action) {
						    Ext.MessageBox.alert('Error Message', action.result.errorInfo);
						},
						success: function(form_instance_create, action) {
						    Ext.MessageBox.alert('Confirm', action.result.info);
						    aAddInstanceDlg.hide();
						    ds.load({params:{start:0, limit:myPageSize}});
						}
					});					
				}else{
					Ext.MessageBox.alert('Errors', 'Please fix the errors noted.');
				}	    	
		    });
 
			var layout = aAddInstanceDlg.getLayout();
            layout.beginUpdate();
            layout.add('center', new Ext.ContentPanel('a-addInstance-inner', {title: 'create account'}));
            layout.endUpdate(); 
 
            aAddInstanceDlg.show();
	    };        
	}

Java server side code to handle add function

protected ActionForward createAccount(ActionMapping mapping,
			ActionForm form, HttpServletRequest request,
			HttpServletResponse response) throws Exception
	{
		Account account = AccountUtil.setAccount(request);
 
		// check condition now...validation
		JSONObject errors = new JSONObject();
		AccountService accountService = new AccountService();
		if (accountService.isUsernameExist(account.getUsername()))
		{
			errors.put("id", "username");
			errors.put("msg",
					"this username also in use, please select another one");
		}
 
		// if any validation errors
		if (errors.length() > 0)
		{
			// set response now with json format
			System.out.println("have error now...");
			JsonMVCUtil.jsonErrorsResponse(errors, request, response);
		} else
		{
			// save this object
			try
			{
				account.setCreationDate(new Date());
				accountService.createOrUpdate(account);
				JsonMVCUtil.jsonOkResponse(
						"You have successfully created this accout", request,
						response);
			} catch (Exception e)
			{
				JsonMVCUtil
						.jsonFailResponse("Internal Error, please try again",
								request, response);
			}
		}
 
		return null;
	}

Update function

User will select one of rows in the list and double click this row. Update function will fetch the data from server-side by user selected id and show the result in dialog form for client to update. When user update the form information and submit the result to server, there have 2 kinds of validation, one is client side (isValid and markInvalid), another is from server side during update failed (JSON data).

In general, it need transfer a hidden "id" to server side for update. We can put this id (and any hidden fields) as one of params during submit form.

/************************************************************
    * Action - update
    *   handle double click 
    *   user select one of the item and want to update it
    ************************************************************/
    grid.on('rowdblclick', function(grid, rowIndex, e) {
	    var selectedId = ds.getAt(rowIndex).id;
 
	    // Get information from DB and set form now...
            // Only need to use a ScriptTagProxy if the URL is at another domain
		var account_data = new Ext.data.Store({
		    proxy: new Ext.data.HttpProxy({url:'/yuiExt/listAccount.do?action=loadData&id=' + selectedId}),
			reader: new Ext.data.JsonReader({},['id','firstname','password','username','lastname']),
			remoteSort: false
		});
 
		account_data.on('load', function() {
 
		    // set value now
                    var newRec = account_data.getAt(0);
		    var updateId = newRec.data['id'];
		    username_show.setValue(newRec.get('username'));
		    firstname_show.setValue(newRec.get('firstname'));
		    lastname_show.setValue(newRec.get('lastname'));
		    password_show.setValue(newRec.get('password'));
		    cPassword_show.setValue(newRec.get('password'));
 
			var updateInstanceDlg;        
	        if (!updateInstanceDlg) {
	            updateInstanceDlg = createNewDialog("a-updateInstance-dlg"); 
	            updateInstanceDlg.addButton('Save', function() {       
	                // validation now
			    	if (password_show.getValue()!=cPassword_show.getValue()) {
			    	    password_show.markInvalid("passwords not match");
			    		password_show.focus();
			    		return; 
			    	}
 
			    	// submit now... all the form information are submit to the server
			    	// handle response after that...
			    	if (form_instance_update.isValid()) {
					    form_instance_update.submit({
					        params:{id : updateId},
							waitMsg:'Updating this account now...',
							reset: false,
							failure: function(form_instance_update, action) {
							    Ext.MessageBox.alert('Error Message', action.result.errorInfo);
							},
							success: function(form_instance_update, action) {
							    Ext.MessageBox.alert('Confirm', action.result.info);
							    updateInstanceDlg.hide();
							    ds.load({params:{start:0, limit:myPageSize}});
							}
						});					
					}else{
						Ext.MessageBox.alert('Errors', 'Please fix the errors noted.');
					}	    	
			    });
 
	            var layout = updateInstanceDlg.getLayout();
	            layout.beginUpdate();
	            layout.add('center', new Ext.ContentPanel('a-updateInstance-inner', {title: 'Update Account'}));
	            layout.endUpdate(); 
 
	            updateInstanceDlg.show();
	        }  
		}); 
 
		account_data.load();	    
	});

This is the JSON data transfer from server-side.

([{"lastname":"11","password":"12","firstname":"11-12345","enterDate":"Jun 16, 2007","username":"ffzhuang","id":40}]);

Handle JSON data in Java server side

To create a JSON object in server side with Java. We can use the lib in "json.org". It provides a quick API to parse POJO and list etc. See the following example code:

public JSONObject toJSONObject() throws Exception {
        JSONObject json = new JSONObject();
        json.put("totalCount", totalCount);
 
        JSONArray jsonItems = new JSONArray();
        for (Iterator iter = results.iterator(); iter.hasNext();) {
             jsonItems.put(iter.next().toJSONObject());
        }
        json.put("results", jsonItems);
 
       return json;
    }

To parse JSON data from JS in serve side, please see the following example:

JSONArray jsonArray = JsonUtil.getJsonArray(jsonString);
    // JsonUtil.getJsonArray(jsonString) just do this ->
    // jsonArray = new JSONArray(jsonString);
 
    // loop through - get from json and update
    for (int i = 0; i < jsonArray.length(); i++) {
        JSONObject jsonObject = jsonArray.getJSONObject(i);
        String id = jsonObject.getString("id");
    }

Here is the general JSON response Java function. You need set the ContentType as application/x-json type.

public static void jsonResponse(JSONObject jsonData,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception
	{
		// check whether it is script Tag...
		// which is called by JSON
		boolean scriptTag = false;
		String cb = request.getParameter("callback");
		if (cb != null) {
			scriptTag = true;
			response.setContentType("text/javascript");
		} else {
			response.setContentType("application/x-json");
		}
 
		PrintWriter out = response.getWriter();
		if (scriptTag) {
			out.write(cb + "(");
		}			
		response.getWriter().print(jsonData);
		if (scriptTag) {
			out.write(");");
		}
	}

Here is the class to load data and create a JSON response to fill the form

private ActionForward loadData(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception
	{
		// get id first
		String accountId = (String) request.getParameter("id").trim();
		AccountService accountService = new AccountService();
		Account account = accountService.loadAccountById(new Long(accountId));
 
		// push data back to Ajax page
		JsonMVCUtil.jsonResponse(account.toJSONObject(), request, response);
 
		return null;
	}

Here is my POJO account class

public class Account extends BaseObject
{
	private static final long serialVersionUID = -2384479668303690161L;
	private String username = null;
	private String password = null;
	private String firstName = "";
	private String lastName = "";
	private String phone = "";
	private String emailAddress = "";
	private Date creationDate = new Date();
	private String activeYN = "active";
 
        // get set function
 
	/**
	 * This will create a JSON object and output to client. Get the data from
	 * database and display result to GUI
	 * 
	 * @return
	 * @throws Exception
	 */
	public JSONObject toJSONObject() throws Exception
	{
		String enterDateString = DateUtil
				.format(creationDate, "MMM dd, yyyy");
		JSONObject json = new JSONObject();
		json.put("username", username);
		json.put("password", password);
		json.put("firstname", firstName);
		json.put("id", id);
		json.put("lastname", lastName);
		json.put("enterDate", enterDateString);
 
		return json;
	}
}

What's Next?

As always, the API documentation and examples are a great way to learn how to tweak this form to fit your needs.

  • This page was last modified 22:57, 31 May 2008.
  • This page has been accessed 105,847 times.