lundi 25 mai 2009

Handling assigments with af:selectManyShuttle + Master Detail Hierarchy

The af:selectManyShuttle component belong to the caste of the select components. Those components canbe populated by static or dynamic data, and the user can interact with them, like performing operations, filtering results, and so on. The select many schuttle implements a very common use case : handle assigments.

In my experience, every database schema has a case where a table holds two foreign key, each one referencing one table. For exemple, it could be aircraft and airport, the middle table will be the landings ; employees and projects, the middle table will be the workings. See, there's plenty ! When you create a view object, you can choose attributes and let the one you don't want behind :

So, the af:selectManySchuttle is the adf components that implements this.
But how do you populate it ? Forget ADF native stuff, bindings and wizards. Oh yes, you'll have to put in your hands in the dirt. Because today the only way to use af:selectManySchuttle is to use GOFJ.

1. Tables

For this test case, I’ve created a quite simple workspace. Let’s first take a quick look at the physical and model layers. Well there’s really nothing physical since I build the workspace with an offline database and static view object: so you can test it, modify, try stuff ...

See ? Easy ! An inconvenient is that we cannot create an entity layer. But you’ll see by the end of this paper that it’ll be of no use. Let’s go on with the model layer : with this I’ve created three view objects and one view link. This last one is just to be able to choose on whom people we’re going to assign knowledge.

Here. The model layer is build. Let’s go the view (since thanks to ADF, we don’t really care about controller). So as I wrote, there’s nothing drag’n’droppable for configuring an af:selectManySchuttle ; nothing in the bindings either. Maybe in the next release? In the mean time, your boss wants this.

2. Building and populating af:selectManySchuttle

I’ve build a simple UI : one panelSplitter and the table bound to the People data control, on the left. On the right, let’s click on the source tab to build our own af:selectManySchuttle. Before all this, you should open three files: the jsp, the backing bean and the page definition. When all this is done, we begin the stuff. Use this code in the second facet of panelSplitter to begin with:

Values attributes of both af:selectManySchuttle and f:selectItem are important here.
  • Attribute value of af:selectManySchuttle : the selected values (so the values on the right of the select many component in the UI)
  • Attribute value of f:selectItem : all possible values.
Each one is bound to a backing bean attribute via EL. The next step is to create these attribute :

The idea here is to write our own getter for mySelectItems. The code will iterate over the knowledge iterator from page definition to build the complete list of knowledge to return. In the meantime, the code will check if the current knowledge is acquired by the current person. If so, it will be added to the both lists. In fact, this one getter will build all we need. And the attribute initialSelectValue will help us match what the end-user has assigned, so we can delete or create new rows in the experience table.

public SelectItem[] getMySelectItems() {
// That way this method will be called only once
if (mySelectItems == null) {
// Getting iterators from bindings
DCIteratorBinding people = (DCIteratorBinding)this.getBindings().get("People1Iterator");
DCIteratorBinding experience = (DCIteratorBinding)this.getBindings().get("Experience1Iterator");
DCIteratorBinding knowledge = (DCIteratorBinding)this.getBindings().get("Knowledge1Iterator");

// Creating the SelectItem array to return and the value attribute of the schuttle itselft
mySelectItems = new SelectItem[knowledge.getAllRowsInRange().length];
myManySchuttleValue = new ArrayList();

// Browsing knowledge
for (int i = 0 ; i < item =" new" j =" 0" initialselectedvalue="myManySchuttleValue;">

Run the application, you'll see that it works, but only for the first people in the iterator. Next step is synchronizing the table and the schuttle.

3. Master table and detail schuttle

Now of course you want to display in the schuttle information related to the selected user in the table. For that, we'll need a selection listener on the table. The method called will change the current row of the iterator in the page definition to point it to the selected row in the table. And since this is PPR, you'll need to enable it on the target component, the af:selectManySchuttle via its partialTriggers attribute.

In the table, add this :

Wich points to this method in the backing bean :

public void peopleSelectionListener(SelectionEvent se) {
RichTable rt = (RichTable)se.getSource();
FacesCtrlHierNodeBinding node = (FacesCtrlHierNodeBinding)rt.getSelectedRowData();

DCIteratorBinding people = (DCIteratorBinding)this.getBindings().get("People1Iterator");

mySelectItems = null;

That's it. All you need to add is the partialTriggers attribute in the af:selectManySchuttle component. See that's an important point. Try not to do it : you'll get an error message. One thing is left :

4. Saving changes

Since you saved the initial selected values, you just have to read the the actual selected values ; and process code :
  • When a selected value was in the initial list and is not anymore : delete ;
  • When a selected value is not in the initial list : add ;
  • When a selected value was in the initial list : do nothing.
And that is it.

[download coming soon]

3 commentaires:

Anonyme a dit…

Thanks, this is exactly what I'm trying to do. Any ETA on the download of the sample application ;) ?

Anonyme a dit…

thank for share, it is very important . ̄︿ ̄

Anonyme a dit…

Check this out:
It contains the implementation steps and a sample application.