miercuri, 25 februarie 2015

JSF ValueChangeListener Use Cases

"The valueChangeListener will only be invoked when the form is submitted and the submitted value is different from the initial value" - BalusC

In this post, you can see several use cases for valueChangeListener attribute and ValueChangeListener interface:

·         use the valueChangeListener attribute to point a bean’s method directly when a radio is selected
<h:form>
 <h:selectOneRadio value="#{playerBean.player}" onchange="submit()" valueChangeListener="#{playerBean.playerChangeValueMethod}">
  <f:selectItems value="#{playerBean.players}" />
 </h:selectOneRadio>           
</h:form>

·         use the valueChangeListener attribute to point a bean’s method directly when a boolean checkbox is clicked
<h:form>          
 <h:selectBooleanCheckbox value="#{playerBean.player}" onclick="submit()" valueChangeListener="#{playerBean.playerChangeValueMethod}"/>    
</h:form>

·         use the valueChangeListener attribute to point a bean’s method directly when an item is selected
<h:form>
 <h:selectOneMenu value="#{playerBean.player}" onchange="submit()" valueChangeListener="#{playerBean.playerChangeValueMethod}">
  <f:selectItems value="#{playerBean.players}" />
 </h:selectOneMenu>           
</h:form>

·         use the valueChangeListener attribute to point a bean’s method directly at every key press - not practical, but it works as inspiration source (<f:ajax listener="..."/> is better is this case)
<h:form>
 <h:inputText value="#{playerBean.player}" onkeyup="submit()" valueChangeListener="#{playerBean.playerChangeValueMethod}"/>           
</h:form>

For these three use cases, the PlayerBean is:

@Named
@SessionScoped
public class PlayerBean implements Serializable {

 private List<String> players = new ArrayList<>();
 private String player;

 public PlayerBean() {
  players.add("Rafael Nadal");
  players.add("Novak Djokovic");
  players.add("Roger Federer");
  players.add("Andy Murray");
  players.add("David Ferrer");
 }

 public void playerChangeValueMethod(ValueChangeEvent e) {
  System.out.println("PlayerBean: [PLAYER CHANGE]" + "  " + e.getOldValue() + " IN " + e.getNewValue());
 }
  
 public List<String> getPlayers() {
  return players;
 }

 public void setPlayers(List<String> players) {
  this.players = players;
 }

 public String getPlayer() {
  return player;
 }

 public void setPlayer(String player) {
  this.player = player;
 }
}

·         use the ValueChangeListener interface instead of valueChangeListener attribute

<h:form>
 <h:selectOneMenu value="#{playerBean.player}" onchange="submit()">
  <f:valueChangeListener type="beans.PlayerChangeValueListener" />
  <f:selectItems value="#{playerBean.players}" />
 </h:selectOneMenu>           
</h:form>

The PlayerChangeValueListener is:

public class PlayerChangeValueListener implements ValueChangeListener {

 @Override
 public void processValueChange(ValueChangeEvent event) throws AbortProcessingException {
  System.out.println("PlayerChangeValueListener: [PLAYER CHANGE]" + "  " + event.getOldValue() + " IN " + event.getNewValue());
 }
}

Note You can use both, <f:valueChangeListener> and valueChangeListener attribute, but you have to be aware that the bean's method will be invoked first.

In some situations, you will prefer <f:ajax listener="..."/> instead of <f:valueChangeListener> or valueChangeListener. Per example, we have adapted the example with <h:selectOneMenu>, as below:

<h:form>
 <h:selectOneMenu id="myPlayersId" value="#{playerBean.player}">               
  <f:selectItems value="#{playerBean.players}" />
  <f:ajax execute="@form"  render=":selectedPlayerId" listener="#{playerBean.playerChangeValueMethod}" />
 </h:selectOneMenu>           
</h:form>

In this case, you have to add the below method in PlayerBean. This time, you will not have access to the old value, so maybe is not a good idea to replace the valueChangeListener with <f:ajax listener="..."/>. Do you need the old value ? This is the question!

public void playerChangeValueMethod(AjaxBehaviorEvent e) {
 System.out.println("PlayerBean: [PLAYER CHANGE]" + "  " + player);
}

But, in case that we need to invoke a method at every key press in a text box (see example above), is more proper to use <f:ajax listener="..."/>. Well, in this case, you can "calculate" the old value as a substring of current value:

<h:form>
 <h:inputText value="#{playerBean.player}">           
  <f:ajax event="keyup" execute="@form" render=":selectedPlayerId" listener="#{playerBean.playerChangeValueMethod}" />
 </h:inputText>
</h:form>

So, you can choose correctly between these two approaches only if you know the differences and the similarities between valueChangeListener and <f:ajax listener="..."/>. Well, BalusC provide a great answer on StackOverflow, so don't miss it! Moreover, the StackOverflow archive is also plenty with useful information.

Let's have a quick and handy guide about ValueChangeListener:
·         Don't forget to attach a onevent="submit()" JavaScript to the input component. Novices usually forget this golden rule, and no event will be fired.
·         The bean's method must have an argument of type ValueChangeEvent.
·         The ValueChangeListener will only be called when the form is submitted (this is why we need, onevent="submit()"), not when the value of the input is changed. Moreover, the submitted value should be different from the initial value.
·         The ValueChangeListener allows us to manipulate in bean the old value (event.getOldValue()) and the new value (event.getNewValue()). When you don't need the old value maybe the <f:ajax listener="..."/> will be a better choice.

Complete code on GitHub.

Niciun comentariu:

Trimiteți un comentariu