JSF 2.2 ajax no refresh multipart/form-data - ajax

I have a problem and i simplfied the code to show you.
<h:form enctype="multipart/form-data">
<h:panelGroup id="images">
<h:inputText id="auctionImage" value="#{testBean.nowy}"/>
<h:commandButton value="Add">
<f:ajax execute="auctionImage" render="images"/>
</h:commandButton>
<ui:repeat value="#{testBean.elements}" var="oneImage">
<h:outputText value="#{oneImage.title}" />
</ui:repeat>
</h:panelGroup>
</h:form>
This is my main Bean
#SessionScoped
#ManagedBean
public class TestBean {
private List<Element> elements;
private String nowy;
public String getNowy() {
return nowy;
}
public void setNowy(String nowy) {
Element el = new Element();
el.setTitle(nowy);
if(elements==null) elements = new ArrayList<>();
elements.add(el);
this.nowy = nowy;
}
public List<Element> getElements() {
return elements;
}
public void setElements(List<Element> elements) {
this.elements = elements;
}
}
This is the element class
#ManagedBean
public class Element {
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
I want to populate the list everytime I click Add button and print it using ajax. The list is populated but ajax refresh the panelGroup only for the first time. To make it work again I have to refresh website. What i do wrong?

Ok i was managed to resolve the issue. Well, to be honest it wasn't me but jsf programmers. I found out that it happens when you use
<h:form enctype="multipart/form-data">
with ajax. This is the bug and it has been repaired some time ago. You just have to update your jsf version.
http://javaserverfaces.java.net/ - i got 2.2.3 version which is the newest for now.
After restarting glassfish and redeploying application everything works just fine.

Related

ClassCastException for <h:selectManyCheckbox> with validation on ajax update when INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL is active

I have a <h:selectManyCheckbox> that has a required-validation on. If I submit the form, I get a validation error when nothing is selected. So far, this ist expected. However, if I do an ajax update on the checkbox then, I get a ClassCastException. But only if empty values are treated as null.
So, I have the following setup. In the web.xml I set
<context-param>
<param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
<param-value>true</param-value>
</context-param>
Then I have an xhtml-page like this:
<h:form id="main">
<h:selectManyCheckbox id="value" value="#{testcb.selected}" required="true" requiredMessage="Select at least one entry">
<f:selectItems value="#{testcb.available}"/>
</h:selectManyCheckbox>
<div><h:message for="value" style="color:red;"/></div>
<h:outputLabel for="checkit" value="Enter some text: "/>
<h:inputText id="checkit" value="#{testcb.text}">
<f:ajax event="change" execute="#this" render=":main:value"/>
</h:inputText>
<div><h:commandButton type="submit" value="Submit" action="#{testcb.action}"/></div>
</h:form>
And this backing bean:
#Named("testcb")
#SessionScoped
public class TestCBBean implements Serializable {
private final Set<TestValue> available = EnumSet.allOf(TestValue.class);
private final Set<TestValue> selected = EnumSet.noneOf(TestValue.class);
private String text;
public void action() {}
public Set<TestValue> getAvailable() { return available; }
public void setAvailable(Set<TestValue> available) {
this.available.clear();
this.available.addAll(available);
}
public Set<TestValue> getSelected() { return selected; }
public void setSelected(Set<TestValue> selected) {
this.selected.clear();
this.selected.addAll(selected);
}
public String getText() { return text; }
public void setText(String text) { this.text = text; }
}
And this enum:
public enum TestValue { ONE, TWO, THREE }
I am running this in Wildfly 26.0.1-Final (JavaEE 8). But this also happens in older versions (like Wildfly 15). What I am doing:
enter some text and leave the box: an ajax update runs setting the value successfully in the model
I press submit: the validation error for the empty checkboxes pops up
I modify the text in the input and leave the box: the ajax update results in the following Exception:
java.lang.ClassCastException: class java.lang.String cannot be cast to class [Ljava.lang.Object; (java.lang.String and [Ljava.lang.Object; are in module java.base of loader 'bootstrap')
com.sun.jsf-impl#2.3.17.SP01//com.sun.faces.renderkit.html_basic.MenuRenderer.getSubmittedSelectedValues(MenuRenderer.java:508)
com.sun.jsf-impl#2.3.17.SP01//com.sun.faces.renderkit.html_basic.SelectManyCheckboxListRenderer.encodeEnd(SelectManyCheckboxListRenderer.java:89)
javax.faces.api#3.1.0.SP01//javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:600)
javax.faces.api#3.1.0.SP01//javax.faces.component.UIComponent.encodeAll(UIComponent.java:1655)
com.sun.jsf-impl#2.3.17.SP01//com.sun.faces.context.PartialViewContextImpl$PhaseAwareVisitCallback.visit(PartialViewContextImpl.java:628)
com.sun.jsf-impl#2.3.17.SP01//com.sun.faces.component.visit.PartialVisitContext.invokeVisitCallback(PartialVisitContext.java:159)
javax.faces.api#3.1.0.SP01//javax.faces.component.UIComponent.visitTree(UIComponent.java:1457)
javax.faces.api#3.1.0.SP01//javax.faces.component.UIComponent.visitTree(UIComponent.java:1469)
javax.faces.api#3.1.0.SP01//javax.faces.component.UIForm.visitTree(UIForm.java:355)
On the ajax update the checkboxes are not submitted. But they seem to contain an empty string as submitted value from the validation step before.
When setting the context parameter to false this works. But I want to keep it on true. Any ideas how I could work around this problem?
Reproduced. This is indeed a bug in Mojarra.
It boils down to that the following method in UIInput superclass ...
#Override
public Object getSubmittedValue() {
if (submittedValue == null && !isValid() && considerEmptyStringNull(FacesContext.getCurrentInstance())) {
return "";
} else {
return submittedValue;
}
}
... is not overridden in UISelectMany superclass in such way that it returns new String[0] instead of "". This was an oversight during implementing Faces issue 671.
I have fixed it in Mojarra issue 5081.
In the meanwhile, until you can upgrade to the Mojarra version containing the fix, you can temporarily work around it by copy pasting the entire source code file of UISelectMany into your project while maintaining the package and adding the following method to it:
#Override
public Object getSubmittedValue() {
Object submittedValue = super.getSubmittedValue();
return "".equals(submittedValue) ? new String[0] : submittedValue;
}

Why does altering my managed bean's definition result in the constructor not being called?

Please have a look at the following JSF page and at the definition of the managed bean class which it references.
When I run my app and load the page I get the results shown in the "Good Results" screen shot. Note that the page is displaying data and that the "Hello World" string printed by the bean's constructor appears in the console.
If I alter the bean's definition by commenting out the definition of the "junk" integer (near the top) then I get the results shown in the "Bad Results" screen shot. Now there is no data and, most tellingly, the "Hello World" string does not appear in the console. No errors are reported to the console. The page is being rendered but it appears as though the JSF engine has decided that it does not like the bean's definition and is therefore not going to use it (the constructor is not called).
I have tried mightily to produce a Minimal, Complete, and Verifiable example. I eliminated several forms from the JSF page (you can observe that much of the bean's code is no longer being referenced by the JSF page). I eliminated JPA from the picture by creating the DummyDataAccessService class. I tried to eliminate the use of one or more of my own custom classes (Order, Patient, Product, DataAccessService, and DummyDataAccessService) but I could not: almost any alteration of the bean's definition produces the same strange behavior that results from removing the definition of the "junk" member variable.
I created a custom logging.properties file which bumped the level to ALL. The logging produced by the Good vs. Bad cases were close to the same. The two "Logging Differences" screen shots below reveal the primary differences.
I do not know how to explore this further. I don't know enough to even conjecture what might be going on. Any clue or recommendation on a course of action will be greatly appreciated.
JSF Page
<!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:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<h:outputStylesheet library="default" name="css/style.css" />
<title>Managed Bean Problem</title>
</h:head>
<h:body>
<h3 class="title">Managed Bean Problem</h3>
<h:outputText value=" " />
<table align="center" width="600">
<tr><td><h:form>
<h:dataTable value="#{orderController.table}" var="order" styleClass="demo" columnClasses="columns, columns">
<h:column>
<f:facet name="header">
<h:column>
<h:outputText value="Patient"></h:outputText>
</h:column>
</f:facet>
<h:outputText value="#{order.patient.fullName}"></h:outputText>
</h:column>
<h:column>
<f:facet name="header">
<h:column>
<h:outputText value="Product"></h:outputText>
</h:column>
</f:facet>
<h:outputText value="#{order.product.name}"></h:outputText>
</h:column>
<h:column>
<f:facet name="header">
<h:column>
<h:outputText value="Actions"></h:outputText>
</h:column>
</f:facet>
<h:panelGrid columns="1">
<h:commandLink value="delete" action="#{orderController.delete}">
<f:setPropertyActionListener target="#{orderController.target}" value="#{order}" />
</h:commandLink>
</h:panelGrid>
</h:column>
</h:dataTable>
</h:form></td></tr>
</table>
</h:body>
Order Controller Managed Bean
package com.rvaessen.dmescripts.controllers;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.model.SelectItem;
import com.rvaessen.dmescripts.model.*;
#ManagedBean
public class OrderController {
private int junk; // Leave it in; Good. Comment it out; Bad.
private DataAccessService dataService = new DummyDataAccessService();
public OrderController() {
System.out.println("Hello, World");
}
#PostConstruct
public void init() {
initPatients();
initProducts();
initOrders();
}
// ********************************* The Orders Table ***************************************
private Order target;
private List<Order> table;
private void initOrders() { table = dataService.getOrders(); }
public List<Order> getTable() { return table; }
public void setTarget(Order order) { target = order; }
public String delete() {
dataService.removeOrder(target);
table.remove(target);
return null;
}
// ********************************* Add New Order ***************************************
// NOTE: The Add New Order methods are no longer referenced by the JSF page
private Order newOrder;
public String addNew() {
newOrder = new Order();
return null;
}
public String save() {
dataService.addOrder(newOrder, patient, product);
table.add(newOrder);
cancel();
return null;
}
public String cancel() {
newOrder = null;
return null;
}
public boolean getAddingNew() { return newOrder != null; }
/************************ The Patients Menu **********************************/
// NOTE: The Patients Menu methods are no longer referenced by the JSF page
private Patient patient;
private List<Patient> patients;
private void initPatients() {
patients = dataService.getPatients();
if (patients.size() > 0) patient = patients.get(0);
}
public List<SelectItem> getPatients() {
List<SelectItem> list = new ArrayList<SelectItem>();
patients.forEach(patient -> list.add(new SelectItem(patient.getId(), patient.getFullName())));
return list;
}
public Long getPatientId() {
return patient == null ? 0 : patient.getId();
}
public void setPatientId(Long id) {
patients.forEach(patient -> {
if (patient.getId() == id) {
this.patient = patient;
}
});
}
/************************ The Products Menu **********************************/
// NOTE: The Products Menu methods are no longer referenced by JSF page
private Product product;
private List<Product> products;
private void initProducts() {
products = dataService.getProducts();
if (products.size() > 0) product = products.get(0);
}
public List<SelectItem> getProducts() {
List<SelectItem> list = new ArrayList<SelectItem>();
if (patient != null) {
products.forEach(product -> {
if (product.getInsurance().equals(patient.getInsurance())) {
list.add(new SelectItem(product.getId(), product.getName()));
}
});
}
return list;
}
public Long getProductId() {
return product == null ? 0 : product.getId();
}
public void setProductId(Long id) {
products.forEach(product -> {
if (product.getId() == id) {
this.product = product;
}
});
}
}
Good Results
Bad Results
Log Differences 1
Log Differences 2
This issue was never solved. It was made to go away by recreating the Eclipse project from scratch; simultaneously upgrading to JSF 2.3 / CDI 1.2.

JSF2.2, RequestScope and component persistence

My actual scenario is a simple JSF project that uses Mojarra 2.2.5.
I'm trying to get programmatically a component instance (via "findComponent" method or binding ...) and set some attributes (click on "ChangeColor" button).
After that, using any other action (for example clicking on "Send" button), the previous changes are ignored !!
It seems that changeColor method don't update the ViewState !
The sample code is the following:
<h:form id="form">
<h:panelGrid columns="1">
<h:inputText id="input" binding="#{page9.input}"/>
<h:commandButton value="Change BackColor" action="#{page9.changeColor}"/>
<h:commandButton value="Send" action="#{page9.dummy}" />
</h:panelGrid>
</h:form>
And the relative RequestScope bean
#ManagedBean(name="page9")
#RequestScoped
public class Page9 implements Serializable {
private static final long serialVersionUID = 1L;
private HtmlInputText input;
public HtmlInputText getInput() {
return input;
}
public void setInput(HtmlInputText input) {
this.input = input;
}
public void changeColor(){
FacesContext fc = FacesContext.getCurrentInstance();
HtmlInputText hit = (HtmlInputText) fc.getViewRoot().findComponent(":form:input");
hit.setStyle("background-color:blue");
}
public void dummy(){
}
}
Some important considerations:
1) I must use RequestScope for compatibility reasons.
2) Setting javax.faces.PARTIAL_STATE_SAVING to "false" all works fine (!).
3) Trying to use MyFaces 2.2.3 libraries all works fine (!).
What do you think about ?
Thanks.

How to make three JSF h:selectOneMenus that depend on each other

I hope someone can help me. This is a "Category-Type-Item" situation where the user has to select from three different drop-down lists. In JSF I made it with three h:selectoneMenu. The first two h:selectOneMenu work OK. I read a solution here, about a "malformedXML" error when I tried the first time. That's the reason each selectOneMenu is surrounded by a h:panelGorup. I implemented it and work partially and only after reload (F5) the page, but this is not an acceptable solution.
Here is the code that I'm implementing:
<p:panel rendered="#{temporadaControllerExt.noHayGrupos}"
style="width: 650px">
<h:panelGrid columns="3">
<h:panelGroup id="fases">
<h:outputLabel value="Fase: "/>
<h:selectOneMenu value="#{temporadaControllerExt.faseSelectedFinal}">
<f:selectItems value="#{temporadaControllerExt.fases}"/>
<f:ajax render="grupos"/>
</h:selectOneMenu>
</h:panelGroup>
<h:panelGroup id="grupos">
<h:outputLabel value="Grupo: "/>
<h:selectOneMenu value="#{temporadaControllerExt.grupoSelected}"
disabled="#{temporadaControllerExt.grupoListDisable}">
<f:selectItems value="#{temporadaControllerExt.gruposDefase}"/>
<f:ajax render="jornadas"/>
</h:selectOneMenu>
</h:panelGroup>
<h:panelGroup id="jornadas">
<h:outputLabel value="Jornada: "/>
<h:selectOneMenu value="#{temporadaControllerExt.jornadaSelected}"
disabled="#{temporadaControllerExt.jornadaListDiseable}">
<f:selectItems value="#{temporadaControllerExt.jornadasEnGrupo}"/>
</h:selectOneMenu>
</h:panelGroup>
Of course is not complete, for space reasons. And here is part of the bean controller that I'm using:
#ManagedBean(name = "temporadaControllerExt")
#SessionScoped
public class TemporadaControllerExt extends TemporadaController {
private Fase fase;
private Grupo grupo;
private Jornada jornada;
private String faseSelectedFinal;
private String grupoSelected;
private String jornadaSelected;
private boolean grupoListDiseable = true;
private boolean jornadaListDiseable = true;
// a lot more declarations, and methods incluiding getters & setters not important for the answer...
//Entities Cache
private Map<String, Fase> mapFases = new HashMap<>();
private Map<String, Grupo> mapGrupos = new HashMap<>();
// use to enable the second selectOneMenu
public void setFaseSelectedFinal(String faseSelectedFinal) {
this.faseSelectedFinal = faseSelectedFinal;
this.grupoListDiseable = false;
}
public void setGrupoSelected(String grupoSelected) {
this.grupoSelected = grupoSelected;
this.jornadaListDiseable = false;
}
// methods used to populate the selecOneMenu's
public List<SelectItem> getFases(){
List<SelectItem> fasesList = new ArrayList<>();
fasesList.add(new SelectItem("-- Seleccione Fase --"));
for(Map.Entry<String, Fase> item : mapFases.entrySet()){
fasesList.add(new SelectItem(item.getKey()));
}
return fasesList;
}
public List<SelectItem> getGruposDefase(){
List<SelectItem> grupoList = new ArrayList<>();
grupoList.add(new SelectItem("-- Seleccione Grupo --"));
Fase faseSel = mapFases.get(faseSelectedFinal);
List<Grupo> grupoInt = faseSel.getGrupoList();
for(Grupo grp : grupoInt){
grupoList.add(new SelectItem(grp.getNombre()));
}
return grupoList;
}
public List<SelectItem> getJornadasEnGrupo(){
List<SelectItem> jornadaList = new ArrayList<>();
jornadaList.add(new SelectItem("-- Seleccione Jornada --"));
Grupo grupoSel = mapGrupos.get(grupoSelected);
for(Jornada jor : grupoSel.getJornadaList()){
jornadaList.add(new SelectItem(jor.getNumero().toString()));
}
return jornadaList;
}
}
As you can appreciate I'm using primefaces (3.4), and this panel is rendered in accordance to the value of the "noHayGrupos" boolean variable. This works OK, my problem is the selectOneMenu chain.
After some investigation on my problem, I found a solution. I read a tutorial in this place, the section relative to "Integrated Ajax Support in JSF 2.0" by Marty Hall. The solution was quite simple:
Declare a boolean variable to determinate if a comboBox is selected or not, and ask if a previous combobox has a data valid selected, and that's it, worked every thing. Here the code that I use in the method getGruposDefase():
public List<SelectItem> getGruposDefase() {
List<SelectItem> grupoList = new ArrayList<>();
grupoList.add(new SelectItem("-- Seleccione Grupo --"));
if (!grupoListDiseable && (faseSelectedFinal != null)) {
Fase faseSel = mapFases.get(faseSelectedFinal);
List<Grupo> grupoInt = faseSel.getGrupoList();
for (Grupo grp : grupoInt) {
grupoList.add(new SelectItem(grp.getNombre()));
}
}
return grupoList;
}
The variable grupoListDisable is used to control access to the second comboBox, and faseSelectedFinal, say if you have selected a valid item in the first comboBox. This simple solution makes the code work smoothly. Thanks Marty Hall

JSF2 - what scope for f:ajax elements?

I have this form:
<h:form>
<h:outputText value="Tag:" />
<h:inputText value="#{entryRecorder.tag}">
<f:ajax render="category" />
</h:inputText>
<h:outputText value="Category:" />
<h:inputText value="#{entryRecorder.category}" id="category" />
</h:form>
What I'm trying to achieve: When you type in the "tag" field, the entryRecorder.tag field is updated with what was typed. By some logic upon this action the bean also updates its category field. This change should be reflected in the form.
Questions:
What scope shall I use for EntryRecorder? Request may not be satisfactory for multiple AJAX requests, while session will not work with multiple browser windows per one session.
How can I register my updateCategory() action in EntryRecorder so that it is triggered when the bean is updated?
Answering point 2:
<h:inputText styleClass="id_tag" value="#{entryRecorder.tag}"
valueChangeListener="#{entryRecorder.tagUpdated}">
<f:ajax render="category" event="blur" />
</h:inputText>
Bean:
#ManagedBean
#ViewScoped
public class EntryRecorder {
private String tag;
private String category;
#EJB
private ExpenseService expenseService;
public void tagUpdated(ValueChangeEvent e) {
String value = (String) e.getNewValue();
setCategory(expenseService.getCategory(value));
}
}
Number 1, anybody?
To point 1, I'll use Request since there is no need to use View and Session is, as you well pointed, completely unnecessary.
For point 2, since you are using <f:ajax/> I suggest making full use of it. Here is my proposal:
xhtml:
<h:form>
<h:outputText value="Tag:" />
<h:inputText value="#{entryRecorder.tag}">
<f:ajax render="category" event="valueChange"/>
</h:inputText>
<h:outputText value="Category:" />
<h:inputText value="#{entryRecorder.category}" id="category" />
</h:form>
Note the use of valueChange event instead of blur (not that blur doesn't work but I find valueChange more 'proper' for a value holder component).
bean:
#ManagedBean
#RequestScoped
public class EntryRecorder {
private String tag;
private String category;
public String getCategory() {
return category;
}
public String getTag() {
return tag;
}
public void setCategory(String category) {
this.category = category;
}
public void setTag(String tag) {
this.tag = tag;
tagUpdated();
}
private void tagUpdated() {
category = tag;
}
}
Unless you really want the tagUpdated method executed only when tag is updated through the view, my proposal looks more clear. You don't have to deal with the events (nor casting) and the tagUpdated method can be private hiding it's functionality from possible misuses.

Resources