roopashree r
roopashree r

Reputation: 33

How to sort f:selectItems in each p:selectOneMenu of my application?

Eg:

<p:selectOneMenu value="#{UserBean.country}" id="countryId">
    <f:selectItem itemLabel="Japan" itemValue="Japan"/>
    <f:selectItem itemLabel="Russia" itemValue="Russia"/>
    <f:selectItem itemLabel="India" itemValue="India"/>
    <p:ajax listener="#{UserBean.onChangeCountry}" process="@this"/>
</p:selectOneMenu>

Like above I have many other selectOneMenu in other jsf pages which is not in sorted form, I want a solution where in surrounding the selectOneMenu tag with the custom tag will display the contents in sorting order (or can suggest any other way also we can achieve this)

Upvotes: 3

Views: 1613

Answers (2)

Jasper de Vries
Jasper de Vries

Reputation: 20198

You could create a custom renderer for the p:selectOneMenu component. Create a new class, say my.custom.MySelectOneMenuRenderer, and extend SelectOneMenuRenderer. In this you want to @Override the encodeInput method to something like:

public class MySelectOneMenuRenderer extends SelectOneMenuRenderer {

  @Override
  protected void encodeInput(FacesContext context, SelectOneMenu menu, String clientId, List<SelectItem> selectItems, Object values, Object submittedValues, Converter converter) throws IOException {
    // Sort the items
    Collections.sort(selectItems, Comparator.comparing(SelectItem::getLabel));
    // Delegate to super to continue rendering
    super.encodeInput(context, menu, clientId, selectItems, values, submittedValues, converter);
  }

}

I've checked this with PrimeFaces 10. If you need the source for a different PrimeFaces version, check the SelectOneMenuRenderer source code and select the according version tag (note that from PrimeFaces 11 the path changed). Note that the method you need to override, might be different in other versions (not likely, but possible).

Add your custom renderer to the render-kit section in your faces-config.xml like:

<render-kit>
  <renderer>
    <component-family>org.primefaces.component</component-family>
    <renderer-type>org.primefaces.component.SelectOneMenuRenderer</renderer-type>
    <renderer-class>my.custom.MySelectOneMenuRenderer</renderer-class>
  </renderer>
</render-kit>

Please note that this will sort the options every time for any p:selectOneMenu render, which comes with a performance penalty.

Upvotes: 3

Adam Waldenberg
Adam Waldenberg

Reputation: 2321

You don't need a custom tag.

In the case of a p:selectOneMenu you can just use f:selectItems (plural) and return a sorted collection (replacing your individual f:selectItem tags). In this case, that would be something like;

<f:selectItems value="#{view.countries}"/>

Where countries is a getter, getCountries(), returning a list of Map<String, String> sorted according to your choice.

Imagine for a second there was no f:selectItems though, and the only option available to us was f:selectItem (singular) - like in your example. Then the solution would be to use a JSF pre-render hook and sort the JSF components before that section was rendered. You can read exactly how this works here; Variable order of components (panels) in JSF page. In that example, a list of p:panel components are shuffled randomly inside a h:panelGroup. It can easily be applied to this case though.

To make it more transparent and hide the f:event tag to the pre-render hook on the pages, you could go one step further and define a composite component;

https://stackoverflow.com/tags/composite-component/info

This composite component could just use a selectOneMenu and introduce a sort attribute that would allow you to invoke the sorting pre-render hook as described above depending on if the value of the sort attribute is true or false.

Upvotes: 3

Related Questions