I have created a composite component around primefaces 5.1 datatable:
The component without AJAX (selectorServicio2.xhtml)
<ui:component
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:cc="http://java.sun.com/jsf/composite"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:p="http://primefaces.org/ui"
xmlns:pe="http://primefaces.org/ui/extensions">
<cc:interface componentType="selectorServicio2">
<cc:attribute name="servicios" required="true" type="java.util.Collection"
shortDescription="Colección de servicios entre los que se buscarán los datos"/>
<cc:attribute name="seleccionSimpleAttr" type="es.imasmallorca.selene.model.prestacion.Servicio"/>
<cc:attribute name="seleccionMultipleAttr" type="java.util.List"/>
<cc:attribute name="seleccionMultiple" required="false" type="java.lang.Boolean" default="false"/>
<cc:attribute name="scrollHeight" required="false" type="java.lang.Integer"/>
</cc:interface>
<cc:implementation>
<p:dataTable id="selectorServicio" widgetVar="#{cc.widgetId}"
value="#{cc.attrs.servicios}" var="_servicio"
scrollable="#{not empty cc.attrs.scrollHeight}" scrollHeight="#{cc.attrs.scrollHeight}"
selectionMode="#{cc.attrs.seleccionMultiple ? 'multiple' : 'single'}"
selection="#{cc.attrs.seleccionMultiple ? cc.attrs.seleccionMultipleAttr : cc.attrs.seleccionSimpleAttr}" rowKey="#{_servicio.codigo}">
<p:column>
<h:outputText value="#{_servicio.entidad.nombre}"/>
</p:column>
<p:column>
<h:outputText value="#{_servicio.tipoServicio.nombre}"/>
</p:column>
</p:dataTable>
</cc:implementation>
</ui:component>
It has a backing class (SelectorServicio2.java):
#FacesComponent("selectorServicio2")
public class SelectorServicio2 extends UIInput implements NamingContainer {
private static final Logger log = Logger.getLogger(SelectorServicio2.class.getName());
private static final String ID_SERVICIOS_SELECCIONADOS = "ServiciosSeleccionados";
#Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
public void encodeBegin(FacesContext facesContext) throws IOException {
#SuppressWarnings("unchecked")
List<Servicio> servicios = (List<Servicio>) this.getAttributes().get("servicios");
if (servicios == null) {
servicios = new ArrayList<>();
}
Collections.sort(servicios, new ServicioPorNombreEntidadTipoServicio(Criterio.ENTIDAD_TIPOSERVICIO));
getStateHelper().put("servicios", servicios);
getStateHelper().put(ID_SERVICIOS_SELECCIONADOS, new ArrayList<Servicio>());
super.encodeBegin(facesContext);
}
#Override
public Object getValue() {
ArrayList<Servicio> servicios = new ArrayList<>();
Servicio servicio = (Servicio) this.getStateHelper().get("servicio");
if (servicio != null) {
servicios.add(servicio);
}
return servicios;
}
public String getWidgetId() {
return "wdgSelectorServicio2_" + this.getClientId().replace(":", "_");
}
public void setServicioSeleccionado(Servicio servicioSeleccionado) {
if (servicioSeleccionado == null) {
this.getStateHelper().remove("servicio");
} else {
this.getStateHelper().put("servicio", servicioSeleccionado);
}
}
public Servicio getServicioSeleccionado() {
return (Servicio) this.getStateHelper().get("servicio");
}
}
This works as expected (text.xhtml):
<!DOCTYPE html>
<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"
xmlns:p="http://primefaces.org/ui"
xmlns:imas="http://java.sun.com/jsf/composite/components">
<h:head></h:head>
<h:body>
<h:form>
<imas:selectorServicio2 id="serviceSelector"
servicios="#{testBean.services}"
seleccionSimpleAttr="#{testBean.selectedService}"
scrollHeight="110">
</imas:selectorServicio2>
</h:form>
</h:body>
</html>
Now, to add Ajax support, I do:
Add <cc:clientBehavior name="servicioSeleccionado" event="rowSelect" targets="selectorServicio"/> to the cc:interface of selectorServicio2.xhtml.
Make SelectorServicio2 implement ClientBehaviorHolder (strangely enough, I did not need to implement any method, but got no issues compiling the class).
Add the ajax tag to test.xhtml:
<imas:selectorServicio2 id="serviceSelector"
servicios="#{testBean.services}"
seleccionSimpleAttr="#{testBean.selectedService}"
scrollHeight="110">
<f:ajax event="servicioSeleccionado" listener="#{testBean.selectService}"/>
</imas:selectorServicio2>
Add the selectService() method to TestBean.java:
public void selectService() {
log.warning("SERVICIO SELECCIONADO !!!!");
}
After those changes, I get a NullPointerException:
java.lang.NullPointerException at
com.sun.faces.facelets.tag.jsf.core.AjaxHandler.applyAttachedObject(AjaxHandler.java:333)
at
com.sun.faces.facelets.tag.jsf.core.AjaxHandler.applyNested(AjaxHandler.java:258)
at
com.sun.faces.facelets.tag.jsf.core.AjaxHandler.apply(AjaxHandler.java:182)
at
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
at
com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler.applyNextHandler(CompositeComponentTagHandler.java:183)
at
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:203)
at
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
at
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
at
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:203)
at
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
at
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
at
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:203)
at
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
at
javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:95)
at
com.sun.faces.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:93)
at
com.sun.faces.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:87)
at
com.sun.faces.facelets.impl.DefaultFacelet.apply(DefaultFacelet.java:161)
at
com.sun.faces.application.view.FaceletViewHandlingStrategy.buildView(FaceletViewHandlingStrategy.java:995)
at
com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:99)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101) at
com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:219)
What am I doing wrong?
I am using Wildfly 8.1 with standard configuracion (JSF 2.2) on Java 7
java.lang.NullPointerException
at com.sun.faces.facelets.tag.jsf.core.AjaxHandler.applyAttachedObject(AjaxHandler.java:333)
Let's check the source of AjaxHandler#applyAttachedObject():
332 Collection<String> eventNames = bHolder.getEventNames();
333 if (!eventNames.contains(eventName)) {
334 throw new TagException(this.tag,
335 getUnsupportedEventMessage(eventName, eventNames, parent));
336 }
Aha, getEventNames() returned null. That method was implemented on UIComponentBase whose javadoc says the following:
This is a default implementation of ClientBehaviorHolder.getEventNames(). UIComponent does not implement the ClientBehaviorHolder interface, but provides default implementations for the methods defined by ClientBehaviorHolder to simplify subclass implementations. Subclasses that wish to support the ClientBehaviorHolder contract must declare that the subclass implements ClientBehaviorHolder, and must override this method to return a non-Empty Collection of the client event names that the component supports.
Technically, you should be overriding getEventNames() in your component returning a collection of supported event names.
However, you actually didn't need it here. The <cc:clientBehavior> is already retargeting it on the <p:dataTable> component nested in the composite, which has that interface and method already properly implemented. So you should just remove the ClientBehaviorHolder interface from the backing component.
Moreover, <f:ajax> wouldn't work on PrimeFaces specific events, you should be using <p:ajax> instead. It will render a script which in turn invokes PrimeFaces-specific ajax client API.
Related
I'm involved in a migration of a Java Web Application from Primefaces 6.2 to Primefaces 8.0.
After resolving all issues as per official migration guides the application works properly except for the behavior on a p:menubutton component.
It regards a xhtml that produce a datatable where a column contains the DefaultMenuItem objects rendered by the attribute model on p:menubutton component.
ISSUE:
First time I invoke one of the action setted into each DefaultMenuItem it works. The action opens a dialog but closing the dialog opened without doing a new submit, the second invocation doesn't produce any output. I can evaluate ajax request into the network tab of the browser console and nothing sounds wrong, it is equals to the first call.
There're no errors in server console neither in browser console.
This wasn't happening on Primefaces 6.2.
All the components involved are the same before and after the migration.
Code as follow:
list.xhtml
the ui:composition that contains the p:menubutton component
<ui:composition>
<h:form id="someForm" enctype="multipart/form-data">
<p:menuButton
model="#{SomeBean.model}"
id="idmenubutton"
icon="fa fa fa-ellipsis-v"
menuStyleClass="response-single-style"
title="some_title"
disabled="false"/>
</h:form>
</ui:composition>
dialog.xhtml
the ui:composition rendered after invoke actions setted on each DefaultMenuItem
<ui:composition>
<p:importEnum type="some.path.SomeEnum" var="someEnum"/>
<p:dialog id="idDlgGeneric" header="some_header" styleClass="generic_pnlMaxWidth95"
widgetVar="dlgGeneric" modal="true" onShow="dlgOnShow(this.jqId);onShowForClosableDlg('dlgGeneric')"
responsive="true" fitViewport="true" showEffect="fade" hideEffect="fade" resizable="false" closable="true">
<p:panel rendered="true">
<p:panelGrid layout="grid" columns="2" columnClasses="ui-grid-col-6,ui-grid-col-6" styleClass="ui-panelgrid-blank">
<p:commandButton id="saveDlg" icon="fa fa-pencil" value="Save" action="#{SomeOtherBean.save}" styleClass="generic_width100"/>
<p:commandButton icon="fa fa-times" value="Close" action="#{SomeBean.destroyBeanViewScope('SomeOtherBean')}" onclick="PF('dlgGeneric').hide()" styleClass="generic_width100 uniqueCloseButton"/>
<p:defaultCommand target="#form:saveSomeOtherBean" scope="#form:idDlgGeneric" />
</p:panelGrid>
</p:panel>
</p:dialog>
</ui:composition>
SomeBean.java
the class that initialize the model used into p:menubutton component
#Named(ConstantsWeb.MBean.SOME_BEAN)
#ViewScoped
public class SomeBean extends AbstractBean {
private transient MenuModel model;
public MenuModel initModel() {
model = new DefaultMenuModel();
final DefaultMenuItem title = DefaultMenuItem.builder()
.value("value_description")
.disabled(true)
.style("opacity:1;font-weight:bold;")
.build();
model.getElements().add(title);
for (final SomeEnum resp : responsSingle) {
Map<String, List<String>> map = new LinkedHashMap<String, List<String>>();
List<String> list = new ArrayList<String>();
list.add(String.valueOf(resp.getId()));
map.put("respId", list);
final DefaultMenuItem menuItem = DefaultMenuItem.builder()
.value(resp.getDisplayName())
.command("#{SomeBean.someMethod}")
.params(map)
.onstart("PF('statusDialog').show();")
.onsuccess("PF('statusDialog').hide();")
.update(someForm:idDlgGeneric)
.icon(resp.getIcon())
.disabled(false)
.build();
model.getElements().add(menuItem);
}
model.generateUniqueIds();
}
} else {
model = null;
}
return model;
}
public void someMethod() {
}
public void destroyBeanViewScope(final String... yourBeanArray) {
FacesHelper.destroyBeanViewScope(yourBeanArray);
renderResponse = false;
FacesHelper.update(someForm:idDlgGeneric);
}
}
Anyone could help me?
I am newbie to JSF/Java ee7 and tried to test some features to understand how things work.
I used templating for testing purposes. Here is the relevant JSF template client:
<ui:define name="content">
<h:panelGroup class="keret" id="tartalom">
<h:form id="email" rendered="#{emailManagedBean.urlap}">
<h:messages/>
<h:outputLabel for="emailbox" value="Add meg az email címed, és juss hozzá bestsellerünkhöz 5Ft-ért"/>
<h:inputText value="#{emailManagedBean.newEmail.email}"/>
<h:commandButton actionListener="#{emailManagedBean.saveEmail()}" value="Mentés">
<f:ajax immediate="true" execute="#form" render=":tartalom"/>
</h:commandButton>
</h:form>
<h:panelGroup id="szoveg" rendered="#{not emailManagedBean.urlap}">
<h3>Köszönjük!</h3>
<p>
Hamarosan emailt fogsz kapni tőlünk. Kérlek, ellenőrizd a levélszemét, illetve spam mappákban is a tőlünk kapott levelet. A levlében lévő linkre kattintva hozzájuthatsz az ajándékodhoz.
</p>
</h:panelGroup>
</h:panelGroup>
</ui:define>
My managed bean:
#Named(value = "emailManagedBean")
#ViewScoped
public class emailManagedBean implements Serializable {
#EJB
EmailsFacadeLocal emailFacade;
private Emailcamp newEmail;
private boolean urlap;
public Emailcamp getNewEmail() {
return newEmail;
}
public void setNewEmail(Emailcamp newEmail) {
this.newEmail = newEmail;
}
#PostConstruct
public void init() {
newEmail = new Emailcamp();
urlap=true;
}
public boolean isUrlap() {
return urlap;
}
public void setUrlap(boolean urlap) {
this.urlap = urlap;
}
public void validateEmail(FacesContext context, UIComponent comp,
Object value) {
String input = (String) value;
EmailValidator validator = EmailValidator.getInstance();
if (!validator.isValid(input)) {
((UIInput) comp).setValid(false);
FacesMessage message = new FacesMessage(
"Helytelen email cím formátum");
context.addMessage(null, message);
} else {
if(!emailFacade.exist(newEmail.getEmail())){
((UIInput) comp).setValid(true);
}
else {
context.addMessage(null, new FacesMessage("Már megadtad korábban az email címed"));
}
}
}
public void saveEmail() {
/*Get current date and time*/
Calendar c = Calendar.getInstance();
newEmail.setRecemaildate(c.getTime());
/*Get Referral*/
HttpServletRequest hr = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
newEmail.setReferer((hr.getHeader("referer")==null) ? "not available" : hr.getHeader("referer") );
newEmail.setStatus(Status.NOT_SENT.toString());
emailFacade.create(newEmail);
urlap=false;
}
}
The expected behavior is that the init function set urlap boolean variable to true so the h:form should be shown as there is conditional rendering on the view side.
Later the customer clicks on Save ("Mentés") and the new email address and some data will be stored in a database. After the ajax call I urlap boolean varaible is set to false not to render the form again but render a thank you message.
Unfortunately the init function is never called so the boolean variable never set to true. As a consequences my thanks message is shown without collecting the email.
The second strange thing which is the consequence of the ajax component (I guess) that I got an error message: outputScript with no library, no name, and no body content. I did not declare any outputScript in my xhtml.
I am writting a simple user registration process.What I want to do is that when the user enters a username, I want to check if the username has already been taken by someone else. If it has been, then I want to show a message under the input field, asking the user to choose a different username.
I have already written the database query/ejb/rest etc...
What I want to know is ... How do I make a call to the backend (ejb/rest) and show the message on the screen without having to make a full round trip to the server and refreshing the entire page?
Update 1:
I have changed my code to look like the example from Simon.(Don't know why it did not appear before).
<h:form>
<h:panelGrid columns="2">
<h:outputLabel for="username" value="Please enter a user name :" />
<h:inputText id="username" value="#{userNameCheckBean.signInName}"
autocomplete="off" size="20" onfocus="blankOut();" required="true"
requiredMessage="Error: Username is required." maxlength="25">
<f:ajax execute="#this" render="message" listener="#{userNameCheckBean.verifyIfUsernameIsAlreadyTaken()}" />
</h:inputText>
<h:outputText rendered="#{userNameCheckBean.userExists}" id="message" value="#{userNameCheckBean.message}" />
</h:panelGrid>
<h:commandButton id="checkUser" action="#{userNameCheckBean.userExists}" value="Check">
</h:commandButton>
</h:form>
The confusing thing for me is, How do I call the verifyIfUsernameIsAlreadyTaken() method when the username inputText looses focus? and how do I update just the bit of the overall form? Is it a must to have a commandButton to submit the username field? I thought ajax was meant to make server calls in the background.
Update 2:
I have changed the .xhtml file to the one given below. I still have to click on the button to call the backing bean. It simply ignores the event. Any help will be much appriciated.
<h:form>
<h:panelGrid columns="2">
<h:outputLabel for="username" value="Please enter a user name :" />
<h:inputText id="username" value="#{userNameCheckBean.signInName}" autocomplete="off" size="20" onfocus="blankOut();" required="true" requiredMessage="Error: Username is required." maxlength="25">
<f:ajax execute="#this" event="blur" render="message" listener="#{userNameCheckBean.checkIfUsernameIsAlreadyTaken()}" />
</h:inputText>
<h:panelGroup>
<h:outputText rendered="#{userNameCheckBean.userExists}" id="message" value="#{userNameCheckBean.message}" />
</h:panelGroup>
</h:panelGrid>
<h:commandButton id="checkUser" action="#{userNameCheckBean.checkIfUsernameIsAlreadyTaken}" value="Check">
</h:commandButton>
</h:form>
Update 3:
Here is backing bean that works when I click on the Button.
package co.uk.dakia.beans;
import java.io.IOException;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.event.AjaxBehaviorEvent;
import javax.naming.NamingException;
import javax.validation.constraints.NotNull;
import example.UserManager;
import example.ServiceLocator;
#ManagedBean
#RequestScoped
public class UserNameCheckBean {
private static Logger logger = Logger.getLogger(UserNameCheckBean.class.getName());
#EJB(beanName = "UserManager", mappedName = "jndi:ext://example/UserManagerRemote")
UserManager userManager;
String signInName;
String password;
String message = "Message: ";
boolean userExists = false;
public void checkIfUsernameIsAlreadyTaken(AjaxBehaviorEvent event) throws NamingException, IOException {
userManager = (UserManager) ServiceLocator.locateService("UserManagerRemote");
logger.info("Checking if username [" + signInName + "] has already been taken by some other user");
userExists = userManager.userExists(signInName);
if(userExists) {
message = "Username is already taken!";
} else {
message = "";
logger.info("Username [" + signInName + "] is available.");
}
}
public void checkIfUsernameIsAlreadyTaken() throws NamingException, IOException {
checkIfUsernameIsAlreadyTaken(null);
}
public String getSignInName() {
return signInName;
}
public void setSignInName(String signInName) {
this.signInName = signInName;
}
public String getMessage() throws NamingException, IOException {
checkIfUsernameIsAlreadyTaken();
return message;
}
public void setMessage(String message) {
this.message = message;
}
public boolean isUserExists() {
return userExists;
}
public void setUserExists(boolean userExists) {
this.userExists = userExists;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
You did not provide any code so it is hard to tell you exactly what to do, but here's a template (assuming you're using JSF 2):
.xhtml:
<h:form>
...
<h:inputText id="username" value="#{myBean.username}">
<f:ajax execute="#this" render="username-already-taken-wrapper" listener="#{myBean.verifyIfUsernameIsAlreadyTaken()}"/>
</h:inputText>
<h:panelGroup id="username-already-taken-wrapper">
<h:outputText rendered="#{myBean.usernameAlreadyTaken}" value="Username is already taken!"/>
</h:panelGroup>
...
</h:form>
MyBean.java:
public void verifyIfUsernameIsAlreadyTaken()
{
usernameAlreadyTaken = verify(username);
}
private boolean verify(String username)
{
...
}
The trick is to use AJAX to submit only a part of your form (in this case: the username), and refresh only a part of the page (in this case: username-already-taken-wrapper). If the username is already taken, the message will show. If not, nothing will show.
I'm trying to invoke a <f:ajax listener> method on change of <h:selectOneMenu>.
This is the view:
<h:selectOneMenu label="verteilerlisten"
value="#{smsBean.verteilerliste}" id="verteilerlisten">
<f:ajax listener="#{smsBean.verteilerlistenChanged}"
render="verteilerlisten" />
<f:selectItems value="#{verteilerlisten.list}"
var="v" itemLabel="#{v.name}" itemValue="#{v.verteilerlistennummer}" />
</h:selectOneMenu>
This is the #{smsBean}:
public void verteilerlistenChanged(AjaxBehaviorEvent e) {
System.out.println("success!");
throw new RuntimeException("Success!");
}
The items are displayed correctly, but nothing happens when I change the item in the select box. How is this caused and how can I solve it?
EDITED:
I created a blank xhtml file (test.xhtml) with the following code:
<?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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head />
<h:body>
<h:form id="smsform">
<h:selectOneMenu label="verteilerlisten" value="#{smsBean.verteilerliste}"
id="verteilerlisten">
<f:selectItems value="#{verteilerlistenBean.list}" var="v"
itemLabel="#{v.name}" itemValue="#{v.verteilerlistennummer}"/>
<f:ajax listener="#{smsBean.verteilerlistenChanged}" render="smsform"/>
</h:selectOneMenu>
</h:form>
</h:body>
</html>
This is the VerteilerlistenBean.java:
#ManagedBean(name= "verteilerlistenBean")
#ApplicationScoped
public class VerteilerlistenBean {
private Logger logger = Logger.getLogger(VerteilerlistenBean.class);
private List<Verteilerliste> list = new ArrayList<Verteilerliste>();
public VerteilerlistenBean() {
}
public List<Verteilerliste> getList() {
// return list;
logger.info("Getter invoked");
List<Verteilerliste> list = new ArrayList<Verteilerliste>();
list.add(new Verteilerliste(1, "Liste 1"));
list.add(new Verteilerliste(2, "Liste 2"));
list.add(new Verteilerliste(3, "Liste 3"));
list.add(new Verteilerliste(4, "Liste 4"));
list.add(new Verteilerliste(5, "Liste 5"));
return list;
}
public void setList(final List<Verteilerliste> parList) {
list = parList;
}
}
And this is the smsBean (SendSMSFormBean.java):
#ManagedBean (name="smsBean")
#ViewScoped
public class SendSMSFormBean implements Serializable {
private String absender;
private Verteilerliste verteilerliste;
private Textbaustein textbaustein;
public SendSMSFormBean() {
}
public String getAbsender() {
return absender;
}
public void setAbsender(final String parAbsender) {
absender = parAbsender;
}
public Verteilerliste getVerteilerliste() {
return verteilerliste;
}
public void setVerteilerliste(final Verteilerliste parVerteilerliste) {
verteilerliste = parVerteilerliste;
}
public Textbaustein getTextbaustein() {
return textbaustein;
}
public void setTextbaustein(final Textbaustein parTextbaustein) {
textbaustein = parTextbaustein;
}
public void verteilerlistenChanged(AjaxBehaviorEvent e) {
System.out.println("Success!");
}
}
These are my libs I use:
(I can't upload image -.- )
javassist-3.15.0-GA.jar
javax.faces-2.0.11.jar
javax.servlet-5.1.12.jar
javax.servlet-api-3.0.1.jar
javax.servlet.jar
javax.servlet.jsp.jstl-1.2.1.jar
EDITED 2
I cleaned my classpath lib so that all these classes aren't inside there anymore (it uses the libs from tomcat now). I also added a in the plain example (i got a in the main example). But still everytime I change the value in the selectBox in the console appears:
2014-01-21 16:03:48 INFO VerteilerlistenBean:44 - Getter invoked
EDITED 3
When changing f:ajax listener attribute to "#{asdf.asdf}. No error was shown! Before that I tried #{smsBean.verteilerlistenChanged} and ${smsBean.verteilerlistenChanged}
EDITED 4
So now I think I got the mistake:
<div class="ym-fbox">
<h:selectOneMenu label="verteilerlisten" id="verteilerlisten">
<f:selectItems value="#{testbean.list}" var="v" itemLabel="#{v.name}" itemValue="#{v.id}"/>
<f:selectItem itemLabel="Manuelle Eingabe" itemValue="man"/>
<f:ajax listener="#{testbean.beep}" render="smsform"/>
</h:selectOneMenu>
</div>
<div class="ym-fbox">
<h:selectOneMenu label="verteilerlisten1" id="verteilerlisten1" value="#{smsBean.verteilerliste}">
<f:selectItems value="#{verteilerlistenBean.verteilerlisten}" var="liste" itemLabel="#{liste.name}" itemValue="#{liste.verteilerlistennummer}" />
<f:selectItem itemLabel="Manuelle Eingabe" itemValue="man"/>
<f:ajax listener="#{testbean.beep}" render="smsform"/>
</h:selectOneMenu>
</div>
The upper one invokes testbean.beep when changed the other one doesn't! It would work if you remove attribute "value" in selectOneMenu. I don't know why JSF does this or why it doesn't show me any error..
But that doesn't solve my problem: I have 2 Beans. One is a Bean, that safes the inputs in the form (smsBean). The select Value should be saved in this bean. The other bean is something like a RessourceManager, which returns the values that the user can select in the box.
How do I make this?
I use Richfaces. I am facing a situation where I have a form and 2 buttons. On click of one of the buttons, say button1, I need the validations. On click of another, button2, I dont. The issue arises when I click on the button1 and I get all validation error messages. This prevents clicking of button2. I tried ajaxSingle, immediate, which help in bypassing the validations but bean values are not updated. How can I achieve that ?
Code :
<h:inputSecret value="#{bean.value} redisplay="true"/>
<a4j:commandButton immediate="true" actionListener="#{bean.actionListener} reRender="ID_OF_PANEL_SURROUNDING_THIS_CODE"/>
Bean :w
public class bean {
.
.
.
public void actionListener(ActionEvent e) {
value = "New value";
}
.
.
.
}
Set immediate attribute to true in your second button.
Immediate attribute skips validation of your values but using immediate for anything other than the trivial case of a cancel button is problematic. If your button2 is some kind of cancel button there is no problem.
If you need to implement something more complex than a cancel button there are some solutions to these problems which you can find in below article.
How_The_Immediate_Attribute_Works
What is actually possible to do for such case is writing a custom validator and do the relevant checks in it.
conditionalValidation.xhtml:
Please notice the h:inputHidden that agregate ids and trigger custom validator to be invoked.
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:c="http://java.sun.com/jstl/core"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
template="/WEB-INF/template/default.xhtml">
<ui:param name="title" value="Conditional Validation"/>
<ui:param name="bean" value="#{conditionalValidationBean}"/>
<ui:define name="content">
<a4j:form id="frmConditionalValidation">
<h:panelGrid columns="2">
<h:outputLabel for="txtValue1" value="Value 1"/>
<h:panelGroup>
<h:inputText id="txtValue1" value="#{bean.value1}"/>
<h:message for="txtValue1"/>
</h:panelGroup>
<h:outputLabel for="txtValue2" value="Value 2"/>
<h:panelGroup>
<h:inputText id="txtValue2" value="#{bean.value2}"/>
<h:message for="txtValue2"/>
</h:panelGroup>
</h:panelGrid>
<h:inputHidden id="inpDetailsProvider" required="true"
validator="#{bean.conditionalValidator}" value="1">
<f:attribute name="forceValidationBtnId" value="btnForceValidation"/>
<f:attribute name="value1InputId" value="txtValue1"/>
<f:attribute name="value2InputId" value="txtValue2"/>
</h:inputHidden>
<h:commandButton id="btnForceValidation" value="Force Validation"/>
<h:commandButton id="btnByPassValidation" value="Bypass Validation"/>
</a4j:form>
</ui:define>
</ui:composition>
ConditionalValidationBean.java
public class ConditionalValidationBean {
private String value1;
private String value2;
#SuppressWarnings("unused")
public void conditionalValidator(FacesContext context, UIComponent component, Object value) {
String forceValidationBtnId = (String) component.getAttributes().get("forceValidationBtnId");
String value1InputId = (String) component.getAttributes().get("value1InputId");
String value2InputId = (String) component.getAttributes().get("value2InputId");
UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
UICommand forceValidationBtn = findComponent(viewRoot, forceValidationBtnId, UICommand.class);
Map<String, String> params = context.getExternalContext().getRequestParameterMap();
boolean forceValidation = params.containsKey(forceValidationBtn.getClientId(context));
if(forceValidation) {
UIInput value1Input = findComponent(viewRoot, value1InputId, UIInput.class);
UIInput value2Input = findComponent(viewRoot, value2InputId, UIInput.class);
String v1 = (String) value1Input.getValue();
if(v1 == null || v1.trim() == "") {
value1Input.setValid(false);
FacesMessage valueRequiredMsg = new FacesMessage("Value required");
context.addMessage(value1Input.getClientId(context), valueRequiredMsg);
}
// Rest of validation logic.
}
}
public static <T> T findComponent(UIComponent base, String id, Class<T> returnType) {
if (id.equals(base.getId())) {
return returnType.cast(base);
}
Iterator<UIComponent> children = base.getFacetsAndChildren();
while (children.hasNext()) {
T found = findComponent(children.next(), id, returnType);
if (found != null) {
return returnType.cast(found);
}
}
return null;
}
public String getValue1() {
return value1;
}
public String getValue2() {
return value2;
}
public void setValue1(String value1) {
this.value1 = value1;
}
public void setValue2(String value2) {
this.value2 = value2;
}
}