Sencha Inc. | HTML5 Apps

Using Ext grid form dialogs to achieve CRUD

Published Jun 17, 2007 | Fenqiang Zhuang | Tutorial | Medium
Last Updated Jul 11, 2011

This Tutorial is most relevant to Ext JS, 2.x, 3.x.

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)

List function


List Account


Update Account


Delete 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;
	}
}
Share this post:
Leave a reply

Written by Fenqiang Zhuang

Commenting is not available in this channel entry.