Another bit of Griffin's wisdom...
How would one template a site so it's look can easly be changed by non-programmers?
There are two possibilities offered by WO that probably address similar concerns. The first is the basic ability to create a WOComponent as a subcomponent of other WOComponents. The second is the ability to extend the subcomponent concept to a page wrapper in which the wrapper contains a WOComponentContent element. But let's take these one at a time.
SUBCOMPONENTS
There are two general requirements for using a WOComponent as a subcomponent. The first is that it generally must be made into a partial page. This is easy to do in WOBuilder by selecting Document in the element browser strip for the page, bringing up the Info panel and selecting "Partial document" from the pop-up. This strips off the HTML header stuff so the component will generate only the HTML needed by another component.
The second requirement is that it must have the bindings needed to be bound to the parent component's attributes. In order to meet this requirement, one has to have the iVars or accessor methods in the subcomponent class to accept values sent down, and to provide values returned. Then, choose the Window->API menu item in WOBuilder (or press Cmd-3 on the keyboard) to bring up the API panel. In this panel you can indicate the keys to be bound from your subcomponent class, indicate which bindings are required, which are optional and provide some validation criteria.
When you're finished, if you've done your job well, there's almost no difference to the follow-on programmer whether he/she's using a standard WO dynamic element (like a WOTextField) or using one of your subcomponents. Each is named and bound into the page in almost identical ways. And, before you ask, yes, subcomponents can have their own subcomponents to any level you desire.
PAGE WRAPPERS
If you have a fairly constant set of headers and/or footers with, perhaps, a left or right nav bar for all or most of the pages, you can create a PageWrapper component containing all these elements and use it as a subcomponent of all your other components. The interesting thing about page wrappers is that though they are subcomponents, they tend to invert the logic a bit in that they contain the HTML header content and your standard page operates very much like a subcomponent to them, though in fact the opposite is true. In order to make them work, four things are required:
* The page wrapper must be a complete page rather than a partial page, even though it's a subcomponent
* The page wrapper must contain a WOComponentContent dynamic element to hold the content of the page
* The actual pages must each be partial pages, even thought they're the parent component, since the wrapper will provide the HTML header information
* The actual pages must generally start with a PageWrapper tag and end with its corresponding end tag wrapping the content of the page.
Basically, you lay out the page wrapper as you would have it appear for all pages, then insert a WOComponentContent element within the page wrapper where you want the page's content to appear within the wrapper.
Your application selects your page (not the wrapper, but the actual page with content) as it would normally, but your page contains a WebObjects tag for the PageWrapper subcomponent at the start, followed by its own content and ends with the PageWrapper end tag.
When WO renders the page you selected, it finds the PageWrapper (subcomponent) tag in your page and sends the appendToResponse to the page wrapper to render the headers, footers, etc. While rendering the page wrapper, it parses the WOComponentContent tag and from within the page wrapper, sends an appendToResponse to the page to render the content inside the page wrapper tags. When it parses the PageWrapper end tag, it goes back to the page wrapper to finish rendering it, then returns to your actual page which returns to the Session's appendToResponse.
Overall, it's more difficult to describe than it is to do, once you've gotten the hang of it.
There's a caveat to subcomponents that you may see discussed on the lists every once in a while. That is, by default, they're pretty safe but not very efficient. As they are initially set up, during the appendToResponse that creates the page, the parent component's values are synchronized with the child component first, then the child is sent the appendToResponse, then the child component's values are synchronized with the parent. The same thing happens on takeValuesFromRequest and on invokeAction. So that for any given Request-Response loop, the values between a parent and a child component are synchronized 6 times.
Furthermore, if several pages are using the same subcomponent (as is typical, for instance, with a page wrapper), then a new instance of that subcomponent is created and stashed in the page queue for each page that uses it.
One can increase efficiency appreciably by controlling the synchronization explicitly. For most components, there is a critical point at which the values must be synchronized or, at least, there is a critical point for each value at which it must be synchronized. Now is not the time to go into the details, but by overriding the method
public boolean synchronizesVariablesWithBindings()
and returning a false value, WO will not synchronize the subcomponent's values at all. Then by using the methods valueForBinding() and setValueForBinding() the synchronization can be handled manually. This resolves the problem of inefficiency in synchronizing the values, but doesn't address the extra instances of the subcomponent on the page cache.
In order to deal with this second problem (if, for your application, it does indeed become a problem), you can attempt to redesign the logic of the component so that it needs no state memory from one invocation to another. If you're successful at doing that, then you can override the method
public boolean isStateless()
and return true (the method returns false by default). If you do this, then WO presumes that the component need only be instantiated once and that instance can be reused by as many components as need it without an additional instantiation. Making a component stateless, doesn't necessarily make it thread safe. However, if multiple threads need the component, WO will automatically instantiate as many instances of the subcomponent as are necessary for the number of threads needing it simultaneously. It's a bit deeper than that, but you've been given way more than you need to know for now.
We've worked with graphic designers who quickly became used to WO. Remember, WO adds only ONE TAG to standard HTML. The tag is differentiated in its actions by the WOActiveElement or subcomponent to which the tag is bound in the .wod file.
If the designers have trouble with that (and a few have), we've typically asked them to lay the page out first. If there are no repetitions nor conditional segments of HTML, this is fairly straightforward. We then take the results of the graphic artists design and plug in the WO tags appropriately.
With conditional elements or repetitions, it's slightly harder, but in general, we typically ask them to design the page as if three repetitions occurred and redesign it as if no repetitions occurred, or design it with and without the conditional HTML. Then we identify what they want in each case and substitute the WOConditionals and WORepetitions for their conditional and repeated HTML and we've got the page.
Unless there's a separate requirement for JavaScript somewhere, there's no code in the WO HTML file at all.
Comments