I updated the PrimeFaces version to 6.2. Everything went good, except for an issue with CDI conversations management.
After the update, it seems like the conversation is initialized at the first access to a page, but it is lost just before the first call to the controller. In fact, when accessing to a page a new conversation is been initialized (and this is correct). But after the first call to a controller method a new conversation is initialized, losing the previous one and all its parameter values. The following calls maintain the last conversation as usual.
With PrimeFaces 6.1 (and previous) this never happened.
Is this a bug or a lack of mine?
After the access to the page, the text displayed is "Init value with conversation id = 1".
By clicking the button, the displayed text changes to "Value set with conversation id = 2".
The following clicks mantain the cid = 2.
Controller
#Named
#ConversationScoped
#LoggedIn
public class TestController implements Serializable {
static final long serialVersionUID = 69823746898775L;
#Inject protected Conversation conversation;
#Inject protected FacesContext facesContext;
rivate String outputValue;
#PostConstruct
public void init() throws IOException {
if (conversation.isTransient()) {
conversation.begin();
conversation.setTimeout(1800000);
}
outputValue = "Init value with conversation id = " + conversation.getId();
}
public void handleCall() {
outputValue = "Value set with conversation id = " + conversation.getId();
}
public String getOutputValue() {
return outputValue;
}
public void setOutputValue(String outputValue) {
this.outputValue = outputValue;
}
}
JSF page
<?xml version="1.0" encoding="UTF-8"?>
<f:view xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui" contentType="text/html">
<html>
<h:head>
<meta http-equiv="X-UA-Compatible" content="IE=8" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="shortcut icon" type='image/x-icon' href='favicon.ico'/>
<ui:insert name="head" />
</h:head>
<h:body onload="javascript: if ('#{param.sessionExpired}'.length != 0) PF('loginDialog').show()">
<h:form id="testForm">
<p:commandButton action="#{testController.handleCall()}" value="Make a call" update="#form" />
<h:outputText value="#{testController.outputValue}" />
</h:form>
</h:body>
</html>
</f:view>
Related
I am making a search by id and in my home page I have an inputText and a commandButton. When I click the commandButton, it has to set the id with the number passed in the inputText and redirect me to the map page to track coordinates referring to this id. As I have to access the ManagedBean to set the Id variable, I have to redirect to the map page through a method which returns a String with the "outcome" of the page address. The problem is that the map is not rendering when the page is loaded. If the map page is accessed normally, without the outcome coming from the bean, the map loads correctly, then its not API key problem or wrong coordinates. Does anyone know what is happening or know any other solution?
Home page:
<ui:composition template="/WEB-INF/template/LayoutPadrao.xhtml"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<ui:define name="titulo">Dashboard</ui:define>
<ui:define name="corpo">
<h:form>
<p:panel
style="margin-top:0px;margin-left:450px;width:480px;border: none !important;">
<p:panelGrid id="grid2" columns="2" styleClass="semBorda">
<p:inputText id="pesquisarGado" size="50" style="height:25px;"
value="#{gadoBean.gadoBeanM.name}" required="true"
requiredMessage="Digite o código do gado" />
<p:commandButton action="#{gadoBean.outcome()}"/>
<p:button outcome="gado/rastrearGado"/>
<p:message for="pesquisarGado" display="icon" />
</p:panelGrid>
</p:panel>
</h:form>
</ui:define>
</ui:composition>
ManagedBean:
#Named
#SessionScoped
//attributes and other methods
public String outcome() {
return "gado/rastrearGado";
}
public void inicializar() {
GravarPosicao();//method to record coo in the DB
obterPosicaoPeloId();
}
public void obterPosicaoPeloId() {
mapa = new DefaultMapModel();
tagsCadastradas = new ArrayList<Tag>();
tagsCadastradas = tagsRep.listarTag();
this.getTagsCadastradas().size();
posicoes = new ArrayList<Coordenadas>();
ultimasPosicoes = new ArrayList<Coordenadas>();
String id = this.getGadoBeanM().getName();
cooPorId = coordenadasRep.listarCoords(Long.parseLong(id));
System.out.println("posicoes: " + cooPorId.getPosData());
System.out.println("id: " + id);
coord = new LatLng(cooPorId.getPosLatitude(), cooPorId.getPosLongitude());
mapa.addOverlay(new Marker(coord, "Gado"));
lastLong = (float) cooPorId.getPosLongitude();
lastLat = (float) cooPorId.getPosLatitude();
center = lastLat + "," + lastLong;
System.out.println(center);
}
Map page:
<?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:p="http://primefaces.org/ui"
xmlns:f ="http://java.sun.com/jsf/core" xmlns:o="http://omnifaces.org/ui">
<h:head>
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDeF32jO4j5mAORJTcNICT3o8Nz8G0QZIg"
type="text/javascript"></script>
</h:head>
<title>Rastreamento</title>
<f:metadata>
<f:event listener="#{gadoBean.inicializar}" type="preRenderView" update="#form"/>
</f:metadata>
<h:body>
<h:form>
<p:panel id="panelMap" style="margin-top:100px;margin-left:410px;width:508px;">
<p:gmap id="mapa"
center="#{gadoBean.center}"
zoom="9"
model="#{gadoBean.mapa}"
type="HYBRID"
style="width:600px;height:400px"/>
</p:panel>
</h:form>
</h:body>
</html>
When you use commandButton to navigate, you have to use h:commandButton (JSF) not p:commandButton (primefaces).
I am trying to get a static variable in my JSF page.
I followed instructions on this post. I am able to get the variables using the Primefaces extension, however, I am not getting anything in the xhtml when doing the following.
I have a constants file:
public class Test {
public static final String NAME = "EL Test";
}
And following the post by balusC, I added an application scoped bean (however, this is being called with every request):
import java.lang.reflect.Field;
import javax.annotation.PostConstruct;
import javax.el.ELContextEvent;
import javax.el.ELContextListener;
import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.context.FacesContext;
#ManagedBean(eager = true)
#ApplicationScoped
public class Config {
#PostConstruct
public void init() {
FacesContext.getCurrentInstance().getApplication().addELContextListener(new ELContextListener() {
#Override
public void contextCreated(ELContextEvent event) {
event.getELContext().getImportHandler().importClass("my.package.constants.Test");
Class<?> clazz = event.getELContext().getImportHandler().resolveClass("Test");
for (Field field : clazz.getFields()) {
System.out.println(field.getName());
}
System.out.println("clazz = " + clazz);
System.out.println(clazz.getPackage());
}
});
}
}
And my xhtml page:
<!DOCTYPE html >
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<h:head>
<meta charset="utf-8"></meta>
<meta http-equiv="X-UA-Compatible" content="IE=edge"></meta>
<meta name="viewport" content="width=device-width, initial-scale=1"></meta>
</h:head>
<h:body>
<h:outputText value="#{Test}"></h:outputText>
<h:outputText value="#{Test.NAME}"></h:outputText>
</h:body>
</html>
Is there anything I am missing?
p:importConstants was added in PrimeFaces 6.x.
XHTML:
<p:importConstants type="com.example.Constants" var="Constants" />
<h:outputText value="#{Constants.TEST}" />
Java:
package com.example;
public class Constants {
public final static String TEST = "Imported Constant";
}
JSF 2.3 supports referencing static variables in EL using the f:importConstants tag.
Your constants file
public class Test {
public static final String NAME = "EL Test";
}
can be imported in the view by adding the following metadata.
<f:metadata>
<f:importConstants type="mypackage.Test" />
</f:metadata>
And then be referenced using EL.
#{Test.NAME}
So your view becomes:
<!DOCTYPE html >
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<f:metadata>
<f:importConstants type="mypackage.Test" />
</f:metadata>
<h:head>
<meta charset="utf-8"></meta>
<meta http-equiv="X-UA-Compatible" content="IE=edge"></meta>
<meta name="viewport" content="width=device-width, initial-scale=1"> </meta>
</h:head>
<h:body>
<h:outputText value="#{Test.NAME}"></h:outputText>
</h:body>
</html>
Source: Arjan Tijms' Weblog.
You can use o:importConstants by omnifaces for that
For example:
public class Foo {
public static final String FOO1 = "foo1";
public static final String FOO2 = "foo2";
}
public interface Bar {
public String BAR1 = "bar1";
public String BAR2 = "bar2";
}
public enum Baz {
BAZ1, BAZ2;
}
The constant field values of the above types can be mapped into the request scope as follows:
<o:importConstants type="com.example.Foo" />
<o:importConstants type="com.example.Bar" />
<o:importConstants type="com.example.Baz" var="Bazzz" />
...
#{Foo.FOO1}, #{Foo.FOO2}, #{Bar.BAR1}, #{Bar.BAR2}, #{Bazzz.BAZ1}, #{Bazzz.BAZ2}
As I see you're using JSF 2, you could go with the Omnifaces library:
public class Test {
public static final String NAME = "EL Test";
}
Then in the facelet:
<o:importConstants type="com.example.Test " />
#{Test.NAME}
Otherwise, if you want to avoid using third party libraries, use an #ApplicationScoped managed bean with a getter for this aim:
#ManagedBean
#ApplicationScoped
public class Test{
public static final String name = "EL Test";
public String getName(){
return name;
}
}
Which you can reference with:
#{test.name}
See also:
The importConstants tag
I'm having trouble understanding why a action method on my ConversationScope'd bean doesnt fire. The bean is:
package org.work;
import java.io.Serializable;
import javax.enterprise.context.ConversationScoped;
import javax.faces.event.ComponentSystemEvent;
import javax.inject.Named;
#Named
#ConversationScoped
public class NewClass implements Serializable {
private static final long serialVersionUID = 6470665657635110586L;
private boolean b1;
public boolean isB1() {
return b1;
}
public void setB1(boolean b1) {
this.b1 = b1;
}
public void preRenderView(ComponentSystemEvent evt) {
}
public String peformAction() {
return null;
}
}
and my XHTML is:
<?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">
<f:view>
<h:head>
</h:head>
<f:metadata>
<f:viewParam name="b1"
value="#{newClass.b1}" />
<f:event type="preRenderView"
listener="#{newClass.preRenderView}"/>
</f:metadata>
<h:body>
<h:form>
<h:commandLink action="#{newClass.setB1(!newClass.b1)}"
style="background-color: #{newClass.b1 ? 'darkorchid' : 'aquamarine'};"
value="btn3"/>
<h:panelGrid rendered="#{newClass.b1}"
columns="1">
<h:commandLink value="edit"
action="#{newClass.peformAction()}" />
</h:panelGrid>
</h:form>
</h:body>
</f:view>
</html>
The performAction() method is not fired after I press the commandLink that should invert the boolean making the other commandLink rendered. When debugging I can see that the boolean is set to true, but it seems to me the "rendered" attribute is evaluated before the viewparams is set. Is this true?
The example works fine with #ManagedBean and #javax.faces.bean.ViewScoped.
I think that you don't have long-running conversation. You could read more information on this site: http://docs.oracle.com/javaee/6/api/javax/enterprise/context/ConversationScoped.html
If you have transient conversation this bean is recreated after every request
I am getting the following problem:
After my view is restored, validation of field causes JSF to skip to Render response phase (because required field is empty). But even though the current value (empty string) is rendered to show to user that he/she did not fill anything, the following statement is not executed:
<c:if test="#{empty cc.attrs.fieldValue}">
<f:attribute name="style" value="background-color: yellow;"/>
</c:if>
Is it a bug or a feature? Please help.
Complete test example (Netbeans 6.8 project) is here: http://www.221b.cz/so/JSFTester.zip
From the tutorial: "If the request is a postback and errors were encountered during the apply request values phase, process validations phase, or update model values phase, the original page is rendered during Render response phase" (http://java.sun.com/javaee/5/docs/tutorial/doc/bnaqq.html)
Does it mean that if view is restored in "Restore View" phase and then any apply request/validation/update model phase fails and skips to "Render response" that Render response only passes restored view without any changes to client?
Managed Bean (TesterBean.java):
package cz.test;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
#ManagedBean
#RequestScoped
public class TesterBean {
// Simple DataStore (in real world EJB)
private static String storedSomeValue = null;
private String someValue;
public TesterBean() {
}
public String storeValue() {
storedSomeValue = someValue;
return "index";
}
public String eraseValue() {
storedSomeValue = null;
return "index";
}
public String getSomeValue() {
someValue = storedSomeValue;
return someValue;
}
public void setSomeValue(String someValue) {
this.someValue = someValue;
}
}
Composite component (field-component.xhtml):
<?xml version='1.0' encoding='ISO-8859-1' ?>
<!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"
xmlns:composite="http://java.sun.com/jsf/composite"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<!-- INTERFACE -->
<composite:interface>
<composite:attribute name="currentBehaviour" type="java.lang.String" required="true"/>
<composite:attribute name="fieldValue" required="true"/>
</composite:interface>
<!-- IMPLEMENTATION -->
<composite:implementation>
<h:panelGrid columns="3">
<c:choose>
<c:when test="#{cc.attrs.currentBehaviour == 'READONLY'}" >
<h:outputText id="fieldValue" value="#{cc.attrs.fieldValue}">
</h:outputText>
</c:when>
<c:when test="#{cc.attrs.currentBehaviour == 'MANDATORY'}" >
<h:inputText id="fieldValue" value="#{cc.attrs.fieldValue}" required="true">
<f:attribute name="requiredMessage" value="Field is mandatory"/>
<c:if test="#{empty cc.attrs.fieldValue}">
<f:attribute name="style" value="background-color: yellow;"/>
</c:if>
</h:inputText> *
</c:when>
<c:when test="#{cc.attrs.currentBehaviour == 'OPTIONAL'}" >
<h:inputText id="fieldValue" value="#{cc.attrs.fieldValue}">
</h:inputText>
</c:when>
</c:choose>
<h:message for="fieldValue" style="color:red;" />
</h:panelGrid>
</composite:implementation>
Page (index.xhtml):
<?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:ez="http://java.sun.com/jsf/composite/components">
<h:head>
<title>Testing page</title>
</h:head>
<h:body>
<h:form>
<h:outputText value="Some value:"/>
<ez:field-component currentBehaviour="MANDATORY" fieldValue="#{testerBean.someValue}"/>
<h:commandButton value="Store" action="#{testerBean.storeValue}"/>
<h:commandButton value="Erase" action="#{testerBean.eraseValue}" immediate="true"/>
</h:form>
<br/><br/>
<b>Why is field's background color not set to yellow?</b>
<ol>
<li>NOTICE: Field has yellow background color (mandatory field with no value)</li>
<li>Fill in any value (eg. "Hello") and press Store</li>
<li>NOTICE: Yellow background disappeared (as mandatory field has value)</li>
<li>Clear text in the field and press Store</li>
<li><b>QUESTION: Why is field's background color not set to yellow?</b></li>
<li>Press Erase</li>
<li>NOTICE: Field has yellow background color (mandatory field with no value)</li>
</ol>
</h:body>
EDIT, following Brian's suggestion (field-component.xhtml)
<?xml version='1.0' encoding='ISO-8859-1' ?>
<!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"
xmlns:composite="http://java.sun.com/jsf/composite">
<!-- INTERFACE -->
<composite:interface>
<composite:attribute name="currentBehaviour" type="java.lang.String" required="true"/>
<composite:attribute name="fieldValue" required="true"/>
</composite:interface>
<!-- IMPLEMENTATION -->
<composite:implementation>
<h:panelGrid columns="3">
<h:outputText rendered="#{cc.attrs.currentBehaviour == 'READONLY'}" id="fieldValue1" value="#{cc.attrs.fieldValue}" />
<h:inputText rendered="#{cc.attrs.currentBehaviour == 'MANDATORY'}" id="fieldValue2" title="#{cc.attrs.fieldValue}" value="#{cc.attrs.fieldValue}" required="true" style="#{empty cc.attrs.fieldValue ? 'background-color: yellow;' : ''}">
<f:attribute name="requiredMessage" value="Field is mandatory"/>
</h:inputText> *
<h:inputText rendered="#{cc.attrs.currentBehaviour == 'OPTIONAL'}" id="fieldValue3" value="#{cc.attrs.fieldValue}"/>
<h:message for="fieldValue" style="color:red;" />
</h:panelGrid>
</composite:implementation>
But still not working even if I got rid of JSTL:-( Seems that only value attribute is updated with new value from http request in h:inputText but the rest of attributes are not reevaluated in phase Render Response.
The <c:choose> and <c:when> tags you are using are JSTL tags, not JSF tags. This means they are evaluated at build time, rather than at render time. When you post back, the component tree does not get rebuilt, rather it get's re-rendered, and the <c: tags don't get re-evaluated.
Try your example again using <h:panelGroup rendered="#{}"> tags instead of the <c: tags.
See this write-up for more details:
http://drewdev.blogspot.com/2008/03/build-time-vs-render-time.html
It is very important to remember that you cannot have components "re-appear" on post back of a JSF form. This is because a JSF component tree should never be altered between having its state saved and when its state is restored. This is very important, so let me say again, a JSF component tree should never be altered between having its state saved and when its state is restored.
I did some investigation and debugging in the last two days and this is my result.
First of all, I simplified example to omit composite component and to have it as simple as possible.
Managed bean (TesterBean2.java)
package cz.test;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
#ManagedBean
#RequestScoped
public class TesterBean2 {
// Simple DataStore (in real world EJB)
private static String storedSomeValue = null;
private String someValue;
public TesterBean2() {
}
public String storeValue() {
storedSomeValue = someValue;
return "index";
}
public String eraseValue() {
storedSomeValue = null;
return "index";
}
public String getSomeValue() {
someValue = storedSomeValue;
return someValue;
}
public void setSomeValue(String someValue) {
this.someValue = someValue;
}
}
Testing page (index.xhtml)
<?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">
<h:head>
<title>Testing page</title>
</h:head>
<h:body>
<h:form>
<h:inputText id="fieldValue" requiredMessage="Field is mandatory" title="#{testerBean2.someValue}" value="#{testerBean2.someValue}" required="true" style="#{empty testerBean2.someValue ? 'background-color: yellow;' : ''}"/>
<h:commandButton value="Store" action="#{testerBean2.storeValue}"/>
<h:commandButton value="Erase" action="#{testerBean2.eraseValue}"/>
</h:form>
</h:body>
Where is the problem?
I think it is problem of com.sun.faces.renderkit_html_basic.TextRenderer and its method getEndTextToRender:
protected void getEndTextToRender(FacesContext context,
UIComponent component,
String currentValue)
throws IOException {
ResponseWriter writer = context.getResponseWriter();
assert(writer != null);
boolean shouldWriteIdAttribute = false;
boolean isOutput = false;
String style = (String) component.getAttributes().get("style");
String styleClass = (String) component.getAttributes().get("styleClass");
String dir = (String) component.getAttributes().get("dir");
String lang = (String) component.getAttributes().get("lang");
String title = (String) component.getAttributes().get("title");
if (component instanceof UIInput) {
writer.startElement("input", component);
writeIdAttributeIfNecessary(context, writer, component);
writer.writeAttribute("type", "text", null);
writer.writeAttribute("name", (component.getClientId(context)),
"clientId");
// only output the autocomplete attribute if the value
// is 'off' since its lack of presence will be interpreted
// as 'on' by the browser
if ("off".equals(component.getAttributes().get("autocomplete"))) {
writer.writeAttribute("autocomplete",
"off",
"autocomplete");
}
// render default text specified
if (currentValue != null) {
writer.writeAttribute("value", currentValue, "value");
}
// Rest of code omitted
}
A currentValue parameter is explicitly passed into this method called from com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeEnd
#Override
public void encodeEnd(FacesContext context, UIComponent component)
throws IOException {
rendererParamsNotNull(context, component);
if (!shouldEncode(component)) {
return;
}
ResponseWriter writer = context.getResponseWriter();
assert(writer != null);
// NOTICE currentValue getter
String currentValue = getCurrentValue(context, component);
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE,
"Value to be rendered {0}",
currentValue);
}
// NOTICE currentValue
getEndTextToRender(context, component, currentValue);
}
and if we look closer at a method getCurrentValue()
/**
* #param context the FacesContext for the current request
* #param component the UIComponent whose value we're interested in
*
* #return the value to be rendered and formats it if required. Sets to
* empty string if value is null.
*/
protected String getCurrentValue(FacesContext context,
UIComponent component) {
if (component instanceof UIInput) {
Object submittedValue = ((UIInput) component).getSubmittedValue();
if (submittedValue != null) {
// value may not be a String...
return submittedValue.toString();
}
}
String currentValue = null;
Object currentObj = getValue(component);
if (currentObj != null) {
currentValue = getFormattedValue(context, component, currentObj);
}
return currentValue;
}
the property returned by getSubmittedValue() is filled in Restore View phase (if Process validation phase skips to Render response phase). As a consequence we get "updated" value which was passed from user only for value attribute, the rest remain unaltered.
In case of successful Process Validation phase, which does not cause direct skip to Render Response phase (if user fills in any not null value), a new constructor of HtmlInputText is called and style, title etc. attributes are filled from the scratch. These attributes are filled from managed bean which was updated with proper data in phase Update Model Values.
OK, this is not a bug, but the feature. It only affirms my thesis that something smells in the sentence: "If the request is a postback and errors were encountered during the apply request values phase, process validations phase, or update model values phase, the original page is rendered during Render response phase".
Any clue how to solve this situation if I really long for yellow background for mandatory fields?
Updated project is here: http://www.221b.cz/so/JSFTester2.zip
I have finally managed to get the validation to work.
I used validator which has access to UIComponent. In case of failed validation special style is applied to the component. This style is also taken into consideration in Render response phase.
So how does it behave?
View is restored including style style="#{empty testerBean2.someValue ? 'background-color: yellow;' : ''}"
Validation does not pass. So testerBean2.someValue is not updated (as Update Model Values phase is skipped) but constant style is set into h:inputText using RequiredValidator - component.setValueExpression("style", new ValueExpressionLiteral("background-color: yellow;", String.class));
In Render Response yellow background is applied even if testerBean.someValue has not been updated as Required Validator has already set constant new ValueExpressionLiteral("background-color: yellow;", String.class)
I have implemented own required validator (inspired by Bashan's validator from http://www.codereye.com/2009/12/validating-empty-text-field-using-jsf.html).
RequiredValidator.java
package cz.test;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
import org.apache.el.ValueExpressionLiteral;
public class RequiredValidator implements Validator {
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
if (value == null || "".equals(value.toString().trim())) {
FacesMessage message = new FacesMessage();
String messageStr = (String) component.getAttributes().get("message");
if (messageStr == null) {
messageStr = "Please enter data";
}
message.setDetail(messageStr);
message.setSummary(messageStr);
message.setSeverity(FacesMessage.SEVERITY_ERROR);
component.setValueExpression("style", new ValueExpressionLiteral("background-color: yellow;", String.class));
throw new ValidatorException(message);
} else {
component.setValueExpression("style", new ValueExpressionLiteral("", String.class));
}
}
}
The line I added:
component.setValueExpression("style", new ValueExpressionLiteral("background-color: yellow;", String.class));
to force JSF trigger validation for empty fields (web.xml):
....
<context-param>
<param-name>javax.faces.VALIDATE_EMPTY_FIELDS</param-name>
<param-value>true</param-value>
</context-param>
....
to register validator with JSF (faces-config.xml):
<validator>
<validator-id>RequiredValidator</validator-id>
<validator-class>cz.test.RequiredValidator</validator-class>
</validator>
and web page using required validator and TesterBean2 (index.xhtml):
<?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>
<title>Testing page</title>
</h:head>
<h:body>
<h:form>
<h:messages/>
<h:inputText id="fieldValue"
title="#{testerBean2.someValue}"
value="#{testerBean2.someValue}"
style="#{empty testerBean2.someValue ? 'background-color: yellow;' : ''}">
<f:validator validatorId="RequiredValidator"/>
<f:attribute name="message" value="Field is mandatory"/>
</h:inputText>
<h:commandButton value="Store" action="#{testerBean2.storeValue}"/>
<h:commandButton value="Erase" action="#{testerBean2.eraseValue}"/>
</h:form>
</h:body>
</html>
NOTICE: There cannot be used required attribute in h:inputText. It would outrun required validator.
After the user fills my backing bean with info through the forms, I want to process the instance in Java code (such as JAXB marshalling).
So at the moment i'm doing this like so:
<% OtherBean.method(myBackingBean); %>
which is - if i'm right - not quite an up to date solution :) So how can I make this happen in a 'better' way?
Thanks in advance,
Daniel.
One solution is to add an action method to the bean - it is possible to have it process itself.
For example, this simple bean:
public class SimpleBean {
private String forename;
private String surname;
public String processData() {
// TODO: real work
System.out.println("forename=" + forename);
System.out.println("surname=" + surname);
return null; // optional navigation rule
}
public String getForename() {
return forename;
}
public void setForename(String forename) {
this.forename = forename;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
}
...defined in WEB-INF/faces-config.xml:
<managed-bean>
<managed-bean-name>simpleBean</managed-bean-name>
<managed-bean-class>simplebean.SimpleBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
...can be processed using the this JSP:
<?xml version="1.0" encoding="UTF-8" ?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<jsp:directive.page language="java"
contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" />
<jsp:text>
<![CDATA[<?xml version="1.0" encoding="UTF-8" ?>]]>
</jsp:text>
<jsp:text>
<![CDATA[<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">]]>
</jsp:text>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Enter Name</title>
</head>
<body>
<f:view>
<h:form>
<h:panelGrid columns="2">
<h:outputText value="forename:" />
<h:inputText id="it1" value="#{simpleBean.forename}" />
<h:outputText value="surname:" />
<h:inputText id="it2" value="#{simpleBean.surname}" />
</h:panelGrid>
<h:commandButton action="#{simpleBean.processData}"
value="process" />
</h:form>
</f:view>
</body>
</html>
</jsp:root>
Note the method binding #{simpleBean.processData}. This must be a public method that takes no arguments and returns a String argument (which can be used for page navigation if desired).
This isn't the only way to go about this, but it is fairly straightforward.
you could have one backing bean as an instance variable of the other bean. and configure it in spring or whatever you are using.