I'm trying to get a composite component working with it's own backing bean,
using the example on p375 from the Core JSF 3 book, but just get an NPE. The problem seems to be at the start of encodeBegin(), Date date = (Date) getValue() returns null.
If I'm honest I don't really understand where the value of the component is supposed to
be getting stored, I specify it as a java.util.Date using cc:attribute type=, but I
don't really understand how this: public Object getSubmittedValue() { return this; } -
which is going to return an instance of an InputDateBean class - results in a Date. I am generally good and confused by how this is supposed to work.
Unlike the book example I am trying to the use backing component for temporary storage,
so when the day is input I try to store it in #{cc.day}, in the book they use an application scoped bean for some reason.
Thanks for any help. I am using Mojarra 2.1.
inputDate.xhtml
<cc:interface componentType="uk.co.myco.jsfbeans.sqcc.InputDateBean">
<cc:attribute name="value" type="java.util.Date"/>
</cc:interface>
<cc:implementation>
<h:panelGrid columns="3">
<h:inputText id="day" value="#{cc.day}"
converter="javax.faces.Integer"/>
<h:inputText id="month" value="#{cc.month}"
converter="javax.faces.Integer"/>
<h:inputText id="year" value="#{cc.year}"
converter="javax.faces.Integer"/>
</h:panelGrid>
</cc:implementation>
InputDateBean.java
package uk.co.myco.jsfbeans.sqcc;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import javax.faces.component.FacesComponent;
import java.util.GregorianCalendar;
import javax.faces.application.FacesMessage;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.convert.ConverterException;
import uk.co.myco.general.SQLog;
import uk.co.myco.jsfbeans.helper.Messages;
#FacesComponent(value = "uk.co.myco.jsfbeans.sqcc.InputDateBean")
public class InputDateBean extends UIInput implements NamingContainer {
private int day = 0, month = 0, year = 0;
public InputDateBean() {
}
#Override
public String getFamily() {
return "javax.faces.NamingContainer";
}
#Override
public void encodeBegin(FacesContext context) throws IOException {
Date date = (Date) getValue();
Calendar cal = new GregorianCalendar();
cal.setTime(date);
UIInput dayComponent = (UIInput) findComponent("day");
UIInput monthComponent = (UIInput) findComponent("month");
UIInput yearComponent = (UIInput) findComponent("year");
dayComponent.setValue(cal.get(Calendar.DATE));
monthComponent.setValue(cal.get(Calendar.MONTH) + 1);
yearComponent.setValue(cal.get(Calendar.YEAR));
super.encodeBegin(context);
}
#Override
public Object getSubmittedValue() {
return this;
}
#Override
protected Object getConvertedValue(FacesContext context, Object newSubmittedValue)
throws ConverterException {
UIInput dayComponent = (UIInput) findComponent("day");
UIInput monthComponent = (UIInput) findComponent("month");
UIInput yearComponent = (UIInput) findComponent("year");
int lday = (Integer) dayComponent.getValue();
int lmonth = (Integer) monthComponent.getValue();
int lyear = (Integer) yearComponent.getValue();
if (isValidDate(lday, lmonth, lyear)) {
return new GregorianCalendar(lyear, lmonth - 1, lday).getTime();
} else {
FacesMessage message = Messages.getMessage("util.messages", "invalidDate", null);
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ConverterException(message);
}
}
// getters & setters & isValidDate() removed
}
I now see my mistake. The problem was that the composite component has to be
called with a Date object, i.e. <cclib:inputDate value="#{bean.date}"/>. As the
code stands the date needs to be instantiated, but it wasn't. The more robust
way of doing this is to do a new Date() in encodeBegin() in the event that
getValue() is null. This then works the same a h:inputText/f:convertDateTime
which does not require that the value is instantiated.
Related
In my faces-config.xml I have set
<context-param>
<param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
<param-value>true</param-value>
</context-param>
Thus an arbitrary input component with no input places a null into the bean property bound to the value="#{myBean.someDate}" attribute and renders an empty component when getting null from that bean property.
While this is generally the wanted behavior for the application, I have a particular component where I want to replace null with a custom non null value.
Is there a general good way to achieve this?
I have tried this for the Primefaces (6.2) calendar with Mojarra 2.3.8:
<h:form id="myForm">
<p:calendar value="#{myBean.someDate}" pattern="MM/dd/yyyy"
converter="mySpecialDateConverter" locale="en" />
<p:commandButton value="send" process="#form" update="#form" />
<hr />
<h:outputText value="#{myBean.someDate}" />
</h:form>
I tried it using a converter that while rendering the component returns an empty String in getAsString() when my custom value is received from #{myBean.someDate}, or spits out my custom value in getAsObject() when null or pure emptyness is submitted:
import java.util.Date;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.DateTimeConverter;
import javax.faces.convert.FacesConverter;
#FacesConverter("mySpecialDateConverter")
public class MyConverter extends DateTimeConverter {
private static final Date PAST = new Date(0);
public MyConverter() {
setPattern("MM/dd/yyyy");
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (PAST.equals(value)) {
return "";
}
return super.getAsString(context, component, value);
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (null == value || "".equals(value.trim())) {
return PAST;
}
return super.getAsObject(context, component, value);
}
}
Rendering an empty calendar component when the bean property equals PAST works as expected. But submitting the calender without input sets the property to null because Primefaces CalendarRenderer simply does not consult the Converter on blank value submission:
package org.primefaces.component.calendar;
public class CalendarRenderer extends InputRenderer {
// ...
public Object getConvertedValue(FacesContext context, UIComponent component, Object value) throws ConverterException {
Calendar calendar = (Calendar) component;
String submittedValue = (String) value;
SimpleDateFormat format = null;
if (isValueBlank(submittedValue)) {
return null;
}
//Delegate to user supplied converter if defined
try {
Converter converter = calendar.getConverter();
if (converter != null) {
return converter.getAsObject(context, calendar, submittedValue);
}
}
// ...
}
// ...
}
This is partially ok, as the JavaDoc on Converter.getAsObject() says:
/**
* #return <code>null</code> if the value to convert is <code>null</code>,
* otherwise the result of the conversion
*/
Such there is no reason for Primefaces to resolve the converter on empty input. Would be different imho if we'd set javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL = false. Then I'd consider above behavior not correct as "" is not null.
For completeness this is my bean aka myBean:
import java.io.Serializable;
import java.util.Date;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
#Named
#ViewScoped
public class MyBean implements Serializable {
private static final long serialVersionUID = 1L;
private Date someDate;
public Date getSomeDate() {
return someDate;
}
public void setSomeDate(Date someDate) {
this.someDate = someDate;
}
}
I don't want to make changes to that bean as in real application the Calendars' value is bound to persisted entity property values. I also don't want the persistence layer do the job.
The very background:
I want to prevent null values in some database DATE fields. In case of periods for example I want the start default to some far away past date not relevant in application context, for end of that period I want a date in the far future not relevant in application context and both without to bother the user which dates they are by default. These default dates help filtering entities for which the current Date is in or out of that period, while no input by the user is assumed to be virtually the period for ever.
JSF components will not call the converter on a null value or anything they considers as null - this is true for the default components in JSF as well. So in this regard I guess PrimeFaces is behaving as expected and this is the correct outcome. Here are two ways of circumventing this that instantly come to mind;
Use OmniFaces
As described in JSF converter with null value, OmniFaces o:param will call your converter regardless of a null value - you can use it to pass parameters to components. In your specific case you should be able to do something similar to this;
<h:form id="myForm">
<p:calendar value="#{myBean.someDate}" pattern="MM/dd/yyyy" locale="en" />
<p:commandButton value="send" process="#form" update="#form">
<o:param name="date" value="#{myBean.someDateConversion}"
converter="mySpecialDateConverter" />
</p:commandButton>
<hr />
<h:outputText value="#{myBean.someDate}" />
</h:form>
Use a custom renderer
The second option is to use a custom renderer and just modify the behaviour of the component to always call the converter, as described in this answer - JSF Custom Converter not called on null value.
This question already has answers here:
commandButton/commandLink/ajax action/listener method not invoked or input value not set/updated
(12 answers)
Validation Error: Value is not valid
(3 answers)
How to populate options of h:selectOneMenu from database?
(5 answers)
Closed 5 years ago.
I am trying to perform ajax on selecting any element in selectCheckBoxMenu primefaces.
But ajax event fires only if itemValue is equal to String. If i am passing object then not event is fired. Please have a look.
package com.gsep.utility.bean;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
#ManagedBean
#Entity
#ViewScoped
public class Tool {
#Id #GeneratedValue
#Column(name="tool_id")
private int toolId;
#Column(name="tool_name")
private String toolName;
public int getToolId() {
return toolId;
}
public void setToolId(int toolId) {
this.toolId = toolId;
}
public String getToolName() {
return toolName;
}
public void setToolName(String toolName) {
this.toolName = toolName;
}
}
Above is my bean class which i am iterating to display values in selectCheckboxMenu.
Below is jsf code
<p:selectCheckboxMenu id="multiple" value="#{projectBean.selectedToolList}" label="Tools" multiple="true"
filter="true" filterMatchMode="startsWith" panelStyle="width:250px" >
<f:selectItems value="#{projectBean.toolList}" var="toolObject" itemLabel="#{toolObject.toolName}" itemValue="#{toolObject}" />
<p:ajax update="projectRequestForm:testTabView" listener="#{projectBean.addTab(requestBean,projectBean)}" />
</p:selectCheckboxMenu>
Here if i am changing itemValue attribute to toolObject.toolName then ajax call is happening but i want the selectedToolList as a Tool collection of objects instead of toolName collection of Strings.
#ManagedBean(name = "projectBean")
#ViewScoped
public class ProjectBeanCompUI {
#ManagedProperty(value="#{jiraDao}")
private JiraProjectDAO jiraDao;
private List<Tool> toolList;
private List<String> selectedToolList;
public List<Tool> getToolList() {
toolList = getTools();
return toolList;
}
public void setToolList(List<Tool> toolList) {
this.toolList = toolList;
}
public List<String> getSelectedToolList() {
return selectedToolList;
}
public void setSelectedToolList(List<String> selectedToolList) {
this.selectedToolList = selectedToolList;
public List<Tool> getTools()
{
List<Tool> toolList = new ArrayList<>();
Tool tool = new Tool();
tool.setToolId(1);
tool.setToolName("Eclipse");
Tool tool1 = new Tool();
tool1.setToolId(2);
tool1.setToolName("Jira");
toolList.add(tool);
toolList.add(tool1);
return toolList;
}
}
I have composite component:
<my:component value="#{bean.property1.property2}/>
From composite component I need to get class of bean.property1 to read its annotations.
I do it by the following code:
ValueExpression valueExpression = expressionFactory.createValueExpression(FacesContext.getCurrentInstance().getELContext(),
"#{bean.property1}", Object.class);
Object bean = valueExpression.getValue(FacesContext.getCurrentInstance().getELContext());
Class<?> beanClass = bean.getClass();
This works well but if I use my:component from a facelet and pass bean as a parameter via ui:param this does not work because bean can't be resolved.
Probably I should use FaceletContext as ELContext instead of FacesContext.getCurrentInstance().getELContext():
FaceletContext faceletElContext = (FaceletContext) FacesContext.getCurrentInstance().getAttributes()
.get("javax.faces.FACELET_CONTEXT");
But this doesn't work on RENDER_RESPONSE phase (from encodeBegin method). It returns last used ELContext instead of actual context (I am not surprised :) ).
The goal is to get class of #{bean.property1} from my:component. How can I do it?
This is easy with RichFaces:
ValueExpressionAnalayser analyser = new ValueExpressionAnalayserImpl();
ValueDescriptor valueDescriptor = analyser.getPropertyDescriptor(context, valueExpression);
Class<?> beanClass = valueDescriptor.getBeanType();
This is ok for me.
Also there is ValueExpressionAnalayzer in javax.faces.validator package but it is package private and can't be used.
You could pass the bean as a parameter to the component.
1) Declare the attribute in the component interface file(if you are using composite component):
<cc:interface componentType="myComponentClass">
<cc:attribute name="myBean" preferred="true"/>
..others attributes
<cc:interface>
2) Implement the respective getter and setter for "myBean" attribute in the component class(myComponentClass)
protected enum PropertyKeys {
myBean;
String toString;
PropertyKeys(String toString) {
this.toString = toString;
}
PropertyKeys() {}
#Override
public String toString() {
return ((this.toString != null) ? this.toString : super.toString());
}
}
public YourBeanClass getMyBean() {
return (YourBeanClass) getStateHelper().eval(PropertyKeys.myBean, null);
}
public void setMyBean(YourBeanClass myBean) {
getStateHelper().put(PropertyKeys.myBean, myBean);
}
3) Set the attribute on you jsf page:
<my:component myBean="#{bean}"/>
4) In the component's render class cast the UIComponent to myComponentClass.
#Override
public void encodeBegin(FacesContext pContext, UIComponent pComponent)
throws IOException {
myComponentClass myComponent = (myComponentClass) pComponent;
myComponent.getYourAttribute();
}
I am working with JSF 2.2 and Tomcat 8 and I am just starting to play with them.
I have a command button in a jsf page.
<h:commandButton id="newObject" value="New Object" action="#{someObject.someAction}">
<f:param name="object_id" value="#{someObject.object_id}" />
</h:commandButton>
The ManagedBean is similar to this:
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
#ManagedBean
public class SomeObject implements Serializable{
private static final long serialVersionUID = 1L;
private int object_id;
public int getObject_id() {
return object_id;
}
public void setObject_id(int object_id) {
this.object_id = object_id;
}
public String someAction() {
setObject_id(sqlInsert());
if(getObject_id() != 0) {
System.out.println(getObject_id());
return "new_page";
}
}
}
The sqlInsert method is working fine. I use it to insert a new row in some sql table and get the auto generated key, which is an int. If the insert did not happen it would return 0.
I can navigate to the new_page, but the param object_id is 0. I added println to show the object_id and it is the actual key.
What am I doing wrong?
Since you are using the only #ManagedBean annotation on your Managed Bean and not specifying any Scope of you bean explicitly, your Bean will act as if its a #RequestScoped bean.[See link]
So every time you click your New Object button, the Bean is re initialized and you will loose the state(variable values).
Think and decide which scope you want to use [See link]. For your requirements #ViewScoped might do the job for you.
In RichFaces 4.1, rich:progressBar 'currentValue' from the ManagedBean does not updating with for-loop.
progressBar.xhtml
<h:form id="formProgress">
<h:commandLink action="#{progressBarBean.startProcess}" value="click here"/>
<rich:progressBar mode="ajax" value="#{progressBarBean.currentValue}" interval="1000" id="pb"
enabled="#{progressBarBean.enabled}" minValue="0" maxValue="100">
<h:outputText value="Retrieving #{progressBarBean.currentValue} of #{progressBarBean.totalRecords}" />
</rich:progressBar>
</h:form>
Bean
package ap;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class ProgressBarBean implements Serializable {
private static final long serialVersionUID = 8775622106408411357L;
private boolean enabled = false;
private Integer totalRecords;
private Integer currentValue;;
public String startProcess() {
setEnabled(true);
setTotalRecords(100);
return null;
}
public Integer getCurrentValue() {
if (isEnabled()) {
for(currentValue=0;currentValue < totalRecords;) {
currentValue++;
}
}
return currentValue;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Integer getTotalRecords() {
return totalRecords;
}
public void setTotalRecords(Integer totalRecords) {
this.totalRecords = totalRecords;
}
}
When i click the 'click here' link, the currentValue updates very fastly and reaches the totalRecords to 100 suddenly. It was not updating in the incremental way(present value in for-loop). The progress bar is not updated by the present value return by the method.
Any help please.
There are two problems: your Java code does not do what you want it to do and you're not telling the page to update (that won't happen automatically).
Take a look at the getCurrentValue() again: It increments currentValue from 0 to 100 and returns the result which is 100. #{progressBarBean.currentValue} does not care (or know) what happens with the variable, it only cares about the result of the getCurrentValue() method.
So in order for it all to work it will have to look like this:
Page
<a4j:commandLink action="#{progressBarBean.startProcess}" value="click here" render="pb" execute="#this"/>
<rich:progressBar mode="ajax" value="#{progressBarBean.currentValue}" interval="1000" id="pb"
enabled="#{progressBarBean.enabled}" minValue="0" maxValue="100">
<a4j:ajax event="begin" listener="#{progressBarBean.increment}" render="text"/>
<h:outputText value="Retrieving #{progressBarBean.currentValue} of #{progressBarBean.totalRecords}" id="text" />
</rich:progressBar>
The a4j:ajax is fired each second (i.e. each interval), it increments the currentValue and updates the text.
You also need a4j:commandLink (or a4j:ajax inside the h:commandLink) in order to rerender the progressbar - in your example you enable the progressbar in the bean but the value on the page does not change.
Bean
public Integer getCurrentValue() {
return currentValue;
}
public void increment() {
if (isEnabled() && currentValue < totalRecords) {
currentValue++;
}
}
Ask if anything isn't clear.