Archive | ExtJS RSS feed for this section

ExtJS: Show a loading mask during Ajax calls

8 Aug

This is my first ExtJS post in a long while and sorry it’s not related to ExtJS4; maybe in the near future.Recently I was asked to implement a page wide mask during Ajax calls to prevent user from interfering with the current call by clicking on the screen or doing something they otherwise should not be doing while the call is running. I initially used the “wait:true” option of the Ext.MessageBox configuration, but the stakeholders did not like it since the progress bar it displayed was not synced to the actual length of the call. This is how I ended up implementing it:

function showLoadingMask(loadingMessage)
{
if (Ext.isEmpty(loadingMessage))
loadText = 'Loading... Please wait';
//Use the mask function on the Ext.getBody() element to mask the body element during Ajax calls
Ext.Ajax.on('beforerequest',function(){Ext.getBody().mask(loadText, 'loading') }, Ext.getBody());
Ext.Ajax.on('requestcomplete',Ext.getBody().unmask ,Ext.getBody());
Ext.Ajax.on('requestexception', Ext.getBody().unmask , Ext.getBody());
}

Hope it helps!

 

The meat of the functionality is in binding the mask() and unmask() function of the Ext.Element object to the ‘beforerequest’, ‘requestcomplete’ and ‘requestexception’ events of the Ext.Ajax object. I need a custom message to be displayed depending on the context that’s the reason why i encapsulated that logic in a function. I also tweaked my CSS a little to customize the mask style, overriding the ExtJS style, as well as the style of the message displayed to the user:


.ext-el-mask
{
color:gray;
cursor:default;
opacity:0.6;
background-color:grey;
}

.ext-el-mask-msg div {
background-color: #EEEEEE;
border-color: #A3BAD9;
color: #222222;
font: 1.2em tahoma,arial,helvetica,sans-serif;
}

.ext-el-mask-msg {

padding: 10px;

}

An Overview of the new ExtJS 4 Javascript framework

28 Apr

Having worked with ExtJS 3.1, it’s a real pleasure to see them keep pushing forward today with the release of version 4. ExtJS is a great framework for doing specific types of UI, and it takes a minute to wrap your mind around the mainly class system configuration programming style required to get things rolling but once you do, you realize the genius and flexibility behind. So kudos to the Sencha team on this achievement. I’ve looked at the press release and notes and  there is some improvement all around. The’ve added a lot more documentation than with previous releases. The class system has been extended to support dynamic loading, mixins and live dependency calculation. It is also completely sandboxed which for example allows you to run multiple versions of ExtJS within the same page. A major change, not that major if you have been following their work with the Sencha Touch mobile development framework, is the introduction of an MVC application architecture which should help streamline application development and faciliate transitions between team members. Welcomed as well is the addition of SDK tools with support for Javascript code optimization.

Topping the new feature list is the introduction of whole new plugin-free charting package (which Sench uses as an example of the sandboxing mentioned earlier by providing a chart written in ExtJS4 running fine within an ExtJS3 desktop) built on web technologies (SVg and VML). Also, the Grid, which i have used extensively in ExtJS3 is getting a makeover and has been modularized and is now extensible. Some of the extensions used in ExtJS3 and previous versions to enable for example column locking or cell editing are now obsolete as those features are available out of the box. Even better yet, the markup used to render the grid, which turned out to be quite a challenge for me as it was not 508 compatible, is now generated on demand which makes the need for pagination unecessary. TreeGrid has been integrated within the core code and supports all of the normal Grid functionality.

The Data package has been revamped and supports HTML5 localStorage and introduces the new Model class which allows you to accurately model your data layer. I’ve somehow played with its Touch counterpart, and will probably expand on it later, but it consists mainly of Fields, which represent the data fields, a Proxy, which is responsible for loading and saving data from a source, Associations which defines how Models relate to each other, and Validations for validating fields before a save. This very much reminds me of the CakePHP MVC architecture, which itself is based on the RoR model.

I would mention in passing improved support for theming, a new FocusManager class which makes it easier to enable keyboard navigation in your application, which should really help with with accessibility and 508 compliance.

Overall, I think that Sencha is maintaining a good direction with their product and as soon as I find a good project whose requirements fit the features offered by the framework,  it will be on top of my list of possibilities. In the mean time as I am working on some mobile development with Sencha Touch, it will allow be to familiarize myself better with all the new features.

Check out the press release for yourself: http://www.sencha.com/blog/ext-js-4-final/

How to POST cross domain and access the returned data using ExtJS

4 Feb

Please refer to my earlier post for background on this project.

Here is how to post data to a server on a different domain and receive data back using ExtJS (with inspiration from the GWT implementation).

First the FormPanel. The fields are standard and in the JS file we define two variables that are critical to making this work as they’ll allow us to keep track of what’s happening:

//These two variables are critical to the success of this operation

var xdm_formSubmitted = false;
var xdm_sameDomainRestored = false;
var renderDivId='myWidget';

var consigneeForm = new Ext.FormPanel(
{
 id: 'customerWidget',
 renderTo:renderDivId,
//By default, ExtJS uses XmlHttpRequest, this has to be a standard submit POST
standardSubmit: true,
 url: 'http://www.iamonanotherdomain.com/addThisCustomerForMe',
 border: false,
 hideBorders: true,
 items:
[
{
 xtype: 'textfield',
 name: 'customerFirstName'
 },
{
 xtype: 'textfield',
 name: 'customerLastName'
 }

],

bbar:
[
//This is where the form post is triggerd
{
 xtype: 'tbspacer',
 width: 300
} ,

{
 text: 'Save',
 handler: function()

{
 var form = Ext.getCmp('customerWidget').getForm();
 form.getEl().dom.target=iframePostContainerName;
 //On Submit we set this variable to true, you'll understand why in the event handler function
 xdm_formSubmitted = true;
 form.submit();
}
}
]

Notice that before submitting, we are assigning the target of the post to an iframe that exists on the page (form.getEl().dom.target=iframeName). This is the second and crucial step of the process. The iframe we are using is defined in the same js file:


//This hidden iframe is used to receive the results of the form POST:

{
 hidden: true,
 renderTo:'myHiddenIFrame',
 html:'<iframe id="' +iframePostContainerName + '" name="' +iframePostContainerName+ '"   onload=\"setupIFrameOnLoad(\'' + callbackFunction +  '\')"></iframe>'

}
//The callbackFunction variable is passed to the script by the calling page.

Which brings us to the third critical piece of the process which is to attach an event handler function to the onload event of the iframe, and this is where all the magic will happen. As you can see i’ve name my event handler function setupIFrameOnLoad and here is the definition:


setupIFrameOnLoad = function()
{
 //If the form was submitted and we have loaded data from our own domain, we are good. Thank you for coming
 //and here is your data! It's gonna be 5 dollars, Thank you!
 if(xdm_formSubmitted && xdm_sameDomainRestored)
 {
      //You can access your data now using the window.name property, but
      //I've got other plans in my callbackFunction so until such time...
      var callbackFunctionCall = callbackFunction + "()";
      eval(callbackFunctionCall);
 }
 //If this is the initial response from the POST, we are still in the POST server's domain
 else if(xdm_formSubmitted && !xdm_sameDomainRestored)
 {
       //Now you know we're about to restore the local domain right?
       xdm_sameDomainRestored = true;
       //localResourceUrl is passed by the calling page and points to a local 1px image or empty page
       document.getElementById(iframeName).contentWindow.location = localResourceUrl;
 }
 else
 {
       return ;
 }
}

What is happening here is basically the solution to the cross-domain issue in being able to pass data back to the calling page. Once your form is submitted, the response will be posted back to your iframe within your form page. Since the response is coming back from the server, security on the browser will keep us from accessing any of the properties on the window. To be able to access the data in window.name(which could be in JSON format if you wanted), the solution is simply to load a local resource file from your environment. It could be a simple blank 1px image or an empty HTML file, anything really to switch the iframe’s source content to be on the same domain where you are calling from. Once the iframe has reloaded you can simply access your data in the window.name property. To close the cycle, the response from your server needs to be a script which sets the window.name property. Using Spring, here is how my action was set up:

</pre>
private void addThisCustomerForMe(HttpServletRequest req, HttpServletResponse response) throws Exception
{

Long customerId = customerController.addOrUpdateCustomer(req);
response.setContentType("text/javascript");
OutputStreamWriter os = new OutputStreamWriter(response.getOutputStream());
StringBuffer buf = new StringBuffer("");
buf.append("<html>");
buf.append("<script type=\"text/javascript\">");
buf.append("  window.name="+customerId+";");    // can set json data here if you want
buf.append("</script>");
buf.append("</html>");

os.write(buf.toString());
os.flush();
os.close();

}

ExtJS: Printing Panels and Grids

27 Jan

One of the features i had to implement in one of the ExtJS application I’ve worked on  is printing. I was surprised to find out that there was no official print stylesheet for ExtJS. Overall, my strategy was to bring up the print layout within a popup window and call the window.print() command once rendering was done. My application being composed of a mixture of form fields, text and grids within the same screen, the number one issue here for me was how to show that same information in a printable format. No magic from ExtJS here, i referenced a PDF version of the application to redesign the UI for printing purposes. As far as the data itself, i could simply grab everything i needed by referencing the Ext object in the parent window:


var parentExt = window.opener.Ext;

My first issue was dealing with Grids. For that, i used the excellent Ext.ux.Printer extension by Ed Spencer that i had to tweak it a bit to get it to work with that i was trying to do since it is designed to print a grid within a popup window. The grid being part of my print page, i just modified its GridRenderer object to render the grid as an HTML table to the DOM. Note that, because of dimensions requirements being different for a print layout, and also display columns requirement being different between the application grid and its printed version, the more convenient resolution was to create a second hidden gridpanel within the application, with its dimensions and columns optimized for printing. The Ext.us.Printer also comes with a print stylesheet that i used as a base for my own.

The second issue i ran into once the layout was completed and rendered fine was that only the first of my two print pages would actually print content. The second page would just be blank. This was in both Firefox and IE6. It took me a while to figure out that this was due to the overflow:hidden CSS property set by the ExtJS stylesheet on most DIVs. Coupled with the way ExtJS renders content by nesting DIV even setting overflow to visible on the body tag of my popup window’s HTML did not yield the necessary results. I created a couple of CSS class for printing and used the bodyCfg property of the Panel object to finally get the necessary CSS to apply and the pages to print correctly.

Here is a summary of the code:


/* Added to my print style sheet */

.x-print-body

{
 overflow: visible;
}
.x-print-bwrap
{
 left: 0;
 overflow: visible;
 top: 0;
}

/* My Panel definition for printing*/

var printPanel = new Ext.Panel(
 {
 id:'printPanel',
 border:false,
 width:650,
 autoHeight:true,
 style:'padding:5px; overflow:visible',
 renderTo:'printContent',
 bodyStyle:'overflow:visible',
 bodyCfg:
 {
 tag: 'div',
 cls: 'x-print-body'  // Default class not applied if Custom element specified
 },
 bwrapCfg:
 {
 tag: 'div',
 cls: 'x-print-bwrap'  // Default class not applied if Custom element specified
 },
 items:
 []

}

&nbsp;

&nbsp;

Until such time!

 

Cross Domain Form Post (XDM) in ExtJS with returned data

24 Jan

I’ve helped a colleague of mine work on a cross domain widget we are building for our applications that allows a user from an application to add information to another application through a pop up widget. Implementation wise, we used a  a simple form implemented using ExtJS FormPanel class. Some of the form fields like State and County dropdowns are  dynamically loaded after rendering, again using ExtJS’s ScriptTagProxy for cross-domain retrieval, no issue here and for more information on cross-domain data retrieval you can check out this post here.

The issue rose when it came to posting the data back to the server on another domain for processing. I did a bit of googling around and stumbled upon this post which explains how it can be done using a combination of server side programming and a little bit of Javascript and browser knowledge. To make a long story short, basically you can post data to a remote domain, and get data back by using the window.name property of an hidden iframe within your form page. Simply ingenious if you ask me,  I knew GWT was using the technique but did not know the the inner workings. Check out this post here for the actual implementation.

By the way if you a looking for a good XDM library, check out EasyXDM.

Addendum: As noted by Oyvind Sean Kinsey in the comments below, what i was trying to accomplish here was XDM, Cross Domain Messaging and not XSS Cross Site Scripting which is actually a security exploit. Thanks again Sean for the clarification, i understand now why the EasyXDM library was renamed from being initially called EasyXSS.

Invalid Argument Error using ScriptTagProxy and IE6

7 Jan

As explained in my earlier post, i used Ext.data.ScriptTag proxy for some cross-domain AJAX data retrieval. Testing in IE6, I kept getting an “Invalid Argument” error in IE traced to this line in the code. A search on the Sencha forum turned up only this post, with no good solution posted. I traced the problem to the this.head.removeChild lines, for some reason, IE6 chokes on it. The fix is quite simple, i overrode the function to use parentNode.removeChild:

Ext.override(Ext.data.ScriptTagProxy, {
	destroyTrans : function(trans, isLoaded){
	try
	{
    	this.head.removeChild(document.getElementById(trans.scriptId));
	}
	catch(e)
	{
		//IE6 does not like removeChild() to be called directly from the parent element
		  document.getElementById(trans.scriptId).parentNode.removeChild(document.getElementById(trans.scriptId))
	}
    clearTimeout(trans.timeoutId);
    if(isLoaded){
        window[trans.cb] = undefined;
        try{
            delete window[trans.cb];
        }catch(e){}
    }else{
        // if hasn't been loaded, wait for load to remove it to prevent script error
        window[trans.cb] = function(){
            window[trans.cb] = undefined;
            try{
                delete window[trans.cb];
            }catch(e){}
        };
    }
}
	});

Of ExtJS’s ScriptTagProxy, Spring Actions, TreePanels and JsonStores

7 Jan

I recently had to implement an Organization lookup (backed by a LDAP repository) widget that will be used by some of the web applications I developed and this in a cross-domain environment.  The data displayed within the widget is broken down in Organization->Group->Person hierarchy and on selection of a Person and click of a button, a callback would sent some information about the selected person to a function in the calling application.

The  organizational hierarchy was implemented logically with a tree using an ExtJS TreePanel component. The challenge then became to propertly load this TreePanel. The ExtJS documentation specifies that a  “TreePanel must have a root node before it is rendered“,  which can be done by specifying the ‘root’ property, or using the setRootNode method. For the purpose of my widget, i needed the tree structure to be completely rendered with parent nodes already expanded to reveal children. What needed was to create this hierarchy in my Spring controller and return it to be loaded in my TreePanel. The Ext.data.ScriptTagProxy component was the key in allowing me to make the cross domain call the widget needs to retrieve the Tree data (Check this article if you are using IE6 and getting a Javascript error when using ScriptTagProxy).

Let’s get in the code, first starting with the Store definition:

var companyStore = new Ext.data.Store({

 autoLoad: true,
 //JSONP Proxy Setup
 proxy: new Ext.data.ScriptTagProxy({
 url: getCompanyTreeURL
 }),
 reader: new Ext.data.JsonReader(
 {
 //I named my json object root's 'node', just for the sake of it
 root: 'node',
 totalProperty: 'totalCount',
 idProperty: 'id',
 fields:
 [
 //Notice that these are TreeNode regular properties
 'id',
 'text',
 'children',
 'expanded',
 'leaf'
 ]
 }),
 listeners:
 {
 load: function(store, recs)
 {
 //Once the data is loaded from the backend, i can draw my tree, with the root node
 //already created by the backend
 var companyTree = new Ext.tree.TreePanel(
 {
 useArrows: true,
 autoScroll: true,
 animate: true,
 renderTo:'tree-div',
 enableDD: true,
 containerScroll: true,
 border: false,
 rootVisible: false,
 root: new Ext.tree.AsyncTreeNode(store.reader.jsonData.node[0])

 });
 }
 }
 });

This is an example of the JSON data that will be created manually in the backend and sent to the company store, and
then finally loaded in the TreePanel root node. Notice the stcCallback1001 function that the data is wrapped in? This is required when you are using a ScriptTagProxy, and you will need to manually do that wrapping in your backend code.

stcCallback1001(
{
 'node':
 [
 {
 'id':1,
 'children':
 [
 {
 'text':'Big Bad Company',
 'id':'222',
 'expanded':true,
 'children':
 [
 {
 'text':'Little Bad Company 1',
 'id':'1005',
 'leaf':true
 },
 {
 'text':'Little Bad Company 2',
 'id':'1010',
 'leaf':true
 }
 ]
 }
 ,
 {
 'text':'Bigger Bad Company',
 'id':'123',
 'expanded':true,
 'children':
 [
 {
 'text':'Little Bigger Bad Company',
 'id':'23423',
 'leaf':true
 }

 ]
 }
 ]
 }
 ]
})

Now switching to setting things up on the server side. If you want to ScriptTagProxy paradigm to work, your response content type needs to be set to “text/javascript” as it will be interpreted as such on the client side.


 @RequestMapping("getCompanyTree.json")
 public void getCompanyTreeList(@RequestParam("callback") String callback, HttpServletResponse response)
 {
 Company root = directoryService.getCompanyTree();
 response.setContentType("text/javascript");
 try
 {
 OutputStreamWriter os = new OutputStreamWriter(response.getOutputStream());
 String companyData = getCompanyNodes(root);
 //The callback parameter value was sent by the ScriptTagProxy object in the UI, we use it to wrap the data in the function call
 os.write(callback + "(");
 os.write("{'node':[{'id':1, " + companyData + "}]}");
 os.write( ")");
 os.flush();
 os.close();
 }catch (IOException e)
 {
 e.printStackTrace();
 }

 }

Still in the controller, this function will actually build out the tree structure in a string format using recursion:


public String getCompanyNodes(Company rootCompany)
 {
 String returnValue = new String();
 boolean firstNode = true;
 returnValue += "'children':[\n";
 for(Company comp : rootCompany.getChildren())
 {
 if(!firstNode)
 {
 returnValue += ",\n";
 }
 else
 {
 firstNode = false;
 }
 returnValue += "{\n";
 returnValue +="'text':'" + comp.getName() + "',\n" +
 "'id':'" + org.getId() + "',\n";
 if(comp.getChildren().size() > 0)
 {
 returnValue += "'expanded':true,\n";
 //A little recursion saves us a few lines of code
 String childrenNodeReturnValue = getCompanyNodes(org);
 returnValue += childrenNodeReturnValue;

 }
 else
 {
 returnValue += "'leaf':true\n";
 }
 returnValue += "}\n";;
 }
 returnValue += "]";
 return returnValue;
 }

There you have it. Hope it helps!

ExtJS: Reloading a Combobox’s Store using OnTriggerClick

19 Dec

I am working on an application where i use a grid and have a combobox outside the grid whose selections are based on the selected rows within the grid. Basically the content of the combobox is dynamically updated based on the selection of rows. I’ve tried many ways of going about solving this problem since as usual i started getting Javascript errors in IE6. I first thought and tried to override the onTriggerClick function of the combobox, but i kept getting this bug where the first click on the trigger did not display the return list of values, but instead displayed a very thin blue box, and any subsequent click would then actually load the store and display the values correctly. Unable to find a solution i gave up on this approach and then tried to use the expand event to reload the combobox’s store. There i also encountered a bug where i would get an “Object is undefined” error in IE while clicking to trigger the combobox. I then did some research on the Sencha forum and then tried moving my code inside the beforequery event listener of the combobox. This worked like a charm.

For code example and a more detailed explanation, check the thread on the Sencha forum:

http://www.sencha.com/forum/showthread.php?119029-Reloading-a-Combobox-using-onTriggerClick&p=552445#post552445

Popup Window in IE6: Window appears behind or below the main or parent window

19 Dec

I’ve been trying to open a popup window on selecting an item from a ComboBox in ExtJS and on opening the window, using

window.open,

the popup would start loading and then quickly move below the current page. The behavior i observed only happened in IE6, Firefox loaded the popup fine and it stayed on top. With a bit of research and debugging on my own, i came to find out that something in the ExtJS code was causing the focus to return the <body> element of the main page. The solution to the problem?

//Function to open the Window
openDelegateWindow= function()
{
var popup = window.open( "qantume.htm", "Selector", "alwaysRaised=yes, status=0, height=300, width=650, resizable=0" );
 if (window.focus)
 {
 popup.focus()
 }
 return false;
}

....
//Function  calls for opening the window in your code
...
setTimeout(openDelegateWindow()', 100);
....

By delaying the start of the openDelegateWindow code, i am figuring the code execution has enough time to return the focus to the <body> tag, and then the open popup window can start uninterrupted and keep the focus on the popup window.

For your reference also i have found this script, which is helpful in making your popup quasi modal in that it checks the focus to make sure that it is not away from the current popup window, unless the focus is on one of the controls within the popup window. If the focus is anywhere else, it is rendered back to the popup window, making it basically stay on top until the user closes it. This is not foolproof, there are ways around, but they are really for the “I really need to break your script” type of user, as explained by the author. You can find it here:

http://getyourwebsitehere.com/jswb/modwin/modalwinparent.html#code

 

ExtJS JSON Date Serializer: TimeZones and Format

23 Nov

Through development, we finally ran into the Javascript date issue where the dates sent between the front and the backend were not in sync. The front end was sending the dates to the backend in “YYYY-MM_DD” format, and the Jackson’s deserializer would append the application’s server timezone to the date, and this would result in inconsistent dates in the right conditions since it assumed that the date sent was in GMT and would try to convert it to the local time which ended up shaving a day of the date. To solve this problem, we decided the frontend would send the date with the local timezone which would be saved on the server. It took me a little while to figure it out, but i finally used this piece of code to set the serializer’s date format:

Ext.util.JSON.encodeDate = function(o)
{
 return '"' + o.format("Y-m-d'T'H:i:s.uZ") + '"';
}

To set the correct format for your particular server, check the Date format patterns accepted by ExtJS Date object:

http://dev.sencha.com/deploy/dev/docs/?class=Ext.util.JSON&member=encodeDate

Follow

Get every new post delivered to your Inbox.

Join 453 other followers