2017-05-23 1 views
0

Je publie MCVE ici parce que je suis confondu avec le comportement JSF. Je index.xhtml:Primefaces selectBooleanCheckbox valeur perdue sur l'action ajax

<h:form id="entityForm" enctype="multipart/form-data" styleClass="entityForm"> 
<p:dataTable id="dataTableId1" value="#{testBean.testEntityList}" var="tent" rowKey="#{tent.id}"> 
    <p:column style="width:16px"> 
     <p:rowToggler/> 
    </p:column> 
    <p:column> 
     <h:outputText value="#{tent.id}"/> 
    </p:column> 
    <p:column> 
     <h:outputText value="#{tent.name}"/> 
    </p:column> 
    <p:rowExpansion> 
     <p:selectBooleanCheckbox value="#{tent.something}"/> 
     <p:inputText value="#{tent.strNextToSomethingCheckBox}" /> 
    </p:rowExpansion> 
</p:dataTable> 
<p:commandButton id="button1" ajax="true" value="Do something" update="entityForm"/> 
</h:form> 

Code haricot:

@ManagedBean(name = "testBean") 
@ViewScoped 
public class TestBean implements Serializable { 

private static final Logger LOG = Logger.getLogger(TestBean.class); 

private List<TestEntity> testEntityList; 

@PostConstruct 
public void init() { 
    LOG.debug("init call"); 
    testEntityList = new ArrayList<>(); 

    testEntityList.add(new TestEntity(1L, "Entry 1", true, "Value 1 str")); 
    testEntityList.add(new TestEntity(2L, "Entry 2", false, "Value 2 str")); 
    testEntityList.add(new TestEntity(3L, "Entry 3", false, "Value 3 str")); 

} 

public class TestEntity { 

    Long id; 

    String name; 

    Boolean something; 

    String strNextToSomethingCheckBox; 


    public TestEntity(Long id, String name, Boolean something, String strNextToSomethingCheckBox) { 
     this.id = id; 
     this.name = name; 
     this.something = something; 
     this.strNextToSomethingCheckBox = strNextToSomethingCheckBox; 
    } 

    public Long getId() { 
     return id; 
    } 

    public void setId(Long id) { 
     this.id = id; 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public Boolean getSomething() { 
     return something; 
    } 

    public void setSomething(Boolean something) { 
     LOG.debug("setSomething call"); 
     this.something = something; 
    } 

    public String getStrNextToSomethingCheckBox() { 
     return strNextToSomethingCheckBox; 
    } 

    public void setStrNextToSomethingCheckBox(String strNextToSomethingCheckBox) { 
     LOG.debug("setStrNextToSomethingCheckBox call"); 
     this.strNextToSomethingCheckBox = strNextToSomethingCheckBox; 
    } 
} 


public List<TestEntity> getTestEntityList() { 
    return testEntityList; 
} 

public void setTestEntityList(List<TestEntity> testEntityList) { 
    this.testEntityList = testEntityList; 
} 

} 

Scénario: lorsque vous la page ouverte, développez première ligne, vous verrez état de case en case est cochée, lorsque vous appuyez sur le bouton, le formulaire est mise à jour et case à cocher est toujours cochée (vous devez étendre la ligne à nouveau pour voir que)

Scénario problématique: ouvrir la page, ne pas développer une ligne, cliquez simplement sur le bouton, après que le formulaire est mis à jour, développez la première rangée et vous verrez que La valeur est définie sur true à false

Question: Pourquoi la valeur est-elle définie de vrai à faux? Est-ce un bug ou un comportement attendu? Comment puis-je éviter ça ? Merci.

+0

Et si vous supprimez ceci: 'enctype =" multipart/form-data "' depuis le 'h: form'? – Kukeltje

+0

@Kukeltje même, la valeur est perdue/réinitialisée à false – jNick

Répondre

0

Vous pouvez consulter primefaces SelectBooleanCheckboxRenderer#decode codes ci-dessous:

@Override 
public void decode(FacesContext context, UIComponent component) { 
    SelectBooleanCheckbox checkbox = (SelectBooleanCheckbox) component; 

    if(checkbox.isDisabled()) { 
     return; 
    } 

    decodeBehaviors(context, checkbox); 

    String clientId = checkbox.getClientId(context); 
    String submittedValue = (String) context.getExternalContext().getRequestParameterMap().get(clientId + "_input"); 

    if(submittedValue != null && isChecked(submittedValue)) { 
     checkbox.setSubmittedValue(true); 
    } 
    else { 
     checkbox.setSubmittedValue(false); 
    } 
} 

La raison pour laquelle la valeur est de true à false est parce que p:selectBooleanCheckbox n'est pas réglé dans le RequestParameterMap lorsque p: rowExpansion est proche. Ainsi, lorsque le p:selectBooleanCheckbox est décodé, submittedValue devient nul. Ainsi, en exécutant checkbox.setSubmittedValue(false).

Lorsque p:rowExpansion est développé, vous pouvez voir la valeur de p:selectBooleanCheckbox dans RequestParameterMap. Vous pouvez confirmer en ajoutant action="#{testBean.doSomething}" à p:commandButton avec la méthode de suivi dans la fève:

public void doSomething() { 
    FacesContext context = FacesContext.getCurrentInstance(); 
    Map<String, String> requestParameterMap = context.getExternalContext().getRequestParameterMap(); 
    Set<String> keySet = requestParameterMap.keySet(); 

    for (String key : keySet) { 
     LOG.debug("key>>" + key); 
     LOG.debug("value>>" + requestParameterMap.get(key)); 
    } 
} 

Notez que p:selectBooleanCheckbox est également PAS dans le jeu RequestParameterMap quand il est décochée.

Vous pouvez éviter ce problème en overiding SelectBooleanCheckboxRenderer#decode et chaging ceci:

if(submittedValue != null && isChecked(submittedValue)) { 
    checkbox.setSubmittedValue(true); 
} 
else { 
    checkbox.setSubmittedValue(false); 
} 

à ceci:

if (submittedValue != null && isChecked(submittedValue)) { 
    checkbox.setSubmittedValue(true); 
} else { 
    if (checkbox.isRendered()) { 
     checkbox.setSubmittedValue(false); 
    } 
} 

De cette façon, la valeur sera fausse que lorsque p:selectBooleanCheckbox est décoché et est rendu.

EDIT:

Je suis désolé. Ma solution de remplacement est incorrecte mais l'idée est que vous devez remplacer SelectBooleanCheckboxRenderer#decode.

Je posterai les codes corrects quand ils seront disponibles.

EDIT 2:

peut sembler ne pas le faire fonctionner en remplaçant SelectBooleanCheckboxRenderer#decode donc j'ai essayé une approche différente.

J'ai ajouté un indicateur (basculé) dans TestEntity avec une valeur init de false. Ceci sera utilisé comme drapeau pour afficher p:selectBooleanCheckbox à l'intérieur de p:rowExpansion.
Ce drapeau sera mis à vrai lorsque la ligne est basculée (VISIBLE) en utilisant la méthode p:ajax event="rowToggle" et onRowToggle dans le bean.

L'idée est que SelectBooleanCheckboxRenderer#decode ne sera pas exécutée lorsque le p:selectBooleanCheckbox n'est pas rendu. Cela entraînera la conservation de la valeur. Cette approche ne pas nécessitent l'annulation SelectBooleanCheckboxRenderer#decode.

Voici les codes:

xhtml:

<h:form id="entityForm" enctype="multipart/form-data" styleClass="entityForm"> 
<p:dataTable id="dataTableId1" value="#{testBean.testEntityList}" var="tent" rowKey="#{tent.id}"> 

    <p:ajax event="rowToggle" listener="#{testBean.onRowToggle}"/> 

    <p:column style="width:16px"> 
     <p:rowToggler/> 
    </p:column> 
    <p:column> 
     <h:outputText value="#{tent.id}"/> 
    </p:column> 
    <p:column> 
     <h:outputText value="#{tent.name}"/> 
    </p:column> 
    <p:rowExpansion> 
     <p:selectBooleanCheckbox value="#{tent.something}" rendered="#{tent.toggled}"/> 
     <p:inputText value="#{tent.strNextToSomethingCheckBox}" /> 
    </p:rowExpansion> 
</p:dataTable> 
<p:commandButton id="button1" ajax="true" value="Do something" update="entityForm"/> 
</h:form> 

haricot:

@ManagedBean(name = "testBean") 
@ViewScoped 
public class TestBean implements Serializable { 

    private static final Logger LOG = Logger.getLogger(TestBean.class); 

    private List<TestEntity> testEntityList; 

    @PostConstruct 
    public void init() { 
     LOG.debug("init call"); 
     testEntityList = new ArrayList<>(); 

     testEntityList.add(new TestEntity(1L, "Entry 1", true, "Value 1 str", false)); 
     testEntityList.add(new TestEntity(2L, "Entry 2", false, "Value 2 str", false)); 
     testEntityList.add(new TestEntity(3L, "Entry 3", false, "Value 3 str", false)); 
    } 

    public class TestEntity { 

     Long id; 

     String name; 

     Boolean something; 

     String strNextToSomethingCheckBox; 

     boolean toggled; 

     public TestEntity(Long id, String name, Boolean something, String strNextToSomethingCheckBox, boolean toggled) { 
      this.id = id; 
      this.name = name; 
      this.something = something; 
      this.strNextToSomethingCheckBox = strNextToSomethingCheckBox; 
      this.toggled = toggled; 
     } 

     // getters and setters 
    } 

    public List<TestEntity> getTestEntityList() { 
     return testEntityList; 
    } 

    public void setTestEntityList(List<TestEntity> testEntityList) { 
     this.testEntityList = testEntityList; 
    } 

    public void onRowToggle(ToggleEvent event) { 
     TestEntity entity = (TestEntity) event.getData(); 

     if (event.getVisibility() == Visibility.VISIBLE) { 
      entity.setToggled(true); 
     } else { 
      entity.setToggled(false); 
     } 
    } 
} 

Hope this helps.