Reusable Ajax
GUI Widgets
for the
Java Developers
|
|
If you feel DHTML is too complicated to build online applications. You may be right. I felt the same way in
1999-2000. Web developers usually create the DHTML components and I felt that they don’t understand the needs of enterprise application developers, who were predominantly Java/C# programmers. Besides why
should only web (e.g. DHTML/JavaScript) developers have all the fun with Ajax/Web 2.0 hype?
|
|
If I need to create a hierarchical menu, why should I write 1000 lines of DHTML
code, which I didn’t know? Why can't I use a pre-built
reusable GUI class (as we can do in Java/Swing) to input a string for each ‘cell’ (or menu item) of the hierarchical menu
(Internet Explorer Only)
and let the Class do the writing? In 1999,
I set out to solve the problem for the developers. Only a developer best knows other Java developers' needs.
|
|
I had got hold of source code for a free DHTML component (Thanks to all of those, who donated great free DHTML components). I had
written a Java Class that takes a String for each cell. The Java class prints the entire Component code, as it is, except replaces the
contents of each cell, with the respective input string. After fixing few minor bugs and trivial modifications, Voila! I had a "data driven"
reusable GUI Widget. Still I didn’t know how to write DHTML code, but I created a GUI Widget that can do the writing for me.
|
|
Let me illustrate using an examples. To be brief, I use only simple DHTML
components (however this process can be repeated for other components). Please click on the following component to see a simple “expandable menu”:
|
|
Code for First child goes here
Code for Second child goes here
Code for Third child goes here
|
|
The
DHTML code for the above expandable menu look like this (after some
cleanup). Please identify, where each subcomponent's code go. Those parts are highlighted with
colors (you may ignore rest of the code).
|
|
1.
<SCRIPT language=JavaScript1.2>
2.
<!--
3.
var expandIE_UNQ_ID = function(el) {
4.
whichEl = eval(el + "Child");
5.
whichIm = eval(el + "img");
6.
if (whichEl.style.display == "none") {
7.
whichEl.style.display = "block";
8.
whichIm.src = "triUp.gif";
9.
}
10.
else {
11.
whichEl.style.display = "none";
12.
whichIm.src = "triDown.gif";
13.
}
14.
}
15. //-->
16. </SCRIPT>
17. <!----------
begin HTML Code ----------->
18.
<DIV>
19.
<TABLE BORDER='0'><TR><TD ALIGN='CENTER'>
20.
<A href='http://calljs()' onclick="expandIE_UNQ_ID('expand_UNQ_ID1');
return false">
21.
<IMG border=0 height=16
name=imEx id=expand_UNQ_ID1img src="triDown.gif"
width=16></A>
22.
</TD><TD VALIGN='CENTER'>
23.
<A href='http://calljs()' onclick="expandIE_UNQ_ID('expand_UNQ_ID1');
return false">
24. Code
for First parent goes here Call: Parent[i].CGM(Out)
25.
</A>
26.
</TD></TR></TABLE>
27.
</DIV>
28.
<DIV id=expand_UNQ_ID1Child STYLE="display:none;">
29.
Code for First child goes here
Call: Child[i].CGM(Out)
30.
</DIV>
31.
32.
<DIV>
33.
<TABLE cellpadding="3"><TR><TD
ALIGN='CENTER'>
34.
<A href='http://calljs()' onclick="expandIE_UNQ_ID('expand_UNQ_ID2');
return false">
35.
<IMG border=0 height=16
name=imEx id=expand_UNQ_ID2img src="triDown.gif"
width=16></A>
36.
</TD><TD VALIGN='CENTER'>
37.
<A href='http://calljs()' onclick="expandIE_UNQ_ID('expand_UNQ_ID2');
return false">
38.
Code
for Second parent goes here Call: Parent[i].CGM(Out)
39.
</A>
40.
</TD></TR></TABLE>
41.
</DIV>
42.
<DIV id=expand_UNQ_ID2Child STYLE="display:none;">
43.
Code for Second child goes here
Call: Child[i].CGM(Out)
44.
</DIV>
45.
46.
<DIV>
47.
<TABLE BORDER='0'><TR><TD ALIGN='CENTER'>
48.
<A href='http://calljs()' onclick="expandIE_UNQ_ID('expand_UNQ_ID3');
return false">
49.
<IMG border=0 height=16
name=imEx id=expand_UNQ_ID3img src="triDown.gif"
width=16></A>
50.
</TD><TD VALIGN='CENTER'>
51.
<A href='http://calljs()' onclick="expandIE_UNQ_ID('expand_UNQ_ID3');
return false">
52.
Code
for Third parent goes here Call: Parent[i].CGM(Out)
53.
</A>
54.
</TD></TR></TABLE>
55.
</DIV>
56.
<DIV id=expand_UNQ_ID3Child STYLE="display:none;">
57.
Code for Third child goes here
Call: Child[i].CGM(Out)
58.
</DIV>
59. <!---------- end HTML
Code ----------->
|
|
Now write Java class for the GUI
Widget, which prints the above GUI component code. The Class supports a
method at lines 14-18 below, which can be used to input a pair of
components (A parent and a Child ). This method may be called repeatedly
to input each pair of components. The Java class contains a Code
generation Method (or "CGM" in short) starting at lines 20
below, which prints JavaScript code at lines 1-16 above.
Then it prints above lines 18-30 in a for loop for each pair.
Please note all it does is: replace Blue &
Red colored lines in the above code, by the code for input components.
|
|
1. package
com.agile.libtemplate;
2. import java.io.*;
3. import java.util.*;
4. import javax.servlet.http.HttpServletRequest;
5. public class
ExpandMenu extends AgileTemplate {
6. int count = 0;
7. Object parents[] = new Object[20];
8. Objects childs[] = new Object[20];
9. String UNQ_ID = null;
10.
11. public ExpandMenu(AC_Info ACI) {
12. super(ACI);
13.
UNQ_ID = ACI.getUniqString();
14. }
15.
public void
setPair( Object par, Object chi) {
16. if (count >= 20) return; //Just an
example ...
Don't sue me!
17. parents[count] = par;
childs[count++] = chi;
18. }
19. //CGM:The Abstract 'Code Generation
Method' for AgileTemplates.
20. public int CGM
(PrintWriter out) {
21.
//Section-1: Print the
JavaScript
22. out.println(" <SCRIPT language=JavaScript1.2>
");
23. out.println(" <!-- ");
24. out.println(" var expandIE_"+UNQ_ID+" = function(el) {
");
25. out.println(" whichEl
= eval(el + 'Child'); ");
26. out.println(" whichIm
= eval(el + 'img'); ");
27. out.println(" if
(whichEl.style.display == 'none') { ");
28. out.println(" whichEl.style.display
= 'block'; ");
29. out.println(" whichIm.src
= 'triUp.gif'; ");
30. out.println(" }
");
31. out.println(" else
{ ");
32. out.println(" whichEl.style.display
= 'none'; ");
33. out.println(" whichIm.src
= 'triDown.gif'; ");
34. out.println(" }
");
35. out.println(" } ");
36. out.println(" //--> ");
37. out.println(" </SCRIPT> ");
38.
//Section-2: Print HTML code
39. out.println(" <!---------- begin HTML Code
-----------> ");
40. for (int i = 0; i , count; i++) {
41. // Print code for Parent
42.
out.println(" <DIV> ");
43.
out.println(" <TABLE BORDER='0'><TR><TD
ALIGN='CENTER'>");
44. out.println(" <A href='http://calljs()'
onclick=\"expandIE_UNQ_ID('expand_"+UNQ_ID+i+"'); return
false\"> ");
45. out.println(" <IMG border=0 height=16 name=imEx
id=expand_"+UNQ_ID
+ i +
"img src='triDown.gif' width=16></A>
");
46. out.println(" </TD><TD VALIGN='CENTER'> ");
47. out.println(" <A href='http://calljs()'
onclick=\"expandIE_UNQ_ID('expand_"+UNQ_ID+i+"'); return
false\"> ");
48.
if(parents[i]
instanceof
AgileTemplate) //
If our GUI Widget
49.
status =
((AgileTemplate)parents[i]).CGM(out);
50.
else // Allow user to
input HTML Code as well
51.
out.print(parents[i]);
// Use default String
52. out.println(" </A> ");
53. out.println(" </TD></TR></TABLE> ");
54. out.println(" </DIV> ");
55.
56. // Print code for Child
57. out.println(" <DIV id=expand_"+UNQ_ID
+ i +
"Child STYLE='display:none;'> ");
59.
// Include the
component code for the subcomponent.
60. if(childs[i]
instanceof
AgileTemplate)
61.
status =
((AgileTemplate)childs[i]).CGM(out);
62. else // Allow user to
pass HTML Code as well
63.
out.print(childs[i]); //
It must support ".toString()"
64. out.println(" </DIV> ");
65. }
66. out.println(" <!---------- end HTML Code
-----------> ");
67. } //
END of the CGM()
68. } // END of
the Class Definition.
|
|
One
suggested method to create a reusable GUI Widget is to, first find the
GUI Component and make sure it runs on all browsers. Once the code is
ready, it is a mechanical process to wrap it in a reusable GUI Class.
If you notice, there is a strange string “UNQ_ID”, which gets a
unique string and appends the string to many global identifiers to
avoid name collisions.
|
|
Q.
Why UNQ_ID was used?
Ans.
What
happens, if a web page uses two instances of ExpandMenus? For example, they both end up naming their first child “expand1Child”. Boom! Name collision.
When I click on one, the other expands. If I need to use only one instance per page, then I may not need to use “UNQ_ID” (unless
another DHTML or a Hierarchical menu component inadvertently end up using the same name).
|
|
//
Instantiate an Object to present an Expandable-Menu GUI
Component
ExpandMenu
ExpMenu
= new ExpandMenu
(HTTP_Request);
// Initialize the Object: set/input pairs of Subcomponents
ExpMenu.setPair(Parent,Child);//Pass
HTML-Code strings or GUI Objects
// Repeat the above call to add more pairs ... Isn't it simple?
|
|
Now you know how to create a reusable GUI Widget, if you can find a DHTML
component. Please be nice to your Web developer friend, he may give you
DHTML components. Also he might help you fix problems in certain
component you find on the web. Some implementations might not be quite
suitable to build reusable GUI Classes. Please see how you are going
to create a reusable GUI Class for the following DHTML Scrolled component?
|
|
|
|
The following is the code for the above "Component Scroller" DHTML component. As I didn’t know
any thing about DHTML Code, I only look for the location, where I should put my subcomponents. It turns out, it is quite simple as shown in the blue color
below. I didn't need to worry about rest of the scare looking code. However there is a
small catch ... continues below ...
|
|
<TABLE
border='1'><TR><TD>
//Section-1: Initialize the variables.
<SCRIPT language=JavaScript1.2>
// GUI-Class can have methods to input values & init following Vars
var
scrollerwidth = 300
var
scrollerheight= 180
var
scrollerbgcolor = '#FFFFE1'
var
scrollerbackground='back.gif'
var
scrollerwait = 3000
var
scrollerspeed = 80
var
scrollerjump = 10
//Section-2:
Initialize the subcomponents to be scrolled.
var messages=new Array()
messages[0]=
"First Component's HTML Code Goes Here";
messages[1]=
"Second Component's HTML Code Goes Here";
messages[2]=
"Third Component's HTML Code Goes Here";
//Section-3: Don't read The scare looking
JavaScript code below.
var mi
= 0;
if
(messages.length>1)
mi=2
else
mi=0
function
move1(whichlayer){
tlayer=eval(whichlayer)
if (tlayer.top>0&&tlayer.top<=scrollerjump){
tlayer.top=0
setTimeout("move1(tlayer)",scrollerwait)
setTimeout("move2(document.main.document.second)",scrollerwait)
return
}
if (tlayer.top>=tlayer.document.height*-1){
tlayer.top-=scrollerjump
setTimeout("move1(tlayer)",scrollerspeed)
}
else{
tlayer.top=scrollerheight
tlayer.document.write(messages[mi])
tlayer.document.close()
if (mi==messages.length-1)
mi=0
else
mi++
}
}
function move2(whichlayer)
{
tlayer2=eval(whichlayer)
if (tlayer2.top>0&&tlayer2.top<=scrollerjump){
tlayer2.top=0
setTimeout("move2(tlayer2)",scrollerwait)
setTimeout("move1(document.main.document.first)",scrollerwait)
return
}
if (tlayer2.top>=tlayer2.document.height*-1){
tlayer2.top-=scrollerjump
setTimeout("move2(tlayer2)",scrollerspeed)
}
else{
tlayer2.top=scrollerheight
tlayer2.document.write(messages[mi])
tlayer2.document.close()
if (mi==messages.length-1)
mi=0
else
mi++
}
function
move3(whichdiv){
tdiv=eval(whichdiv)
if (tdiv.style.pixelTop>0&&tdiv.style.pixelTop <= scrollerjump){
tdiv.style.pixelTop=0
setTimeout("move3(tdiv)",scrollerwait)
setTimeout("move4(second2)",scrollerwait)
return
}
if (tdiv.style.pixelTop>=tdiv.offsetHeight*-1){
tdiv.style.pixelTop-=scrollerjump
setTimeout("move3(tdiv)",scrollerspeed)
}
else{
tdiv.style.pixelTop=scrollerheight
tdiv.innerHTML=messages[mi]
if (mi==messages.length-1)
mi=0
else
mi++
}
}
function
move4(whichdiv){
tdiv2=eval(whichdiv)
if (tdiv2.style.pixelTop>0&&tdiv2.style.pixelTop <=
scrollerjump){
tdiv2.style.pixelTop=0
setTimeout("move4(tdiv2)",scrollerwait)
setTimeout("move3(first2)",scrollerwait)
return
}
if (tdiv2.style.pixelTop>=tdiv2.offsetHeight*-1){
tdiv2.style.pixelTop-=scrollerjump
setTimeout("move4(second2)",scrollerspeed)
}
else{
tdiv2.style.pixelTop=scrollerheight
tdiv2.innerHTML=messages[mi]
if (mi==messages.length-1)
mi=0
else
mi++
}
}
function
startscroll(){
if (document.all){
move3(first2)
second2.style.top=scrollerheight
second2.style.visibility='visible'
}
else if (document.layers){
document.main.visibility='show'
move1(document.main.document.first)
document.main.document.second.top=scrollerheight+scrollerjump
document.main.document.second.visibility='show'
}
}
document.writeln('<span id="main2" style="position:relative;width:'+scrollerwidth+';
height:'+scrollerheight+';
overflow:hiden;background-color:'+scrollerbgcolor+'
;background-image:url('+scrollerbackground+')">')
document.writeln('<div style="position:absolute;width:'+scrollerwidth+';
height:'+scrollerheight+'; clip:rect(0
'+scrollerwidth+' '+scrollerheight+' 0);left:0;top:0">')
document.writeln('<div class="scrollxx"
id="first2" style="position:absolute;width:'+scrollerwidth+';left:0;top:1;">')
document.write(messages[0])
document.writeln('</div>')
document.writeln('<div class="scrollxx"
id="second2"
style="position:absolute;width:'+scrollerwidth+';
left:0;top:0;visibility:hidden">')
document.write(messages[1])
document.writeln('</div>')
document.writeln('</div>')
document.writeln('</span>')
</SCRIPT>
</TD></TR></TABLE>
|
|
It
can accept only string, but how can I use other GUI Widgets such as a
HTML table in one or more of the Cells as subcomponents? I wanted a data
driven GUI Class that can be used with one line of code in my JSP file:
|
|
// Construct a Scroll Object and Pass Items
Array (Please Click Here for a Sample).
ScrollWidget
SC = new
ScrollWidget
( subcomponents_array );
|
|
However,
I can only initialize a String to replace the BLUE text. I was not a DHTML developer to redesign the scary looking
DHTML code to use "DIV" (as ExpandMenu did for
subcomponents. This is also using DIV but using document.writeln to
write). After thinking a while, (I
found a Java solution) I can
write the code into a String buffer. Then write a utility
to insert a back-slash before each special character such as double-quote and new-line in the
string. Now I can print the HTML code between the quotes. For example:
|
|
void CGM(out) { //CGM for the above Scroll Widget (Please see Sections above)
// Print the Section-1. Use the Object Input values to set JavaScript variables
Write_javascript_init_variables(out);
// Print the Section-2.
Use the Object Input subcomponents to generate code.
for(int i = 0; i < count; i++) {
String HTML_Code =
subcomponent[i].toString(); //Get Component code
// Insert a 'back-slash' before each double quotes or 'new-line'
String HTML_Code_String
= Util.HTML_to_String(HTML_Code);
out.println(" messages["+i+"
]= \"" + HTML_Code_String
+ "\";");
}
// Print the Section-3. Write the section 3 in the above DHTML Code.
Write_the_scary_javascript_code(out);
}
|
|
The
above was not a perfect solution but worked for many test
cases (note the Component Hierarchy figure at bottom Internet
Explorer Only). My Objective is provide a quick
& brief introduction. Basic principles are same, but I found many useful
techniques for building GUI Widgets, which are discussed in other pages
in the web site. As you could see 80% of the work is to create the perfect DHTML
component. Please
note, all Java class does is: replace Blue
Colored lines in section-2 by the code for input subcomponent.
|
|
Now,
being a Java (or C#) programmer you learned enough about reusable GUI Widgets. If you have a best friend who is an
excellent JavaScript programmer, you both might create better GUI Widgets
than many Ajax framework companies out there today. Most of them
offer between 7 & 15 components. How long it takes to match? Once a perfect
DHTML Component is created (or found on the Web), it may take couple of hours
to create a Java code generator wrapper (i.e. GUI Class). You need to pay attention to few other
simple issues such as variable-name collisions and use relative
coordinate system (if you want this to be used as a
subcomponent and/or has pop-up subcomponents).
|
|
For
an exercise, think how you could create a reusable
GUI Widget for "Google suggest" type text box. You may
take code from Google-suggest
and customizing it. Your Widget must take a URL as input, and use
the URL to quarry the "suggest" strings. Also try to avoid
name-collision, just in case, if you need more than one such Input text components in
a web page (e.g. two for inputting emails and one for search-phrase).
|
|
Int size = 32; // Text box size.
// There are certain differences, how the Text box functions based on Type.
// CGM can use simple “if-then-else”
to print appropriate set of JavaScript functions
// for the Component for given type. (Make
it highly customizable & reusable)
String Type = "SUGGEST_TYPE_EMAILS"; //
Type of the input, E.g: email/search-phrase
// The following URL is used to get the list of suggest-strings,
as user typing something
String URL = "http://email_list.Pioneer-soft.jsp";
SuggestTextBox STB = new SuggestTextBox ( size, style, URL, Type);
|
|
The
GUI Class let the application developers focus on 'Business-logic'
& "HIDE"
Ajax code.
One must be free to refine the GUI Class independently,
if he wish to do so later.
|
|
|
If one makes a bread, he can eat one day. If he builds a bread factory, many people can eat everyday.
It is possible to create reusable Ajax GUI Class (a factory) for any cool component or system we find on the web.
|
|
|
The only objective here is to briefly teach how one can build a Java class
wrapper, for any DHTML component one finds on the Web. If one wishes to create a great cross browser DHTML
Widget, one must start with a great cross browser DHTML code.
|
|
It
is also possible to use if-then-else to dynamically create browser specific
code. One may also implement a single GUI Class that use if-then-else to
dynamically generate any XML language such as SVG, XAML or MXML (for
each requesting viewer or version). If a component uses other GUI Objects
to build subcomponents, each GUI Objects in the hierarchy could generate
appropriate code compatible to the requesting browser/version, hence the
resultant component will be browser compatible.
|
|
If
it is not possible to implement certain features of the component on one
of the browsers, of course, no one can create full-featured cross
browser GUI component. Each browser needs to support at least one way to
implement the component.
Remember
browser wars during 1998-2000 and nightmares we had, to maintain bug-to-bug
compatibility to run on the competing browsers and their newer versions? This was one of the problem, which
led me to create the reusable GUI Classes in the first place (to separate
and encapsulate all the if-then-else statements). Do you like multilingual support with that? It also offers an elegant method for
that too The
cross browser issues can be handled: (A) in the DHTML code - the resultant code
is cross browser (the DHTML code contains if-then-else to handle cross browser
issues). (B) In Java at server: In this case, Java-code checks
the type and uses if-then-else to generates browser compatible DHTML-code. So, one
can't save the resultant page and view using other browser-types.
Remember,
both the Java-classes are cross-browser. But in the later case,
the Java-class creates DHTML code specific for the requesting browser-type.
Hence the resultant code runs on only one type of browser (for which it was
created, if I save the webpage code for later viewing). Now you know, why
many of our examples ended up
Internet-explorer only.
|
|
Now,
you know more about reusable GUI API than most Ajax or web application framework vendors out there!...Please click here to see
proof that they are absolutely clueless...Have fun with Web 2.0!
|
|
It
is too bad, even the best JavaScript developers can create at
most two or three layer
of data-driven Ajax component
hierarchy, which is inadequate to build next generation 2D/3D
graphics intensive online Applications.
|
|
Please click here, if you wish to learn creating reusable GUI Classes to present 2D/3D
vector graphics based animated interactive Ajax components.
|
|
Pioneer-soft
plans to create GUI-API, where each GUI Class generates either XAML or SVG
components dynamically for requesting client. This website has already demonstrated that we can create a
Class for any GUI component. Also the Ajax GUI Classes can be far more flexible
than possible for GUI Classes in the traditional platforms, such as
Java/Swing or Windows/VC++. Please click
here for proof and sample GUI Components.
|
|
If you wish to know what Pioneer-soft has accomplished in the recent years and our
future goals, Please click here. Thank you for visiting our web site!
|
|
Don’t you feel that most
other Ajax frameworks for building GUI or Rich Internet Applications today are too complex and yet offer incomplete
solution to such simple problems? (
We think, we
just figured out why Google created GWT?
)
If one wants to
include a 2D/3D Ajax GUI Component in his web application, why can’t he just use a reusable GUI Class to instantiate and initialize an
Object, as we can do in Windows/VC++ or Java/Swing? Why do their
reusable Ajax GUI-API end up with
fundamental limitations
and primitive?
Why can’t developers snap many GUI Objects together to build custom component-hierarchies and integrate them to build the online application?
If one wants to use just one or two components, why the browser end up downloading large JavaScript files, which contains code for nearly the entire framework?
Doesn't it potentially cause each page contains all the associated bugs and incompatibilities?
Why they upload entire GUI system to present a page containing just one Ajax
component?
How many Ajax components can they support in their GUI-API using increasingly
large JavaScript files?
How long such complex
and yet "incomplete" solutions survive? If they don’t survive, can you afford to
maintain their buggy API (e.g. port to newer browser/versions, evolving technologies or business needs etc.) along with your
application to protect your investment?
Most
vendors lock you to one framework and can't co-exist with other frameworks or tools!
|
|
|
|