Programatically creating a WODisplayGroup

I like WODisplayGroups. OK, call me crazy – or lazy – but I like them alot. However, I do find that I often need more control over them than the drag and drop creation gives me. So here is how I like to set up WODisplayGroups programatcially:

Just to prevent any potential confusion: This example demonstrates creating a WODisplayGroup from scratch, and will not work if you’ve created one by dragging an Entity from EOModeler into WebObjects Builder. – Thanks Alex!

Using this pattern setup the _displayGroup object

protected WODisplayGroup _displayGroup;
 
public WODisplayGroup displayGroup() {
	if (_displayGroup == null) {
		prepDisplayGroup();
		find();
	}
	return _displayGroup;
}
public void setDisplayGroup(WODisplayGroup dg) {
	_displayGroup = dg;
}

Add the prepDisplayGroup() method:

public void prepDisplayGroup() {
	_displayGroup = new WODisplayGroup();
	_displayGroup.setNumberOfObjectsPerBatch(10); 
	EOSortOrdering asc = EOSortOrdering.sortOrderingWithKey(
			"keyToSortOn", 
			EOSortOrdering.CompareAscending);
	NSArray ordering = new NSArray(new Object[] {asc});
	_displayGroup.setSortOrderings(ordering);
}

The WODisplayGroup API is here

Finally add the find() method:

public void find() {
	NSArray foundObjects = //fetch your objects here
	displayGroup().setObjectArray(foundObjects);
}

In my find() method I usually take advantage of the objectsFor... methods that can be generated by EOGenerator. These wrap your EOModel’s fetchSpecs in static methods in your EO’s, very cool. Take a look at the EOGenerator EOGJavaSource.eotemplate for more details. Update: I’ve blogged more details about my EOGenerator templates here

I often base my search pages on a common super class that includes a displayGroup along with variables for setting objects per batch and a search string amongst other things.

Lazy WOComponent ivar initialization

How do you setup instance variables if they need to be initialized after their WOComponent is fully formed?

Overiding the awake() method happens too early, so you’re usually left overriding later in the request-response loop *. Here is a nice simple alternative pattern that I use all the time.

  1. Create your instance variables (I like to make them protected at least)
    protected NSArray _myThings;
  2. Give your ivars public accessor methods
    public NSArray myThings() {
    	return _myThings;
    }
     
    public void setMyThings(NSArray array) {
    	_myThings = array;
    }
  3. Modify the getter method so it follows this pattern:
    public NSArray myThings() {
    	if (_myThings == null) {
    		//initialize _myThings here
    	}
    	return _myThings;
    }

This pattern works really well for setting up things like display groups or arrays of fetched objects. If you bind to the accessor method in your WOComponent, the first time the method is hit, the values will be initialized. Very clean and easily understood. I like it a lot 🙂

* You do know the stages of the request-response loop don’t you? If you don’t buy Chuck’s book. Do it now. I’ll wait…

Using WebObjects DirectActions – pt. 2

At the conclusion of part one of this mini-series we had successfully configured a WebObjects application to accept DirectActions, had written a simple DirectAction method, and created a WOHyperlink to trigger it. Now it’s time to look at some of the common additional requirements you might encounter. As I mentioned last time they are:

  1. Passing parameters to our action
  2. Accessing a database with our action
  3. Invoking or using a Session with our action

Each of these will take a little work to get through, so I’m going to tackle them in several separate posts. Today we will look at passing parameters to our actions. We are going to extend the examples we used last time so fire up your IDE (Eclipse I hope) and lets get started.

Passing parameters to a DirectAction

Being able to trigger an action based on the URL is fun and all, but often we will want to do more. More? what could we possibly want that is more? Well submitting a form to a direct action URL might be nice, or passing a value to the next page for display or to modify its behaviour would be cool too.

To get this to work we have a number of issues to cover:

  1. Handling parameters in our action
  2. Passing a single parameter to our action
  3. Passing multiple parameters to our action

1. Handling parameters in our action

The first thing we are going to tackle is to get our DirectAction to handle parameters attached to our URL. Our DirectAction class has access to a WORequest object through the request() method. The WORequest contains a bunch of information about the current request, including any form values – which is how we will be passing values into our DA’s. These can be accessed through the following methods:

  • formValues()
  • formValuesForKey(String aKey)
  • formValueForKey(String aKey)

I suggest you take a look at the documentation for WORequest for more information but here is a brief summary of what they do:

  • formValues – returns an NSDictionary with all of the form values. The names are represented as the NSDictionary keys each with an NSArray containing its value(s).
  • formValuesForKey – returns an NSArray of the form value(s) that match a given name (key).
  • formValueForKey – returns an Object that is one of the value(s) that matches a given name (key).

Note: formValues() and formValuesForKey(String aKey) will return NSArray’s of values. This is because multiple form values can be associated with any given name (key). The NSArrays returned are sorted in no particular order – be warned.

Similarly, the Object returned by formValueForKey(String aKey) will be arbitrarily chosen from the NSArray of returned values – only use this method if you can be assured that there is only one value for each key (or don’t care which value you receive).

Lets extend the helloWorld example from last time to see how this works. Open your project and modify the helloWorld method in the DirectAction.class so it looks like this:

public WOActionResults helloWorldAction() {
	// Using formValues()
	NSDictionary dict = this.request().formValues();
	System.out.println("Form Values for request: " + dict);
	WOComponent page = pageWithName("Main");
	page.takeValueForKey("Hello World", "myDisplayString");
	return page;
}

Granted, this is not awe inspiring code, but it will help us to explore the next section.

2. Passing a single parameter to our action

Build the application and run it. If you are working on the project we built last time your web browser should open a new page with a single link. Click on the link to get to the Hello World page. You should see something like this in your console:

Form Values for request: {}

Which is understandable, as we haven’t supplied any values. Let’s change that. Append the following to the current URL "?=thisIsATest" so it looks like this:

http://STD_WEBO_URL/Test.woa/wa/helloWorld?=thisIsATest

The STD_WEBO_URL will look something like: "localhost:55555/cgi-bin/WebObjects/" it’s abbreviated for brevity… and to make it shorter too.

Hit return to submit the modified URL and you should see this in the console:

Form Values for request: { = ("thisIsATest"); }

Because we did not append a key-value pair to the URL, our resulting dictionary is missing the key. This is fine if we don’t need to uniquely identify our values. If we are just passing one value, we can retrieve it using formValueForKey("").

3. Passing multiple parameters to our action

Having one value is rare, in my experience, so lets look at identifying our value with a key. Appending a key to the URL is easy, the resulting URL will look like this:

http://STD_WEBO_URL/Test.woa/wa/helloWorld?aKey=thisIsATest

Submitting this URL to our application should give us this in the console:

Form Values for request: {aKey = ("thisIsATest"); }

We can explicitly retrieve this value using formValueForKey("aKey").

Finally, lets look at sending multiple key-value pairs. Simply append additional pairs (key1=value1) separated by an & symbol:

http://STD_WEBO_URL/Test.woa/wa/helloWorld?key1=value1&key2=value2

This URL should give us the following in the console:

Form Values for request: {key1 = ("value1"); key2 = ("value2"); }

Again, using formValueForKey("keyN") will allow us to get each of these values from the request.

You should now have a pretty good idea of how a simple DirectAction works, and how values are encoded into the URL. However, it would be nice if we could get the URLs without having to enter them by hand. So, next time we’ll look at generating the DirectAction URLs by using the bindings on WebObjects WODynamicElements.

Using WebObjects DirectActions pt. 1

Someone asked where they could go to find a tutorial on using WebObjects DirectActions , I went looking and, horrors of horrors, found the Internet wanting! Really, I should write a letter. Sigh… why do I have to do everything around here?

There are two ways of invoking actions in a WebObjects application. Component Actions and Direct Actions (Actions are methods triggered by a request – ie: clicking a link triggers an action to return the next page, or clicking a button triggers an action to log a user in with form values). Component actions are used by default and are covered extensively by the Apple tutorials (binding to the action binding of a WO Dynamic Element – WOActiveImage, WOHyperlink, WOSubmitButton, etc – generates a component action). The resulting URLs are dynamic (based on the context id, session id, and component id) and cannot be bookmarked.

Component actions are well suited for many types of applications (wizards, order checkout, admin utilities, etc). But because they have a dynamic URL they may not be suitable if you need to allow visitors to bookmark their pages (a catalog or reference site for instance). As well, each visitor will receive a relatively heavyweight Session object, so they may not be appropriate if you expect a lot of visitors and/or you do not need to track their state. Direct Action are designed for these situtations.

Direct Actions (DAs) are a WebObjects mechanism for performing actions with a static URL. Direct Actions don’t automatically invoke a Session (if you don’t need one) so they can be lighter weight. However they require a little more effort to get to work, and because they are not inherently stateful, you need to think a little more to ensure that state is captured.

We’re going to look at a simple example of the steps needed to setup and trigger a DirectAction.

There are three steps to setting up and using a DirectAction:

  1. Configure the Application Request Handler
  2. Create the action in the DirectAction class
  3. Create a link or redirect to access the new directAction

1. Configuring the Application Request Handler

By default a new WebObjects application will have a component action request handler (identified by the /wo/ portion of the URL). This is a sample component action URL:

http://localhost/cgi-bin/WebObjects/
            this.woa/wo/E5gVcEa7GA0bT714xOYsUg/0.0.3.1

As an aside: The string E5gVcEa7GA0bT714xOYsUg is the Session ID and identifies the current user session, you can remove it from the URL by storing it in a browser cookie instead if you wish (warning: this will break your app if the browser is configured refuses cookies – so check first):

// In your Session constructor
setStoresIDsInCookies(true);
setStoresIDsInURLs(false);

To use DirectActions we need to change the Application’s defaultRequestHandler to the directActionRequestHandler. This code in your Application constructor will do the job:

// In your Application constructor
String directActionRequestHandlerKey = 
	this.directActionRequestHandlerKey();
WORequestHandler directActionRequestHandler = 
	this.requestHandlerForKey(directActionRequestHandlerKey);
this.setDefaultRequestHandler(directActionRequestHandler);

2. Create the action in the DirectAction class

Your custom code can go in either your own java class, or in the default DirectAction.java created with each project. To keep things simple we will extend the DirectAction.java class.

If you take a look at the DirectAction.java class of a new project, you will see that it has one action: defaultAction that looks like this:

public WOActionResults defaultAction() {
	return pageWithName("Main");
}

This action is pretty straight forward. It follows the naming convention for DirectActions (namely: actionNameAction) and it returns the WOComponent with the name "Main". An application that has a directActionResponseHandler as it’s defaultResponseHandler will process any request that doesn’t specify an action (and/or class) by the defaultAction in the DirectAction class. You can see this for yourself by changing the defaultAction to this:

public WOActionResults defaultAction() {
	System.out.println("Using the default action");
	return pageWithName("Main");
}

If you build and run your application and you should see Using the default action in the console when the Main page loads.

That’s all very exciting, but what we really want to do is create our own custom action that gets triggered by a specific URL. So lets create our own action. In the DirectAction.java class add the following:

public WOActionResults helloWorldAction() {
	WOComponent page = pageWithName("Main");
	page.takeValueForKey("Hello World", "myDisplayString");
	return page;
}

To make this work we need to modify our Main WOComponent. Add a String called myDisplayString to Main.java (make sure to give it accessor methods) and in the Main.wo add a WOString component and bind its value to myDisplayString.

Build and run the application. It should open and empty browser window. Use this URL to trigger your new helloWorldAction:

http://localhost:55555/cgi-bin/WebObjects/Test.woa/wa/helloWorld

Replace the 55555 in the URL above with the actual port for your application instance – also notice that we’ve replaced the /wo/ with /wa/ indicating that this is a DirectAction URL. When the page reloads you should see this:

Result of HelloWorld DirectAction example

So, by using DirectActions we can easily call arbitrary methods by specifying them in our URL.

3. Create a link or redirect to access the new directAction

To access our DirectActions we could hard code the URL as an href in a link, or ask our users to manually enter them – like we just did, but neither of those are really practical. Thankfully many of the WO Dynamic Elements – such as the WOHyperlink we are about to use – have built in support for DirectActions.

Add a WOHyperLink to your Main WOComponent and set its bindings so they look like this:

HelloWorld Direct Action Exampl WOHyperLink bindings

Build and run your application, when the Main page loads, it should have your new hyperlink. Clicking on it should reload the page with the helloWorld URL.

This little tutorial gives a basic overview of how DirectActions are used. But there is a lot more that we can do. Some of the things we will still need to tackle are:

  • Passing parameters to our action
  • Accessing a database with our action
  • Invoking or using a Session with our action

I’ll try to get to some of that next time.