1

I'm using an editable Primefaces p:datatable to show the data to the user. In this datatable, I have a p:column with a h:selectOneMenu, and another one with a p:selectBooleanCheckbox.

I want to check or uncheck and disable or enable the checkbox depending on the value selected in the h:selectOneMenu.

If I only had one h:selectOneMenu and one p:selectBooleanCheckbox, I'd use a p:ajax to attach a listener to the change event, and I'd manipulate the p:selectBooleanCheckbox in this method. But I have a pair of h:selectOneMenu and p:selectBooleanCheckbox per row and I don't know how to do this.

This is what I tried:

<h:form>
    <p:dataTable var="appointment" value="#{prescController.appointmentsToday}" editable="true" id="tblAppointments">
        <p:ajax event="rowEdit"
            listener="#{prescController.onEdit}" update=":messages" />

        <p:column sortBy="presc.drug" headerText="Drug">
            <p:cellEditor>
                <f:facet name="output">
                    <h:outputText value="#{appointment.presc.drug.name}" />
                </f:facet>

                <f:facet name="input">
                    <h:selectOneMenu value="#{appointment.presc.drug}"
                        converter="#{drugConverter}" required="true">
                        <f:selectItem itemLabel="" noSelectionOption="true" />
                        <f:selectItems value="#{prescController.drugs}"
                            var="drug" itemLabel="#{drug.name}" />

                        <p:ajax update="autoAmount" />
                    </h:selectOneMenu>
                </f:facet>
            </p:cellEditor>
        </p:column>

        <p:column sortBy="presc.autoAmount" headerText="Auto amount">
            <p:cellEditor>
                <f:facet name="output">
                    <h:outputText value="Y"
                        rendered="#{not empty appointment.presc.drug.rules and appointment.presc.autoAmount}" />
                    <h:outputText value="N"
                        rendered="#{empty appointment.presc.drug.rules or not appointment.presc.autoAmount}" />
                </f:facet>
                <f:facet name="input">
                    <p:selectBooleanCheckbox id="autoAmount"
                        value="#{not empty appointment.presc.drug.rules and appointment.presc.autoAmount}"
                        disabled="#{appointment.presc.drug.name eq 'somethingsomething'}" />
                </f:facet>
            </p:cellEditor>
        </p:column>


        <p:column>
            <p:rowEditor />
        </p:column>
    </p:dataTable>
</h:form>
zootropo
  • 2,441
  • 3
  • 31
  • 48
  • Hi Herreria, you want to execute only this one selectOneMenu and only render the fields of the one connected table row - without the whole table being sent back to the server and rerendered? – L-Ray Dec 03 '13 at 09:26
  • Yes, L-Ray, that's what I'd like. Is it possible? – zootropo Dec 03 '13 at 09:32
  • 1
    What problem exactly are you facing? How exactly did you fail to specify `` "the usual way" as if you wasn't inside the data table? – BalusC Dec 03 '13 at 10:24
  • I tried the `` combined with `` (where `entity` is the value of the attribute var for the `p:dataTable` and `subentity` is what I selected in the `h:selectOneMenu`) but the AJAX response is always the same; it doesn't add the disabled attribute when I select the "subentity" with name "somethingsomething" – zootropo Dec 03 '13 at 11:55
  • Showing a bit of code would help – David Goshadze Dec 03 '13 at 12:06
  • Sorry. I updated the question with the code. The problem is, I think, that the related entity, the one that the user selects in the `h:selectOneMenu`, is not set until the user saves the row... – zootropo Dec 03 '13 at 12:20

3 Answers3

1

The post Retrieving other component's client ID in JSF 2.0 describes how to retrieve ids of other components in a page. In my opinion, the #{p:component('sampleButton')} should find the next component having this ID in the component tree - this should be the same row.

Alternatively, you should be able to rerender the whole row via JSF 2 #{component.parent.clientId} functionality (measure out, how many "parent" steps you need, e.g. #{component.parent.parent.clientId}).

Hope it helps, else just add comments... :-)

Community
  • 1
  • 1
L-Ray
  • 1,637
  • 1
  • 16
  • 29
1

I can't imagine why you are unsatisfied with simple update="checkboxId"but what you can try is updating component through widgetVar which you can generate during page render.

Tiny example:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:p="http://primefaces.org/ui">
<h:head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Table Example</title>
</h:head>
<h:body>
    <h:form prependId="false">
        <p:dataTable var="data" value="#{tableBean.data}">
            <p:column headerText="Command">
                <p:commandButton value="Toggle" actionListener="#{tableBean.toggleSelection(data.id)}"
                    update="@widgetVar(tableCheckboxComponent_#{data.id})" />
            </p:column>
            <p:column headerText="Value">
                <h:outputText value="#{data.value}" />
            </p:column>
            <p:column headerText="Selected">
                <p:selectBooleanCheckbox widgetVar="tableCheckboxComponent_#{data.id}" value="#{data.selected}" />
            </p:column>
        </p:dataTable>
    </h:form>
</h:body>
</html>

Backing bean:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;

@ManagedBean
@ViewScoped
public class TableBean {
    private Map<String, MyData> data;


    public List<MyData> getData(){
        return new ArrayList<MyData>(data.values());
    }

    public TableBean() {
        data = new HashMap<String, MyData>();
        for (int i = 0; i<22; i++) {
            String id = "id" + Integer.toString(i);
            data.put(id, new MyData( id , i));
        }
    }

    public void toggleSelection(String id) {
        MyData myData = data.get(id);
        myData.setSelected(!myData.isSelected());
    }
}

And Data object:

public class MyData {
    private String id;
    private boolean selected;
    private int value;

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public int getValue() {
        return value;
    }
    public void setValue(int value) {
        this.value = value;
    }

    public boolean isSelected() {
        return selected;
    }

    public void setSelected(boolean selected) {
        this.selected = selected;
    }   

    public MyData(String id, int value) {
        this.id = id;
        this.value = value;
        this.selected = false;
    }
}
David Goshadze
  • 1,439
  • 12
  • 16
0

I still don't know why my approach didn't work.

In the end, I added a listener to the p:ajax component to manipulate the SelectBooleanCheckbox in the managed bean

<p:ajax listener="#{prescBean.onDrugSelected}" update="autoAmount" />

public void onDrugSelected(AjaxBehaviorEvent event) {
    Drug drug = (Drug) ((UIOutput) event
            .getSource()).getValue();
    boolean hasRules = drug.getRules().size() > 0;

    SelectBooleanCheckbox cbAutoAmount = (SelectBooleanCheckbox) ComponentUtils
            .findComponent(FacesContext.getCurrentInstance().getViewRoot(),
                    "autoAmount");

    cbAutoAmount.setDisabled(!hasRules);
    cbAutoAmount.setValue(hasRules);
}
zootropo
  • 2,441
  • 3
  • 31
  • 48