Search code examples
jsfprimefacesjsf-2jsf-2.2

Parameter passed from a child of p:timeline is null in action method


My goal: I like to add some commandLink as a child of PrimeFaces p:timeline The user should be able to click one of those links. The click shall invoke a method in my bean (CDI, ViewScoped). In the action method I need information from the clicked item in the timeline. Depending on the outcome of the action method there there might be a navigation to a different UI.

My attempt: The full form is quite short and simple. So I better post it entirely:

<h:form>
  <p:fieldset legend="Upcoming Gates">
    <p:timeline height="200px" style="width: 99%;"
           start="#{facilitatorCockpit.timelineStart}"
           end="#{facilitatorCockpit.timelineEnd}"
           value="#{facilitatorCockpit.timelineModel}"
           var="timelineitem"
           selectable="false"
           zoomable="false"
           moveable="false"                                
           >     
       <h:outputText value="#{timelineitem.systemElement}"/> <br/>
       <p:commandLink action="#{facilitatorCockpit.openStatusSheet(timelineitem.roadmapId)}"
                               process="@this"  value="#{timelineitem.roadmapId}"
                               title="Open Status Sheet"/>         
    </p:timeline>
  </p:fieldset>
</h:form>

Current behaviour: As one can see in the xhtml, I am displaying some "Id" on the timeline item. This id shows up correctly in the UI. The action method is executed, but the passed parameter is resolved to null on the server side.

I am using the very same attempt from inside a datatable (same UI, just a different form), where it works fine. I am just not able to call a action method with parameter(s) like this from inside of p:timeline

No nested forms on my page, no JavaScript errors visislbe in the browsers console. I also tried all combinations of commandLink / button and action/listener, even though the one used in the example is excatly what I want, but with the same results.

I am using PF 6.0 and Mojarra 2.2.10

Update: I noticed some slighty difference in the generated HTML to the generated HTML when I am using this kind of commandLink e.g. in a datatable.

In p:timeline the very same identifier and JavaScript calls are generated for every single timeline item:

<a id="j_idt88:j_idt94" href="#" class="ui-commandlink ui-widget" 
   aria-label="Open Status Sheet" 
   onclick="PrimeFaces.ab({s:&quot;j_idt88:j_idt94&quot;,p:&quot;j_idt88:j_idt94&quot;});
            return false;" 
   title="Open Status Sheet">FPR 137
</a>
<!-- .... -->
<a id="j_idt88:j_idt94" href="#" class="ui-commandlink ui-widget"
    aria-label="Open Status Sheet" 
    onclick="PrimeFaces.ab({s:&quot;j_idt88:j_idt94&quot;,p:&quot;j_idt88:j_idt94&quot;});
           return false;"
    title="Open Status Sheet">FPR 138

Current Solution: Based on the accepted answer my current approach now looks like this:

<p:commandLink action="#{facilitatorCockpit.openStatusSheetFromTimeline()}"
    process="@this"
    value="#{timelineitem.phase.label}"
    title="Open Status Sheet"
>         
   <f:param name="id" value="#{timelineitem.roadmapId}" />
</p:commandLink>

I added an additional action method to the backing bean which delegates to the original one.

public String openStatusSheetFromTimeline() {
    Map<String, String> params = FacesContext.getCurrentInstance()
        .getExternalContext()
        .getRequestParameterMap();
    String roadmapId = params.get("id");
    Long id = Long.parseLong(roadmapId);
    return openStatusSheet(id);
}

Still I am just wondering what's wrong with the inital approach.


Solution

  • Why do not you use the

    <p:ajax event="select" listener="#{facilitatorCockpit.onSelect}"/> ?

    Into the onSelect-method you can call openStatusSheet or refactore openStatusSheet for TimelineSelectEvent. The id can you save in the data-attribute.

    public void onSelect(TimelineSelectEvent e) {  
        TimelineEvent timelineEvent = e.getTimelineEvent();  
    
        // get your Id
        String roadmapId = timelineEvent.getDate.toString();
    } 
    

    Edit: Second solution

    For submit the id with custom components use p:commandButton with f:param.

        <p:timeline id="timeline" value="#{timelineBean.model}"
            height="250px" selectable="true" var="item">
    
            <p:commandButton value="Click #{item}"
                actionListener="#{timelineBean.click()}">
                <f:param name="id" value="#{item}" />
            </p:commandButton>
        </p:timeline>
    

    And into your backing bean can you catch the param like

    public void click() {
        Map<String, String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
        String roadmapId = params.get("id");
    
        ...
    }
    

    So you can insert 2 or more commmandButtons.