• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: WOComponent children
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: WOComponent children


  • Subject: Re: WOComponent children
  • From: email@hidden
  • Date: Tue, 15 Jan 2008 12:23:10 -0500


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.

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.

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

  • Follow-Ups:
    • Re: WOComponent children
      • From: Jean-François Veillette <email@hidden>
    • Re: WOComponent children
      • From: Mike Schrag <email@hidden>
    • Re: WOComponent children
      • From: Paul Hertz <email@hidden>
  • Prev by Date: Re: Cheat: Run EOModeler on Leopard
  • Next by Date: Re: WOComponent children
  • Previous by thread: Re: WOComponent children
  • Next by thread: Re: WOComponent children
  • Index(es):
    • Date
    • Thread