I am selecting a date from a <p:calendar> component and I want to have the current time appended to the selected date.
Assume that now the time is 12/13/13 04:30:12 .
I had selected the date as 12/17/13 from the calendar and I want to save it as 12/17/13 04:30:12.
You can implement your custom #FacesConverter and apply it on the <p:calendar> component.
#FacesConverter("timestampConverter")
public class TimestampConverter implements Converter {
#Override
public Object getAsObject(FacesContext facesContext,
UIComponent uIComponent,
String string) {
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yy");
Date date = null;
Calendar calendar = Calendar.getInstance();
try {
date = sdf.parse(string);
calendar.setTime(date);
} catch (ParseException e) {
e.printStackTrace();
}
Calendar now = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, now.get(Calendar.HOUR_OF_DAY));
calendar.set(Calendar.MINUTE, now.get(Calendar.MINUTE));
calendar.set(Calendar.SECOND, now.get(Calendar.SECOND));
Timestamp result = new Timestamp(calendar.getTime().getTime());
return result;
}
#Override
public String getAsString(FacesContext facesContext,
UIComponent uIComponent,
Object object) {
if (object == null) {
return null;
}
return object.toString();
}
}
In the getAsObject(..) method you can get the String that's received from the front-end, append the current time and construct a Timestamp object as a result.
The snippet from the facelet (plus my testing button) looks like this:
<h:form>
<p:calendar value="#{myBean.date}" >
<f:converter converterId="timestampConverter" />
</p:calendar>
<p:commandButton title="Test" action="#{myBean.testAction}" />
<h:form>
and in the myBean class there should be a date property with the corresponding accessor methods.
#RequestScoped
#ManagedBean(name = "myBean")
public class MyBean {
private Date date;
public Date getDate() {
return date;
}
public void setDate(Date date) {
return date;
}
public String testAction() {
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/YY HH:mm:ss");
String output = sdf.format(date);
System.out.println("Selected date with timestamp: " + output);
}
}
More info:
JSF Custom Converter
Related
I am using the PrimeFaces datePicker component in my .xhtml file like this
<p:datePicker id="src_time"
value="#{flightDetailController.flight.UTCsource}"
pattern="dd.MM.yyyy" inline="true" />
This flightDetailController has a getter and setter for a Flight object
public void setFlight(Flight flight) {
this.flight = flight;
doReloadFlight();
}
public void doReloadFlight() {
this.flight = flightService.loadFlight(flight.getFlightId());
}
public Flight getFlight()
{
if (flight == null) {
flight = new Flight();
}
return flight;
}
And the Flight object has a getter and setter for a Java.util.Date object
public Date getUTCsource() {
return UTCsource;
}
public void setUTCsource(Date uTCsource) {
UTCsource = uTCsource;
}
If I run this code, the flight (which is previously set) will load fine and the datePicker will display the Date of the object. However, when I add the attribute showTime="True" to the datePicker, I get the following JS exception in my console:
Cannot read property 'split' of undefined TypeError: Cannot read property 'split' of undefined
at K.<computed>.<computed>.parseTime
I am using PrimeFaces 7.0, where according to the documentation value should be set to a java.util.Date object, and it simply states to enable timePicker functionality by setting showTime="True" so I am not sure where I am going wrong.
Thank you.
My solution was to create a converter and for the date to be displayed as a String
#Dependent
#FacesConverter(value = "fechaHoraConverter", managed = true)
public class FechaHoraConverter implements Converter<Date>{
private SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm a");
#Override
public Date getAsObject(FacesContext context, UIComponent component, String fecha) {
try {
return sdf.parse(fecha);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Date fecha) {
return sdf.format(fecha);
}
}
<p:datePicker id="ven" value="#{tr.tarea.vencimiento}" required="true" showTime="true" appendTo="#(body)" showIcon="true" readonlyInput="true" pattern="dd/MM/yyyy" hourFormat="12"
converter="fechaHoraConverter"/>
Show pickdate
I'm working on a datatable in which a single column contains various fields like
Name
Salary
Date
Birth
So I took datatype as String in my bean for that column.
Now my concern is, can I use p:calendar for date of birth with datatype String in my bean?
If so then how?
Use a FacesConverter
Proof of concept:
#Named
public class DateStringConverter implements Converter {
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) {
SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd");
try {
return parser.parse(arg2);
} catch (ParseException e) {
e.printStackTrace();
return null;
}
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2) {
return arg2.toString();
}
}
xhthml
<p:calendar value="#{dateAsString}" pattern="yyyy-MM-dd" converter="#{dateStringConverter}" />
Well. I find in all places and I still without solution. I need to do a input mask with Primefaces and bind the value to a Date object in my bean.
The problem is: I use a converter with all validations, convertions and formats with my own code. My answer is: Is other solution with better performance. I hope so. Please help.
P/D: I don't need use a pickdate. I need the input mask to do this
I use this:
<div style="margin-bottom:1em;font-size: 1.2em;">
<p:inputMask id="dob" mask="99/99/9999" value="#{viewMB.request.dateOfBirth}" style="width:8em;" >
<f:convertDateTime pattern="MM/dd/yyyy" />
</p:inputMask>
<p:watermark value="MM/DD/YYYY" for="dob" />
</div>
and you can still add custom validator if you wish.
Well, this is my solution
<h:form>
<p:inputMask mask="99-99-9999" value="#{mask.date}" converterMessage="Invalid Date!" converter="dateconverter" />
<h:commandButton actionListener="#{mask.submit()}" value="Submit" />
</h:form>
And the converter
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
System.out.println(value);
if (value != null) {
try {
if (validateDate(value)) {
return convertDate(value).toDate();
}
else{
throw new ConverterException("Invalid date!");
}
} catch (Exception e) {
throw new ConverterException("Invalid date!");
}
}
throw new ConverterException("Null String!");
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
System.out.println(value);
if (value != null) {
try {
Date now = (Date)value;
DateTime d = new DateTime(now);
return d.getDayOfMonth() + "-" + d.monthOfYear() + "-" + d.getYear();
} catch (Exception e) {
throw new ConverterException("Convertion failure!");
}
}
throw new ConverterException("Null object!");
}
private boolean validateDate(String param) throws ParseException{
//Param is a date format from input mask
String[] values = param.split("-");
int day = Integer.valueOf(values[0]);
int month = Integer.valueOf(values[1]);
int year = Integer.valueOf(values[2]);
DateTime converted = convertDate(param);
if (converted.getDayOfMonth() != day || converted.getMonthOfYear() != month || converted.getYear() != year) {
return false;
}
else{
return true;
}
}
private DateTime convertDate(String param) throws ParseException{
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");
Date convertedCurrentDate = sdf.parse(param);
return new DateTime(convertedCurrentDate);
}
The Managed Bean
#ManagedBean(name="mask")
public class MaskBean {
private Date date;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public void submit(){
RequestContext context = RequestContext.getCurrentInstance();
context.execute("alert('date submited: value recibed " + date.toString() + "')");
}
}
It work for me.
I have a <p:selectCheckboxMenu> and I want to get the selected values back in bean. But the value I receive
when I select an item from the menu it's a string, representing the type field from the CategorizationBean.
I just want when I select an item from the table, to get the whole CategorizationBean structure in the bean.
This is the snippet from the xhtml page:
<p:selectCheckboxMenu label="Categorization"
value="#alertMB.selectedCategories}"
converter="com.converter.CategoryConverter">
<f:selectItems value="#{alertMB.categoryDomainEntry}"
var="category"
itemLabel="#{category.type}"
itemValue="#{category}"/>
</p:selectCheckboxMenu>
Snippet from bean:
public List<CategorizationBean> getSelectedCategories() {
return selectedCategories;
}
public void setSelectedCategories(List<CategorizationBean> selectedCategories) {
this.selectedCategories = selectedCategories;
}
public class CategorizationBean implements Serializable{
private String type;
private long id;
I think that you have missed by using a list of beans, I use this example and it works:
<p:selectCheckboxMenu id="slctRdBtn"
value="#{yourBean.compLovDtgrid}"
converter="compLovDtgridConverter">
<f:selectItems
value="#{yourBean.listCompLovDtgrid}"
var="rdbtn" itemLabel="#{rdbtn.vjlrLibelleRep}"
itemValue="#{rdbtn}" />
</p:selectCheckboxMenu>
and for the converter:
#FacesConverter(forClass=CompLovDtgrid.class , value="compLovDtgridConverter")
public class CompLovDtgridConverter implements Converter{
#Override
public String getAsString(FacesContext context, UIComponent component, Object value)
{
return (value instanceof CompLovDtgrid) ? ((CompLovDtgrid) value).getVjlrCodeRep() : null;
}
#Override
public Object getAsObject(FacesContext context, UIComponent component,String value)
{
if(value == null)
return null;
YourBean data = context.getApplication().evaluateExpressionGet(context, "#{yourBean}", YourBean.class);
for(CompLovDtgrid compLovDtgrid : data.getListCompLovDtgrid())
{
if(compLovDtgrid.getVjlrCodeRep().equals(value))
return compLovDtgrid;
}
throw new ConverterException(new FacesMessage(String.format("Cannot convert %s to CompLovDtgrid", value)));
}
}
and for the list, I use:
public List<CompLovDtgrid> getListCompLovDtgrid()
{
return listCompLovDtgrid;
}
public void setListCompLovDtgrid(List<CompLovDtgrid> listCompLovDtgrid) {
this.listCompLovDtgrid = listCompLovDtgrid;
}
I have created a custom ISO date time Converter:
public class IsoDateTimeConverter implements Converter, StateHolder {
private Class type;
private String pattern;
private boolean transientValue = false;
public void setType(Class type) {
this.type = type;
}
public void setPattern(String pattern) {
this.pattern = pattern;
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException {
if (StringCheck.isNullOrEmpty(value)) {
throw new ConverterException("value not specified");
}
try {
if (IsoDate.class.equals(type)) {
if (WebConst.ISO_DATE_NONE.equals(value)) {
return IsoDate.DUMMY;
} else {
//TODO User spezifische TimeZone auslesen
return new IsoDate(value, TimeZone.getDefault().getID());
}
} else if (IsoTime.class.equals(type)) {
if (WebConst.ISO_TIME_NONE.equals(value)) {
return IsoTime.DUMMY;
} else {
//TODO User spezifische TimeZone auslesen
return new IsoTime(value, TimeZone.getDefault().getID());
}
} else if (IsoTimestamp.class.equals(type)) {
if (WebConst.ISO_TIMESTAMP_NONE.equals(value)) {
return IsoTimestamp.DUMMY;
} else {
//TODO User spezifische TimeZone auslesen
return new IsoTimestamp(value, TimeZone.getDefault().getID());
}
} else {
throw new ConverterException("value not convertible");
}
} catch (Exception e) {
throw new ConverterException(e.getMessage());
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) throws ConverterException {
if (value == null) {
throw new ConverterException("value not specified");
}
if (IsoDate.class.equals(value)) {
IsoDate isoDate = (IsoDate) value;
if (isoDate.isDummy()) {
return WebConst.ISO_DATE_NONE;
} else {
//TODO User spezifische TimeZone auslesen
return isoDate.toString(pattern, TimeZone.getDefault().getID(), false);
}
} else if (IsoTime.class.equals(value)) {
IsoTime isoTime = (IsoTime) value;
if (isoTime.isDummy()) {
return WebConst.ISO_TIME_NONE;
} else {
//TODO User spezifische TimeZone auslesen
return isoTime.toString(pattern, TimeZone.getDefault().getID(), false);
}
} else if (IsoTimestamp.class.equals(value)) {
IsoTimestamp isoTimestamp = (IsoTimestamp) value;
if (isoTimestamp.isDummy()) {
return WebConst.ISO_TIMESTAMP_NONE;
} else {
//TODO User spezifische TimeZone auslesen
return isoTimestamp.toString(pattern, TimeZone.getDefault().getID(), false);
}
} else {
throw new ConverterException("value not convertible");
}
}
#Override
public Object saveState(FacesContext context) {
return new Object[]{type, pattern};
}
#Override
public void restoreState(FacesContext context, Object state) {
type = (Class) ((Object[]) state)[0];
pattern = (String) ((Object[]) state)[1];
}
#Override
public boolean isTransient() {
return transientValue;
}
#Override
public void setTransient(boolean transientValue) {
this.transientValue = transientValue;
}
}
And I use the Converter as <mh:IsoDateTimeConverter> in the following view:
<p:dataTable value="#{imports.list}" var="item">
<p:column>
<h:outputText value="#{item.balanceDate}" immediate="true">
<mh:IsoDateTimeConverter type="#{webConst.ISO_DATE_CLASS}" pattern="#{webConst.ISO_DATE_FORMAT}"/>
</h:outputText>
</p:column>
</p:dataTable>
The problem is, when I first open this view, all properties are set in my Converter class only once and then the datatable renders and converts the values based on initial properties.
I expected that the properties are set on a per-row basis. How can I achieve this?
To the point, you expected that the converter's properties are set every time a datatable row is rendered. This is indeed not true. JSF will create only one converter instance per component when the view is to be built, it will not create/reset the converter each time the row is rendered.
There are several ways to get it to work.
Pass the dynamic attributes as <f:attribute> of the component and let the Converter intercept on that. You can find an example here: JSF convertDateTime with timezone in datatable. This can then be used as
<h:outputText value="#{item.balanceDate}">
<f:converter converterId="isoDateTimeConverter" />
<f:attribute name="pattern" value="#{item.pattern}" />
</h:outputText>
Use an EL function instead of a Converter. You can find an example here: Facelets and JSTL (Converting a Date to a String for use in a field). This can then be used as
<h:outputText value="#{mh:convertIsoDate(item.balanceDate, item.pattern)}" />
Bind the converter and datatable's DataModel as a property of the same managed bean. This way you will be able to set the converter's properties based on the row data before returning it. Here's a basic kickoff example based on standard JSF components and standard DateTimeConverter (it should work equally good on PrimeFaces components and with your custom converter):
<h:dataTable value="#{bean.model}" var="item">
<h:column>
<h:outputText value="#{item.date}" converter="#{bean.converter}" />
</h:column>
</h:dataTable>
with
#ManagedBean
#ViewScoped
public class Bean implements Serializable {
private List<Item> items;
private DataModel<Item> model;
private DateTimeConverter converter;
#PostConstruct
public void init() {
items = Arrays.asList(
new Item(new Date(), "dd-MM-yyyy"),
new Item(new Date(), "yyyy-MM-dd"),
new Item(new Date(), "MM/dd/yyyy"));
model = new ListDataModel<Item>(items);
converter = new DateTimeConverter();
}
public DataModel<Item> getModel() {
return model;
}
public Converter getConverter() {
converter.setPattern(model.getRowData().getPattern());
return converter;
}
}
(the Item class is just a bean with two properties Date date and String pattern)
this results in
23-09-2011
2011-09-23
09/23/2011
Use OmniFaces <o:converter> instead. It supports render time evaluation of EL in the attributes. See also the <o:converter> showcase example.
<h:outputText value="#{item.balanceDate}">
<o:converter converterId="isoDateTimeConverter" pattern="#{item.pattern}" />
</h:outputText>
The above excellent (as always) answer from BalusC is comprehensive but didn't quite hit my exact requirement. In my case, I need to bind a Converter to each iteration in a ui:repeat. I need a different Converter depending on each item being repeated. The answer did point me in the right direction, though, so I thought it worth sharing my solution in case it helps anyone else.
I use a Converter that delegates all it's work to another Converter object specified in the attribute, as in the first of BalusC's answers. Note that this doesn't help at all if you wish to use converters with parameters, it's aimed at the situation where you would want to bind a Converter to a property of a repeating object.
Here's the delegating Converter. It's also a Validator, which works in exactly the same way.
// package and imports omitted for brevity
#FacesConverter(value="delegatingConverter")
#FacesValidator(value="delegatingValidator")
public class Delegator implements Converter, Validator {
// Constants ---------------------------------------------------------------
private static final String CONVERTER_ATTRIBUTE_NAME = "delegateConverter";
private static final String VALIDATOR_ATTRIBUTE_NAME = "delegateValidator";
// Business Methods --------------------------------------------------------
#Override
public Object getAsObject(FacesContext context, UIComponent component,
String value) throws ConverterException {
return retrieveDelegate(component, Converter.class, CONVERTER_ATTRIBUTE_NAME)
.getAsObject(context, component, value);
}
#Override
public String getAsString(FacesContext context, UIComponent component,
Object value) throws ConverterException {
return retrieveDelegate(component, Converter.class, CONVERTER_ATTRIBUTE_NAME)
.getAsString(context, component, value);
}
#Override
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
retrieveDelegate(component, Validator.class, VALIDATOR_ATTRIBUTE_NAME)
.validate(context, component, value);
}
private <T> T retrieveDelegate(UIComponent component, Class<T> clazz,
String attributeName) {
Object delegate = component.getAttributes().get(attributeName);
if (delegate == null) {
throw new UnsupportedOperationException("No delegate was specified."
+ " To specify, use an f:attribute tag with: name=\""
+ attributeName + "\"");
}
if (!(clazz.isAssignableFrom(delegate.getClass()))) {
throw new UnsupportedOperationException("The specified delegate "
+ "was not a " + clazz.getSimpleName() + " object. " +
"Delegate was: " + delegate.getClass().getName());
}
return (T) delegate;
}
}
So now where I would wish to use this code within my ui:repeat, which won't work:
<h:outputText value="#{item.balanceDate}">
<f:converter binding="#{item.converter} />
<f:validator binding="#{item.validator} />
</h:outputText>
I can instead use this code, which works OK:
<h:outputText value="#{item.balanceDate}">
<f:converter converterId="delegatingConverter"/>
<f:validator validatorId="delegatingValidator"/>
<f:attribute name="delegateConverter" value="#{item.converter}"/>
<f:attribute name="delegateValidator" value="#{item.validator}"/>
</h:outputText>
Assuming that the repeating item has a public Converter getConverter() method and similar for the Validator.
This does have the advantage that the same Converters or Validators that are used elsewhere can be re-used without any changes.