aspose file tools*
The moose likes JSF and the fly likes Cascading drop-down lists Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » JSF
Bookmark "Cascading drop-down lists" Watch "Cascading drop-down lists" New topic
Author

Cascading drop-down lists

Volodymyr Levytskyi
Ranch Hand

Joined: Mar 29, 2012
Posts: 505
    
    1

Hello Tim!

I need your help.
I have issue with two interdependent drop down lists. First select:

Second select:

If 'regionMenu'(second select) is selected and then I change value of 'countryMenu'(first select) then new 'regionMenu'(second select) is built due to other country
The value="#{user.user.region}"(second select) remains with value selected for previous 'regionMenu'(second select) which is not included in <f:selectItems> of new 'regionMenu'(second select) built due to new country selected. That is why I have:
registerForm:regionMenu: Validation Error: Value is not valid
When value of 'countryMenu'(first select) changes (listener="#{user.showRegions}" is called) I programmatically set value="#{user.user.region}"(second select) to 'Select region' which is for every possible 'regionMenu' drop down list. Unfortunately it doesn't work.
Do you have any sound ideas?

Thank you!


True person is moral, false is right!
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16250
    
  21

When you have a new question, start a new topic. It helps avoid confusion. I split this question off from the previous (unrelated) topic.

I have found a very effective technique to handling cascading lists in JSF. It requires a ValueChangeListener and a specialized "get" method for the dependent list(s).

The valuechangelistener does basically the following:


In other words, it destroys the SelectItem list that the dependent dropdown was using and clears the selection value. I am guaranteed that I need to do this, since only if the parent list (level1) value changes will this method fire.

For level 2, I build my SelectItem list "get method" to be sensitive to this condition:


This structure has several benefits. It builds a list if one exists, otherwise it retrieves it as a cached value. This is especially important in JSF, where the "get" method may be called multiple times in a request, and therefore we're reducing the overhead. Additionally, it ensures that the list is current (buildLevel2List builds its SelectItems based on the value in level1). All you have to do to force an update is to null out the list. And as a bonus, it simplifies setting the original Level2 list when the View is first requested, since the list isn't built until needed. Extra points come in because if you're building the list from a database, the persistent data resources aren't reliably available any earlier than postcontstruct time anyway.

This architecture can be repeated multiple times, taking it to as many levels as you like. I think my current record is 4. Do note that if you change a list, you MUST reset the selected value, since if it isn't in the new list, a ValidatorException will occur. Also, usually you need to cascade the list-nulling/value-clearing operation so that if you have a level1, level2, and level3, for example, changing level1 zaps both the level2 AND level2 lists and selections, Otherwise level3 will be out of sync with its level2 parent.
}


Customer surveys are for companies who didn't pay proper attention to begin with.
Volodymyr Levytskyi
Ranch Hand

Joined: Mar 29, 2012
Posts: 505
    
    1

Hello Tim!

Thanks to your advises I just solved this. I still don't understand how it can be?
Really at first my second dependent drop down list was built every time querying the database.
I can't make out but after I put check for null in method that fills second list with options

And set regionSelectItems to null in method that is called each time other country is selected via <f:ajax listener='' />

Everything began to work as needed. Then I removed regionSelectItems = null and check for null in first method and it stopped working,
it built only one item (one region) of regionSelectItems whenever I selected another country. I think it has to do something with <f:ajax>.
Thank you for your help, and one more question:
How to find out root of my deployed web app programmatically except method servletContext.getContextPath and method servletContext.getRealPath?
I need to create new file via file.createNewfile() and I need absolute path to my web app because path like '/myRoot/webapp' doesn't work.
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16250
    
  21

Tim Holloway,
Your post was moved to a new topic.
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16250
    
  21

My experience has been that I get the best reliability when I stick strictly to the exact way I coded my example.

In cases where a list of SelectItems is generated from a database, my "buildLevel2List" method in turn invokes a "loadLevel2List" method that calls the database backend service and returns a list of the items I want buildLevel2List to construct SelectItems from. That keeps each of the various methods that are user small and neat.

I'll have to bring up Rule #1 for JSF on your second example, however. Rule #1 says that any time you access anything in a javax.faces package that isn't a model (DataModel or SelectItem), there's a good chance you're doing it wrong.

In this particular case, getting the selection value from the component is going the long way around. Just access the backing bean property of the selected value.
Volodymyr Levytskyi
Ranch Hand

Joined: Mar 29, 2012
Posts: 505
    
    1

Hello Tim!

I query database to get appropriate array of SelectItem in getter of drop down list. After my Collection is retrieved I simply call static method of JsfUtil:
public static SelectItem[] getSelectItems(List<?> entities, boolean selectOne);
In fact I have a lot generated by netbeans instead of me. It generated from my entities appropriate facades for operations with EntityManager, managed beans
for every entity, this JsfUtil class, custom el resolver, great pagination technique for tables and even jsf pages with ability to perform CRUD operations.
It is significant help and it simplifies a lot to me! Stop, I never write my own entities because they are generated also by netbeans from database which I create
myself, although I should add some annotations like @Pattern or specify 'message' attribute which is displayed on page if constraint fails. I have this:

Just access the backing bean property of the selected value.

You are right because I have this value bound to entity property. Now this method is much better:

Thank you!
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16250
    
  21

Helpful hint:

I usually define my dropdown lists like this:

-- Select Country --
Belarus
Latvia
Lithuania
Poland
Ukraine
Russia

Then I define the list in View Template Language like so:

The special case of no selection thus becomes pre-pended to the dropdown list without me having to code a dummy entry into what comes back from the database.

Note that because HTTP doesn't do NULL values, the non-selected value will be an empty string, not NULL, so the corresponding logic is:

 
With a little knowledge, a cast iron skillet is non-stick and lasts a lifetime.
 
subject: Cascading drop-down lists