WritingYourOwnComponentPeersPart3

Writing your own Component Peers Tutorials - Part 3

Written by Brad Baker

This is the third article in a series of articles on writing your own component peers. They are based on my experiences while creating the EchoPointNG library of components. The other articles are can be found here :

In the previous articles we have discussed how to create custom Echo2 components with their own rendering peers and client side code. This was all done in the context of the Echo2 framework and the server side Java programming model.

However there is a drawback to this approach and that is developer productivity, and specifically issues around debugging client side JavaScript. This article will discuss a new approach to developing new custom components that allows for a very productive developments sessions and well debugged components, the results of which can be used inside the Echo2 framework.

The Productivity Problem Explained

The EPNG development experience has shown that debugging the JavaScript for a custom component can take up to 50% of the development time. It relatively easier to invent a components properties, render a XHTML representation and design XML messages for the cleint side code however the hard bit I found was making sure the JavaScript worked as required. A compiler.. a compiler.. my laptop for a JavaScript compiler...

Echo2 has some nifty features to make sure only the JavaScript required for a component is dowloaded. This makes sure an Echo2 web app is only as big as it need be.

It does this by sending the required JavaScript in AJAX messages and dynamically evaluating the code. Now the astute reader may ask why doesnt it simply dynamically create scipt XHTML element and point it back to the JS code in question? Well that would be a great idea except that some browsers such as Safari dont allow this and the dynamic eval was the best cross browser solution.

The downside to this however is that the JavaScript debuggers out there, Venkman, IE Scipt Debugger and FireBug have trouble seeing the JS code and hence this makes it hard to set break points where you want and makes the debugging cycle harder than it might be.

In some ways this can be circumvented by hard coding debugger; statements into the source code but this is cumbersome and requires source code changes just to set a break point.

Also you have to restart your web app server in order for any JS changes to take effect and when I was using Tomcat this would take 30-60 odd seconds to get it restarted and the web app back to the point I wanted to debug.

For the record, moving to Jetty made this much much faster but it still has the same fundamental issues as explained above.

Just for the record, the use of dynamic eval of downloaded JavaScript code means that the code must be written using a specific syntax. So instead of writing functions like this

function fred() {
        alert('fred called');
}

you must write it like this :

fred = function() {
        alert('fred called');
};

Better JavaScript coders than me will explain why this is needed, but take my word for it, it wont work if you use the first syntax.

The Approach - Static Prototyping

It was Tod at NextApp that introduced me to what I have termed static prototyping. The basic idea is that you develop 90% of the JavaScript and XHTML code outside the Echo2 framework, get it working using just XHTML and JavaScript local files, and then fold it back into Java code.

It turns out that Echo2 imposes quite a lightweight skeleton into which the components are placed. In essence it is a static script include to the client side engine code ClientEngine.js and a transitional XHTML document declaration. For example the Echo2 Chat demo app looks like this at startup.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> </title><meta content="NextApp Echo v2.1.0.beta1" name="generator"/>
<script src="/ChatClient/app?serviceId=Echo.ClientEngine" type="text/javascript"> </script>
</head>
<body onload="EchoClientEngine.init('/ChatClient/app', true);" style="position:absolute;font-family:verdana, arial, helvetica, sans-serif;font-size:10pt;height:100%;width:100%;padding:0px;margin:0px;overflow:hidden;">
<form action="#" id="c_root" onsubmit="return false;" style="padding:0px;margin:0px;"/>
</body>
</html>

Thats it. The rest of the Echo2 web application is dynamically assembled via dynamic DOM inserts, retrieved via AJAX calls.

So you can create a static XHTML file that looks the same or similair and work inside that.

The key things here are that you must use a transitional XHTML declaration. This affects how the browser works so make sure you use it as declared above. The other key thing is to include the ClientEngine.js not via dynamic URLS but via the file system like this :

    <script type="text/javascript"
    src="file:///c:/java/NextApp_Echo2/SourceCode/src/webrender/java/nextapp/echo2/webrender/resource/ClientEngine.js">

From there you can start to prototype you component.

A Skeleton XHTML File

The following is a skeleton XHTML file that can be used to get you started to prototype your new component. Obviously you have to fix up the absolute file paths that I have used here to match your system.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> </title>
<meta content="NextApp Echo v2.1.0.beta1" name="generator"/>
<script type="text/javascript"
    src="file:///c:/java/NextApp_Echo2/SourceCode/src/webrender/java/nextapp/echo2/webrender/resource/ClientEngine.js"></script>
<script type="text/javascript"
    src="file:///c:/java/echopointng/projects/jar/src/ui/java/echopointng/ui/resource/js/ep.js"></script>

<script type="text/javascript">

yourOnLoadInit = function() {
        //
        // you code goes in here ro you could create it in another .js file that is included in like above
};

</script>

</head>
<body onload="yourOnLoadInit()" style="position:absolute;font-family:verdana, arial, helvetica, sans-serif;font-size:10pt;height:100%;width:100%;padding:0px;margin:0px;overflow:hidden;">

<form action="#" id="c_root" onsubmit="return false;" style="padding:0px;margin:0px;">

        <!-- you XHTML will be made a child of this element in Echo2 so you should as well -->

</form>
</body>
</html>

Up Front Component Rendering Design

Now you do have to do some design work up front. For example you should decide on the name of your component, what properties it will have (in Java terms) and how these properties will be propogated down to the client.

I am assuming here that you know you want a widget with say 5 UI properties and with a particular behaviour. Dont start coding till you know this. But of course no-one starts coding until they know what they want do they?

You will also need to decide on your XHTML rendering strategy  hey I think I just invented another term.

You need to decide whether you are going to pre-render the XHTML in the rendering peer before the client code gets invoked or whether you are going to "dynamically generate" the DOM elements yourself in JavaScript based on the data sent down to the client side message processor.

The XHTML Pre Render Strategy

In this design strategy, the XHTML is rendered on the server side and sent down to the client as a standard DOM insert message. By the time the client side message processor runs, all the DOM XHTML should be in place. This is great if most of the knowledge needed to build the output resides on the server. For example a Table component must run is TableCellRenders on the server to create the look and feel of the rows. So it makes sense to do all this on the server and create the XHTML as this is done.

The client side component JS code will then make a design assumption that all the XHTML it needs is already there.

So when you statically prototype such a component you have to make sure you type in the XHTML that the component would expect the server to produce.

I used this strategy when creating the TableEx component

The Client Side Generation Strategy

In this design strategy, the XHTML generation is all done on the client side. The client side message processor is sent all the data it needs to be able to dynamically create DOM elements for the component representation. This type of component is well suited to static prototyping because its self contained to the JS code itself.

When you statically prototype such a component you have need only set the same state information as would come down in the AJAX XML message from the rendering peer. This also makes for more light weight components from a messaging transport point of view because less bytes needs to be transferred to the client in order to represent the component.

I believe NextApp have started approach more. Recently the WindowPane component was re-implemented as a pure client-side component and many of the Echo2Extra components are done this way.

I used this strategy when creating the ColorChooser component.

An Example - Client Side Generation Strategy - ColorChooser

The ColorChooser component was one that was developed whereb all the DOM was gnerated by the client side code.

All I need to do was provide a parent container element that the color chooser would be placed in. This mimics what happens inside the Echo2 framework.

    <div id="c_177_parent"></div> 

The other trick was to simulate the XML messages that wouyld come down to the ColorChooser's message processor from its rendering peer. Again this can be done with some hand crafted JavaScript. You simply create an XML DOM structure that is the eqquivalent of what woulod come from the Echo2 framework. For example :

        var itemXML = document.createElement('item');
        
        itemXML.setAttribute('eid','c_177');
        itemXML.setAttribute('container-eid','c_177_parent');
        itemXML.setAttribute('swatchesPerRow','17');
             itemXML.setAttribute('enabled','true');

        
        itemXML.setAttribute('styleDefault','padding:1px;border:1px solid #317082;');
        itemXML.setAttribute('styleSwatch','width:10px;height:10px;border:1px solid #000000;');
        
        //itemXML.setAttribute('colors','#1234|#...|...|...');
        itemXML.setAttribute('colorTitles','');

        itemXML.setAttribute('interestedPartyId','c_189');
        itemXML.setAttribute('currentColorSelection','#ff33ff');
        itemXML.setAttribute('showCurrentColorSelectionSwatch','true');
        itemXML.setAttribute('currentColorSelectionText','Current Color : ');
        

        
        var colorChooser = new EPColorChooser('c_177');
        colorChooser.init(itemXML);

Notice here I am creating an item XML element and then setting attributes into it. This is exactly how the ColorChooser rendering peer would send down its property values. In fact this helps design the property names used and the format of the property values.

You can find a listing of the ColorChooser prototyping code here : ColorChooser Prototype Code

An Example - XHTML Pre Render Strategy - TableEx

TableEx is an example where I used the pre-rendered XHTML design strategy. I wanted to add the ability to scroll the table body without moving the table header and also to make the columns re-sizeable via mouse movements.

I decide to use the the pre-rendered XHTML design strategy for 2 reasons. The first was that I had XHTML already coded to render on the server and also if the user didnt put the TableEx into "scrollable" mode then I didnt want to change anything from a output point of view.

So what I did was mock up a static XHTML file that included the basic Echo2 library and the EPNG libraries.

I then created the TableEx structure in static XHTML as I envisaged the TableEx peer would produce. In essence it was a 2 div setup where the table header rows would be at the top in one table and the body data would be in a second "scrollable" div at the bottom. The JavaScript would be responsible for keeping the widths of the top header table in synch with the bottom data table.

Now a couple of lessons I learned was in the use of CSS classes in the static XHTML markup. Any XHTML/CSS designer knows that its better to use style declarations like this and apply them to a XHTML element via the class="xxx" attribute.

<style>
...
...
.contentDivClass {
                width:100%;
                background-color:#00ff00;
                height:200px;
                overflow:scroll
}

td.cell1 {
        border:1px blue solid;
        overflow:hidden;
        background-color:#DCDCDC;
        overflow:hidden;
        white-space:nowrap;
}

td.cell2 {
        border:1px blue solid;
        overflow:hidden;
        background-color:#E9967A;
        overflow:hidden;
        white-space:nowrap;
}

td.cell3 {
        border:1px blue solid;
        overflow:hidden;
        background-color:#FF8C00;
        overflow:hidden;
        white-space:nowrap;
}
</style>
...
...

<div id="contentDiv" class="contentDivClass">
...
...
        </tr>
                <tr>
                <td class="cell1">Column 1</td>
                <td class="cell2">Column 2</td>
                <td class="cell3">Column 3</td>
        </tr>
                <tr>
                <td class="cell1">XXXXX X</td>
                <td class="cell2">XXXXX X</td>
                <td class="cell3">XXXXX X</td>
                </tr>

This expecially true for repeating style declarations like the cells as it makes it easier for a static XHTML developer to change. However in Echo2 things are a little different. First of Echo2 doesnt use class names when rendering elements (because many browsers dont support dynamic style elements) instead it renders a direct style="xxxx;yyyy;" attribute.

Now from the server code point of view you dont care because your XHTML output is in a programmatic loop and its the same effort wheter theres one cell or a thousand. But in static XHTML thats a lot of cut and paste so it seems easier to use CSS class declrations.

However another downside of CSS classes is that the JavaScript might not do what you want. For example given the examnple above, if a I called this code what would you expect to be output?

var value = document.getElementById('contentDiv').style.width;
alert(value);

100% ? Maybe the actual width of the element ? No it will return empty string. Because the elements style attribute has not be set to anything. Rather is inheriting its width from the CSS class selector.

So this is a bit a challenge for your prototype JS code because in the Echo2 world it will be done via style="xxx" attributes and not classes. So watch out for this. If your code relies on it (and TableEx did a lot) then be prepared to either use a style="" attribute or another mechanism to get the style actual value such as EP.StyleSheet.getComputedStyle(elem).

As for the code itself, the re-sizing of the columns made up 95% or the development effort. I wont go into this too much because its a loengthy tome in itself. I spent hours and hours trying to work out the behaviour of fixed width tables on IE and FireFox. But because of this the ability to do faster JS debugging was invaluable in getting it working and the static prototyping appraoch helped.

I wrote all the code as if I was really inside the Echo2 framework and when the time came to fold it back in, I simply cut and pasted most of the JavaScript back into the real tableex.js of the EPNG project and then set about the task for re-trofitting the TableExPeer renderer to produce the XHTML I needed to make a scrollable table.

You can find a listing of the TableEx prototyping code here : TableEx Prototype Code

Conclusion

You may have noticed that the code in these prototypes is not the final code in the EPNG code set. This is actually the point. I prototyped the code in an environment that had certain developer productivity advantages but later moved it in the EPNG code set, where a whole different set of developer advantages are available like real classes and syntax checking compilers.

In this case the prototype code is somewhat throw away. I suppose you could try to keep the 2 in synch somehow for future development but unless you are completelty re-implementing the components, I suggest you can add extra features the old way by changing the cleint side code and restarting the web app server. After all this is how I wrote the most of the EPNG components.

That said I wish I had discovered this mechanism earlier as there would be more components available in EPNG as I feel it dramatically boost your productivity.

last edited 2006-07-19 01:54:21 by bbakerman