Re: WOComponent children
Re: WOComponent children
- Subject: Re: WOComponent children
- From: Paul Hertz <email@hidden>
- Date: Tue, 15 Jan 2008 12:19:28 -0600
Thanks, this is interesting. Having just run into a colleague
wretling with a similar problem--portlets that add their own CSS
without any way of tracking just what they will do ahead of time
(unless the file-parsing solution suggested earlier in this thread
would work), I have a new appreciation of what you're trying to do.
I've had similar situations where a component neads to have .js files
for an HMTL editor (TinyMCE) loaded into the header, and has to
notify the topmost parent, a PageWrapper class. In at least some
situations, the script won't run if it's in the body, and it's
definitely preferable to load it once only.
Many of these problems seem to originate in the piecemeal nature of
Web 2.0 applications. Writing everything for a Java client, instead
of using an HTML browser at all, could be attractive--though it's
clear that all stuff that's already done for you by browser
technologies is worth tapping into, as is integration into the wwweb.
At 12:23 PM -0500 1/15/08, email@hidden wrote:
Thank you Paul Hertz, Lachlan Deck, and Alexandar Spohr for your
recent thoughts on the subject.
The ultimate goal is "resusability." If you were to post to me and
say "Aaron, those widgets you made are cool, can I use them?" I
would like to be able to just give you a framework file and let you
just link it into your project. All you should have to worry about
is dropping "CoolDateWidget" on a page and it would just work. You
shouldn't be concerned with the following three points:
1) Don't worry that CoolDateWidget is inside a WOConditional that is
inside an AjaxUpdateContainer. When the page loads you won't see the
CoolDateWidget, but after selecting a choice from a dependent popup
(dropdown), it would appear. I don't want this widget to break on
you in this circumstance.
2) I shouldn't have to tell you to place one or more javascript and
CSS resources in some static HTML web directory and then add them to
the head of your page wrapper. You might forget one of them, you
might place them out of order, the site admin might forget to upload
them, moreover... why should you care? The CoolDateWidget should be
a black box with exposed bindings that just works. You don't *have*
to know how it works.
3) I shouldn't have to tell you to extend all of your page level
components from MyGenericComponent in my framework instead of
WOComponent. Doing this might allow me to get things right with some
special logic but now you are in a bind. What if not only I did this
but some guy named Tom also did this in his framework. Now you have
to choose between Tom's framework and mine because you can't extend
from both MyGenericComponent and TomsGenericComponent at the same
time. Bummer.
Point 3 seems to me to be the biggest issue, in the absence of
standard methods in WOComponent for modifying the Head tags and
scripts.
Those three points make it very clear. It is presently impossible to
develop effective reusable objects for the Web 2.0 era in
WebObjects. Mike's and Lachlan's ideas to manipulate the DOM to load
the required resources could work in the major browsers, but it's
tricky and appears to be the easiest way around a fatal flaw in the
WO component architecture. That fatal flaw is that the tree of
components that make up a page does not exist. Each node gets
temporarily built piece meal through a lot of pushing and popping in
and out of woContext in each phase of the request-response loop.
Another way to say this is that there is no notion of "children" and
that is what is really needed. The "parent" relationship should be
considered optional and avoided for reusable components.
Paul's method of building a tree of WOComponents sounds like it
could bare fruit, but he'll need to post some code samples. Still,
he is effectively building around a deficiency in the WOComponent
architecture by actually creating the desperately needed tree
structure. It also sounds like you'd have to do a bit of book
keeping to set up this node structure somewhat obviating the benefit
for the purpose at hand.
I doubt that my code is ready for much exposure. I hope to get a
major revision (simplified!) done this summer, and then we'll see.
But if you're curious, some JavaDocs (in need of updating) are here:
http://collaboratory.nunet.net/researchTemp/javadocs/
The overview may be a reasonably clear explanation of what I was
setting out to do. This was also my first serious Design Patterns
project.
And you're right, book keeping is critical. Keeping the compoments
light weight is, too. Since some are intended for scientific research
web services, this is particularly tricky. But that's another story.
-- Paul
For the record, our current solution works for us and is a
reasonable compromise. It fails from item #3 outlined above so it
makes our code not easily used by others. But, if you find yourself
building lots of AJAX enabled reusable components in WebObjects,
this could work well for you across all browsers.
Summary:
Create a general WOComponent that will be the ancestor for all your
page level components, call it "GenericComponent.wo" for this
illustration. It could extend WOComponent, ERXWOComponent, or
something else. Every time you make a new AJAX reusable component,
you'll need to make GenericComponent.wo aware of it. Here's the
source code to GenericComponent.wo:
import com.webobjects.foundation.*;
import com.webobjects.appserver.*;
import com.webobjects.eocontrol.*;
import com.webobjects.eoaccess.*;
import er.ajax.AjaxUtils;
public class GenericComponent extends WOComponent {
public GenericComponent(WOContext context) {
super(context);
}
public void appendToResponse(WOResponse response, WOContext context) {
super.appendToResponse(response, context);
// It's important that <html> and <head> tags are
already present so we call
// "addChildrenResourcesInHead" after the
appendToResponse cycle but right before that cycle ends.
addChildrenResourcesInHead(response, context);
}
protected void addChildrenResourcesInHead(WOResponse
response, WOContext context) {
if (parent() == null) {
// Only perform this logic if we are the
outermost component (no parent)
// start - General and WOnder related items
AjaxUtils.addScriptResourceInHead(context,
response, "Ajax", "prototype.js");
AjaxUtils.addScriptResourceInHead(context,
response, "Ajax", "scriptaculous.js");
AjaxUtils.addScriptResourceInHead(context,
response, "Ajax", "wonder.js");
// end - General and WOnder related items
// your app or framework specific
CoolDateWidget.addWebResourcesInHead(response, context);
CoolValidatingTextField.addWebResourcesInHead(response, context);
CoolBlockLayout.addWebResourcesInHead(response, context);
...
}
}
}
Here's the relavent code for CoolBlockLayout.wo:
import com.webobjects.foundation.*;
import com.webobjects.appserver.*;
import com.webobjects.eocontrol.*;
import com.webobjects.eoaccess.*;
import er.ajax.AjaxUtils;
public abstract class CoolBlockLayout extends WOComponent {
public CoolBlockLayout(WOContext context) {
super(context);
}
public static void addWebResourcesInHead(WOResponse
response, WOContext context) {
// Notice how we concentrate only on what
CoolBlockLayout needs to function.
// This is a nice abstraction so we never have to
worry about what is in the head
// because the next time we tweak this method to add
one more javascript resource,
// the head tags will pick it up so long as the
GenericComponent calls the overall
// method here.
AjaxUtils.addStylesheetResourceInHead(context,
response, "MyFramework", "myBlocks.css");
AjaxUtils.addScriptResourceInHead(context, response,
"Ajax", "prototype.js");
AjaxUtils.addScriptResourceInHead(context, response,
"MyFramework", "myJavascriptUtilities.js");
AjaxUtils.addScriptResourceInHead(context, response,
"MyFramework", "myBlocks.js");
AjaxUtils.addScriptResourceInHead(context, response,
"MyFramework", "mySameHeight.js");
}
}
Again, this example is workable but isn't perfect. At least it
allows us to think about one reusable component at a time. It also
makes sure that all the needed resources are in the head tag. You
have to do a small amount of book keeping in the GenericComponent (a
drawback). You also have to make your page level components extend
the GenericComponent (bigger drawback). Finally, all your resources
will be in the head tag of every page (not so bad since they get
cached).
The ideal solution is for "addWebResourcesInHead()" to not be static
and to be called automatically by the WO framework at the right time
so that it can put the proper values in the head. That way, no other
fiddling is necessary and "GenericComponent" is no longer needed. Do
I ask for too much? I think not.
"Seaside" does it right. Its "WAComponent" knows how to find the
"children" but does not know how to find the "parent" (the exact
opposite of WOComponent). You can override #updateRoot in a
WAComponent of your own to do the equivalent of what I just called
"the ideal solution".
This post by Ramon Leon is worth reading:
http://onsmalltalk.com/programming/smalltalk/pragmatic-css-and-javascript-in-seaside/
You know, I really don't mind Apple holding the source code tight to
WebObjects so long as they continue to innovate. But I fear if they
don't start innovating our beloved tool of choice will begin
evaporating. Presently we are in a configuration nightmare and it's
not so much fun to develop anymore. I really feel for Mike, Anjo and
others who continue to fight valiantly against all odds. The WOnder
team are the innovators right now. WOnder should be packaged
together with WebObjects... yea it should BE WebObjects. You should
be free to fix the problems with rapid-turn-around-mode parsing of
updated resources without having to sit and wait for Apple to
incorporate them. I can go on... But our champions can't do this so
long as WebObjects is closed source. Apple, if you love WO, set it
free.
Additionally, please forgive my wandering eye. Squeak did spring
from Apple after all.
-- Aaron
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden
--
Paul Hertz <email@hidden>
|(*,+,#,=)(#,=,*,+)(=,#,+,*)(+,*,=,#)|
<http://collaboratory.nunet.net/phertz>
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden