5
votes

What I actually want is two PrimeFaces <p:pickList>s that get validated with OmniFaces' <o:validateAll> component. Note, that validating a pickList with <o:validateAll> has a problem, which can be solved as described in issue 488 in the OmniFaces issue tracker.

So a very simple example of my requirement looks like this:

<h:form id="form1">
  <p:messages id="messages">
    <p:autoUpdate/>
  </p:messages>

  <p:pickList id="pick1" value="#{dummy.dualListModel}"
              var="item" itemLabel="#{item}" itemValue="#{item}">
    <p:ajax event="transfer"/>
  </p:pickList>

  <p:pickList id="pick2" value="#{dummy.dualListModel2}"
              var="item" itemLabel="#{item}" itemValue="#{item}">
    <p:ajax event="transfer"/>
  </p:pickList>

  <o:validateAll id="validPicks" components="pick1 pick2"
                 message="all values required!" />

  <h:commandButton id="done" value="Done" action="#{dummy.action1}"/>
</h:form>

<h:form id="theOtherForm">
  <h:commandButton id="otherFormAction" value="Action in other form"
                   action="#{dummy.action2}"/>
</h:form>

The dummy backing-bean behind just provides getters/setters for the two dualListModel properties and the action-methods that do nothing.

When I run this code and leave at least one pickList empty, submitting the Done-button brings up a validation failure as excepted. However, clicking the button in the other form after the validation failed, leads to a NullPointerException in the PickListRenderer. Here is the StackTrace:

Caused by:java.lang.NullPointerException
    at org.primefaces.component.picklist.PickListRenderer.encodeMarkup(PickListRenderer.java:92)
    at org.primefaces.component.picklist.PickListRenderer.encodeEnd(PickListRenderer.java:59)
    at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:920)
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1863)
    at javax.faces.render.Renderer.encodeChildren(Renderer.java:176)
    at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:890)
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1856)
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1859)
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1859)
    at com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:456)
    at com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:134)
    at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:337)
    at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:337)
    at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:337)
    at org.omnifaces.viewhandler.OmniViewHandler.renderView(OmniViewHandler.java:119)
    at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:120)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
    at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:219)
    [...]

Im using OmniFaces 2.7 and PrimeFaces 6.2.

Please note, that I'm using a custom renderer for the PickList that overrides getConvertedValue(), as described in the linked issue from above. However, this does not change behavior of the renderer in any way that should effect this bug. It just makes <o:validateAll> recognize that the pickList is empty.

This seems like a bug to me, but I'm not sure if it is a bug in OmniFaces or in PrimeFaces. Anyone got any idea?

Workaround

As a workaround one could add the required="true" attribute to all of the pickLists.

Update

The same problem exists with PrimeFaces 7.0 (just the line numbers differ a bit).

In addition I produced another NPE with the above code and PrimeFaces 7.0, when sumbitting empty picklists via "done" and then transfering an item in one of the picklist to the target list.

The other exception occurs here:

Caused by:java.lang.NullPointerException
    at org.primefaces.component.picklist.PickList.validateValue(PickList.java:140)
    at javax.faces.component.UIInput.validate(UIInput.java:982)
    at org.primefaces.component.picklist.PickList.validate(PickList.java:181)
    at javax.faces.component.UIInput.executeValidate(UIInput.java:1248)
    at javax.faces.component.UIInput.processValidators(UIInput.java:712)
    at com.sun.faces.context.PartialViewContextImpl$PhaseAwareVisitCallback.visit(PartialViewContextImpl.java:575)
    at com.sun.faces.component.visit.PartialVisitContext.invokeVisitCallback(PartialVisitContext.java:183)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1689)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1700)
    at javax.faces.component.UIForm.visitTree(UIForm.java:371)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1700)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1700)
    at com.sun.faces.context.PartialViewContextImpl.processComponents(PartialViewContextImpl.java:403)
    at com.sun.faces.context.PartialViewContextImpl.processPartial(PartialViewContextImpl.java:266)
    at org.primefaces.context.PrimePartialViewContext.processPartial(PrimePartialViewContext.java:63)
    at javax.faces.context.PartialViewContextWrapper.processPartial(PartialViewContextWrapper.java:219)
    at org.omnifaces.context.OmniPartialViewContext.processPartial(OmniPartialViewContext.java:124)
    at javax.faces.component.UIViewRoot.processValidators(UIViewRoot.java:1193)
    at com.sun.faces.lifecycle.ProcessValidationsPhase.execute(ProcessValidationsPhase.java:76)

In the corresponding code, the value of oldModel, which is initialized via getValue() is null.

3
I will wait for @balusC to chime in as he usually knows where this bug actually lies. I have a feeling it might be related to how PickList works.Melloware
I'd add the code where you override the renderer to since it might play a role...Kukeltje
The code for the custom renderer is available from the linked OmniFaces issue. As mentioned, it just overrides getConvertedValue() and only changes the single line, which instantiates the DualListModel.Martin Höller

3 Answers

3
votes

As Melloware wrote, the issue was reported as PrimeFaces issue #4756. It turned out, that the problem only exists with Mojarra (tested with 2.2.17, 2.2.18, 2.2.19 and 2.3.9) and not with MyFaces. So another issue was create: Mojarra #4398.

2
votes

Issue verified and reported on GitHub: https://github.com/primefaces/primefaces/issues/4756

I submitted a fix which will hopefully be included in PF 7.0.2 and PF 7.1

https://github.com/primefaces/primefaces/pull/4759

0
votes

If your picklist uses a FacesConverter please note that the input value must be unescaped when compared to retrieve the original object. In fact, if the input string is something like 'Chicken&Egg' the converter receives 'Chicken&amp;Egg' Example:

      @Override
  public YourObject getAsObject(FacesContext facesContext, UIComponent uiComponent, String name)
  {
    PickList pickList = (PickList) uiComponent;
    DualListModel<YourObject> dualListModel = (DualListModel<YourObject>) pickList.getValue();
    List<YourObject> all = dualListModel.getSource();
    all.addAll(dualListModel.getTarget());

    return all.stream().filter(element ->
      String.valueOf(element.getName()).equals(HtmlUtils.htmlUnescape(name)))
      .findFirst().get();
  }