Open eclipse-faces-bot opened 13 years ago
@glassfishrobot Commented Reported by @edburns
@glassfishrobot Commented lamine_ba said: One of the solutions to the "Cascading Dropdown problem " is to make the UISelect components aware of each other by adding a parent-child relations. If a selection occurs in the parent (change event), the child is update automatically. The JSF framework will send an Ajax call to invoke a method in the managed bean like for example : load_childs(parent_id ). By parent_id, I mean the value selected in the parent combo, the id of a country for example to invoke a method like load_cities(country_id ) for example. The selected value is the only thing a developer needs in order to run successfully his data access logic. A developer must provide in the UI the method to be called for the partial update. The JSF framework will manage transparently the interaction (display,update,remove,disable) with a client side approach. no UI binding in your managed beans thus making your application scalable. one selection and multiple selection must be managed.
@glassfishrobot Commented lamine_ba said: JB> Agreed. This seems to be the way that other frameworks (and the manual work-around I’ve implemented) are solving it and it seems to work quite well
MLB> Yes it is my opinion, the idea has already been tested and it works quite well. So, let's implement it..
@glassfishrobot Commented i_oss said: Sorry I don't quite understand the problem, isn't the setter of <h:selectXXX value="#
{my.selectionSetter}
"/> the method you are looking for? if you change the items of dropdown #2 in the value-setter of #1, it should just work the way described above.
So I am not sure, which lifecylce-phase is missing here? Maybe if someone could point out at which time in the lifecycle "page_init" and "page_load" would take place?
@glassfishrobot Commented lamine_ba said: we are talking about doing something like this : http://www.coderanch.com/t/510988/JSF/java/ajax-update-selectOneMenu.
"page_init" and "page_load" functions belong to ASP.NET lifecycle.
our problem is to update dynamically a dropdown based on a selection made on another dropdown (change event). and the solution should be something like this.
<h:selectOneMenu value="#{bean.country}" id="countries">
<f:selectItems value="#{bean.countries}" />
<f:ajax event="change" render="cities" />
</h:selectOneMenu>
<h:selectOneMenu value="#{bean.city}" id="cities">
<f:selectItems value="#{bean.cities}" />
</h:selectOneMenu>
@ManagedBean
public class Bean {
private Long country;
private Long city;
............................................
}
@glassfishrobot Commented i_oss said: so it just should be:
(apart from Long probably not being very userfriendly in the rendered page )
public void setCountry(Long country) {
// put checks for changes here, if it is a long running operation...
this.cities = whateverToGetTheCitiesFor(country);
this.country = country;
}
Or am I missing something here?
Still I'd like to know where page_init and page_load would be added to the lifecycle?
@glassfishrobot Commented lamine_ba said: i_oss> (apart from Long probably not being very userfriendly in the rendered page )
You can choose whatever you want. I often use the Long type for my id and my primary key.
public class Country {
@Id
private Long id; // choose whatever you want, Long,Integer,String,....
private String name;
}
i_oss>
public void setCountry(Long country) {
// put checks for changes here, if it is a long running operation...
this.cities = whateverToGetTheCitiesFor(country);
this.country = country;
}
And you are saving three states which can be a good thing when you have the need to cache your data in order to prevent multiple time access
@ManagedBean
public class Bean {
private Long country; //1
private Long city;//2
private List<SelectItem> cities; //3
............................................
}
and you have created two methods
public List<SelectItem> whateverToGetTheCitiesFor(Long id country) {
}
public List<SelectItem> getCities() {
return cities;
}
which could be just one method
public List<SelectItem> getCities() {
// call your DAO here }
Your solution is not my solution and that is the reason why I have just created a skeleton. The only thing I need myself is to be able to know the id of the country and the id of the city that have been selected. ( value="#
{bean.country}
", value="#
{bean.city}
").
i_oss> Or am I missing something here?
Yes, you are really missing something here. We are talking about ASP.net, not about JSF
JB>ASP.NET solves this (and other problems) by JB> defining several "page lifecycle functions" akin to JSF but let's JB> you easily override functions in the backing classes (i.e.: JB> "page_init", "page_load" etc) to programmatically control where to JB> populate each dropdown. Remember - You can't paint the values in JB> dropdown #2 until you can retrieve the selected index from dropdown JB> #1 - but you can't retrieve the selected index until you first fill JB> the options so the framework can then bind the selected index.
This issue is not really an issue because you can create a working solution with JSF in 5 minutes. We were just unaware that we could realize it without using a programmatic approach. Its only merit is that it helps us find some ideas in how to ease the job and as soon as we have finished to describe them, it will be closed.
@glassfishrobot Commented lamine_ba said:
public class Country {
private Long id;
private String name;
}
public class City {
private Long id;
private String name;
private Country country;
}
public interface Dao {
public List<Country> getCountries();
public List<City> getCities(Long country_id);
}
@ManagedBean
public class Bean {
private Long country_id=new Long(0); // select the first option in the combo private Long city_id=new Long(0); // select the first option in the combo private @Inject Dao dao;
public List<SelectItem> getCountries() {
List<SelectItem> items=new ArrayList<SelectItem>();
for(Country country:dao.getCountries())
items.add(new SelectItem(country.getId(),country.getName()));
return items;
}
public List<SelectItem> getCities() {
List<SelectItem> items=new ArrayList<SelectItem>();
for(City city: dao.getCities(country_id))
items.add(new SelectItem(city.getId(),city.getName()));
return items;
}
-----------------------------------------------------------------
}
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<h:outputScript name="jsf.js" library="javax.faces"/>
</h:head>
<body>
<h:form>
<h:selectOneMenu value="#{bean.country_id}" id="countries">
<f:selectItem itemLabel="-- Select a Country -- " itemValue="0"/>
<f:selectItems value="#{bean.countries}" />
<f:ajax event="change" render="cities" />
</h:selectOneMenu>
<h:selectOneMenu value="#{bean.city_id}" id="cities">
<f:selectItem itemLabel="-- Select a City -- " itemValue="0"/>
<f:selectItems value="#{bean.cities}" />
</h:selectOneMenu>
</h:form>
</body>
</html>
And That's all. we have created a cascading dropdown without using a programmatic approach like this ugly one. (http://www.juurlink.org/2011/02/dynamic-dependent-list-boxes/). We select a country in the first combo and automatically an ajax request is fired behind the scenes to update the other combo. This feature is really a powerful one until the idea to add a commandButton in your form to make a postback and save things, comes to you.
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<h:outputScript name="jsf.js" library="javax.faces"/>
</h:head>
<body>
<h:form>
<h:selectOneMenu value="#{bean.country_id}" id="countries">
<f:selectItem itemLabel="-- Select a Country -- " itemValue="0"/>
<f:selectItems value="#{bean.countries}" />
<f:ajax event="change" render="cities" />
</h:selectOneMenu>
<h:selectOneMenu value="#{bean.city_id}" id="cities">
<f:selectItem itemLabel="-- Select a City -- " itemValue="0"/>
<f:selectItems value="#{bean.cities}" />
</h:selectOneMenu>
<h:commandButton action="#{bean.save}" value="Save">
</h:form>
</body>
</html>
And boum! you get a validation error. Sadly, the combo displaying your cities is saying that the value you have picked in its list is not a valid one because this same value is not anymore in its list". Isn't it a craziness statement?. After some long and hard hours debugging the jsf.js file and looking at the tree printed in the console by my PhaseListener, I came accross no rationale idea. Everything was fine. The partial view processing and rendering were done perfectly and the state of the combo was updated and saved. And suddenly when I was about to loose hope, comes this ironical idea : "Hey put the managed bean in the session scope".
@ManagedBean
@SessionScoped
public class Bean {
public String save() {
----------------------
}
}
and definitely that was the solution.......
@glassfishrobot Commented lamine_ba said:
creating SelectItem objects is a recurrent and tedious task for a developer. We must automate it. The developer will save time and we will gain in performance because we have a double iteration, one for creating a list of SelectItem objects and another iteration for creating from this same list another list of UISelectItem components. With this annotation, we can cut the first.
@SelectionModel(value="id",label="name")
on a method returning a list or a Collection of Country means virtually
new SelectItem(country.getId(),country.getName())
for each country.
@ManagedBean
@SessionScoped
public class Bean {
@SelectionModel(value="id",label="name")
public List<Country> getCountries() {
return dao.getCountries();
}
@SelectionModel(value="id",label="name")
public List<City> getCities() {
return dao.getCities(country_id))
}
-----------------------------------------------------------------
}
If we bring a convention on how to get the value and the label on objects, we can make this annotation optional which will be again for us a gain of time and clarity.
@ManagedBean
@SessionScoped
public class Bean {
public List<Country> getCountries() {
return dao.getCountries();
}
public List<City> getCities() {
return dao.getCities(country_id))
}
-----------------------------------------------------------------
}
@glassfishrobot Commented @edburns said: In progress prototype
@glassfishrobot Commented
i_oss said:
The @SelectionModel is a nice idea.
At the moment you can avoid using SelectItems already by:
1) A Converter, and a sensible toString method on your beans (so this is the current "convention" for not using SelectItems and/or Annotations)
Drawbacks: This can't be tuned on a per
In case we create @SelectionModel, I think value and label should be EL with a implicit varname for the items in the list for example: @SelectionModel(value="#
{item.id}
", label="#
{item.firstname}
#
{item.lastname}
")
@glassfishrobot Commented lamine_ba said: Hi Imre. Yes The @SelectionModel is a nice idea to avoid using SelectItems. I had this idea because I was unaware that this annotation already exists in JSF 2.0 through another representation more cleaner, more simpler and more transparent.
2) Use itemValue and itemLabel on the
public List<Country> getCountries() {
return dao.getCountries();
}
<f:selectItems value="#{bean.countries}"
var="country" itemValue="#{country.id}" itemLabel="#{country.name}" />
I prefer now this way which makes my annotation obsolete and there is no way to get an annotation through the Expression Language. But It would be wonderful to have such feature in the EL api.
2) Use itemValue and itemLabel on the
Yes I agree and that is why we want to bring a convention in JSF 2.2. In the real life, every man has an ID (fingerprint, DNA) and a name. In the software life, most of the objects we display are entities. So if we don't provide the information on how to resolve the ItemValue or the ItemLabel, their values will be resolved by invoking getId() and getName() on our objects.
JSF 2.2
public List<Country> getCountries() {
return dao.getCountries();
}
<f:selectItems value="#{bean.countries}"/>
means virtually
<f:selectItems value="#{bean.countries}" var="country"
itemValue="#{country.id}" itemLabel="#{country.name}" />
The var attribute like the ItemValue and ItemLabel is now optional and by default its value is equal to 'it'.
<f:selectItems value="#{bean.countries}" itemValue="#{it.id}" itemLabel="#{it.name}" />
so adhere to our JSF 2.2 convention and you will save time.
@glassfishrobot Commented @edburns said: The most recent patch, a collaboration from Lamine and I, but mostly Lamine, shows one way to do this.
@glassfishrobot Commented i_oss said: Hi Lamine, only a short note
<f:selectItems value="#{bean.countries}"/>
at the moment is equivalent to
<f:selectItems var="country" itemValue="#{country}" itemLabel="#{country.toString()}"/>
With the #
{country}
being coerced to/from String with a converter (if needed and existing). So I don't think we want to change the convention that already exists, possibly breaking existing apps.
@glassfishrobot Commented lamine_ba said: Hi Imre, I'm really sorry, I haven't thought about that.
There is already a behavior for
<f:selectItems value="#{bean.countries}"/>
If we change that, the existing apps will break and that is not really a good thing.
So I will just keep this part of my writing of course if you don't see any problem with it
The var attribute is now optional and by default its value is equal to 'it'.
<f:selectItems value="#{bean.countries}" itemValue="#{it.id}" itemLabel="#{it.name}" />
@glassfishrobot Commented jakobkorherr said:
The var attribute is now optional and by default its value is equal to 'it'.
Why are we using the term "it" here exactly? Should it mean "item"? In that case I would propose naming it "item" instead of "it", because "item" is a LOT more significant than "it" (and it's just two more letters...).
@glassfishrobot Commented lamine_ba said: Hi Jakob. Why are we using the term "it"? That is a very good question. It is just something I have borrowed to Groovy.
Inside each closure, Groovy defines a default variable, it, for one argument passed to the closure. This makes it very easy to have a single parameter for your closures without having to explicitly declare it. The it variable works just like Perl’s $_ variable in subroutines:
namePrinter = { println "Hello, $
{it}
!" }; namePrinter("John"); // prints "Hello, John!"
If you prefer, we can name it "item". It is also a very nice proposal. I'll be happy with whatever name you'll choose as long as the var attribute is optional. That is the only thing I want.
@glassfishrobot Commented jakobkorherr said: Hi Lamine,
Thanks a lot for the clarification. I did not know that!
Nevertheless, I prefer "item" over "it". It really is a lot more significant in this case, because most attributes of
So it would be very great, if we could change that!
@glassfishrobot Commented lamine_ba said: You are right Jakob and I vote for "item". Don't worry, nothing has been done yet. It was just an idea we were testing.
@glassfishrobot Commented @edburns said: I don't think we made the var attribute optional after all.
@glassfishrobot Commented @edburns said: Set priority to baseline ahead of JSF 2.3 triage. Priorities will be assigned accurately after this exercise.
@glassfishrobot Commented @manfredriem said: Setting priority to Minor
@glassfishrobot Commented File: 20110510-i_spec_987.patch Attached By: @edburns
@glassfishrobot Commented This issue was imported from java.net JIRA JAVASERVERFACES_SPEC_PUBLIC-987
JB> 1) the classic "cascading drop down problem" - this is a scenario JB> where the first dropdown is dynamically populated with values and JB> the selection available in the second dropdown is dependent on the JB> value selected in the first dropdown (e.g.: select a country then JB> the list of cities are dynamically populated). Important to note JB> here - if dropdown #1 has static values this problem obviously JB> trivial, the challenge arises from a dynamically defined list being JB> dependent on the selected value from a parent list the is also JB> dynamically defined. ASP.NET solves this (and other problems) by JB> defining several "page lifecycle functions" akin to JSF but let's JB> you easily override functions in the backing classes (i.e.: JB> "page_init", "page_load" etc) to programmatically control where to JB> populate each dropdown. Remember - You can't paint the values in JB> dropdown #2 until you can retrieve the selected index from dropdown JB> #1 - but you can't retrieve the selected index until you first fill JB> the options so the framework can then bind the selected index.
Affected Versions
[1.1, 1.2, 2.0, 2.1]