JSF convertDateTime validation error [duplicate] - validation

This question already has an answer here:
"Validation Error: Value is not valid" error from f:datetimeConverter
(1 answer)
Closed 6 years ago.
I have a problem mentioned in the topic. I have
<h:selectOneMenu class="time" id="time" value="#{auctionBean.expDate}">
<f:convertDateTime pattern="dd/MM/yyyy HH:mm:ss"/>
<f:selectItem itemValue="11/11/1111 11:11:11" itemLabel="1 day" />
<f:selectItem itemValue="#{auctionBean.calculateExpDate(4)}" itemLabel="4 days" />
<f:selectItem itemValue="#{auctionBean.calculateExpDate(7)}" itemLabel="7 days" />
<f:selectItem itemValue="#{auctionBean.calculateExpDate(14)}" itemLabel="14 days" />
</h:selectOneMenu>
The problem is i am getting Validation Error: Value is not valid message for all items but first one.
The method:
public String calculateExpDate(int days) {
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
cal.add(Calendar.DATE, days);
Format formatter = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
System.out.println("data: " + formatter.format(cal.getTime()));
return formatter.format(cal.getTime());
}
It returns String in good format. Output from system.out:
INFO: data: 10/10/2013 20:40:04
Where is the problem? I have no clue at all

A good one!
If what you are getting is a VALIDATION error, not a CONVERSION problem, then the probable scenario is:
the list of available values is created, and the values have precision of 1 second,
user picks one of them,
but on postback the available values get recalculated, and they are all a couple of seconds later than the original ones.
so the value that your user picked is no more available on the list of possible values,
and so a validation error happens (which is what JSF does always when the value chosen is no longer on the list of select items).
you do not get it with the first item, because it's the only one that does not change with time :-).
If you move the backing bean to view scope (or session scope), or cut the precision, it should work. Or better yet - make an enum with values of NOW, IN_2_DAYS, IN_4_DAYS and so on. And calculate the real date after the enum is chosen.

fdreger is right! I marked his post as an answer. Thanks:)
This is my solution if you are lazy (however it might be done better I guess):
JSF:
<h:selectOneMenu class="time" id="time" value="#{auctionBean.choosenOption}">
<f:selectItems value="#{auctionBean.days}" var="days" itemValue="#{days}" itemLabel="#{days.label}"/>
</h:selectOneMenu>
fragment of my auctionBean:
public enum Days {
IN_1_DAY("1 dzieƄ", 1),
IN_4_DAYS("4 dni", 4),
IN_7_DAYS("7 dni", 7),
IN_14_DAYS("14 dni", 14);
private String label;
private int days;
private Days(String label, int days) {
this.label = label;
this.days = days;
}
public String getLabel() {
return label;
}
public Date calculateExpDate() {
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
cal.add(Calendar.DATE, this.days);
return cal.getTime();
}
}
private Days choosenOption;
public void setChoosenOption(Days choosenOption) {
this.choosenOption = choosenOption;
expDate = choosenOption.calculateExpDate();
}
public Days getChoosenOption() {
return choosenOption;
}
public Days[] getDays() {
return Days.values();
}
User chooses how many days his auction should be active and i calculate what is an expiration date.
expDate is Date object which i set only once, after choosing the single enum and sumbitting the form. Pretty good solution suggested :)

Related

MudDatePicker DefaultValue plus DateChange

I want to use MudDatePicker element in a way where I need to be able to set a default value on load but at the same time define a onChange event for it. I am trying to do this but error says "The component parameter is used two or more times for this component". Is there a way I can do this?
<MudDatePicker #bind-Date="#DefaultValue.Value" Label="Date" DateChanged="OnDateChange"
Required="true" Class="mb-3" />
If you have a two-way binding in Blazor i.e. #bind-Date="date", you can convert it to a one-way binding with a change event which must set the value. The two-way binding is just syntax sugar and will do the same behind the scene.
<MudDatePicker Date="#_date" Label="Date" DateChanged="OnDateChange"
Required="true" Class="mb-3" />
#inject ISnackbar Snackbar
#code {
DateTime? _date = new DateTime(2021, 12, 24);
void OnDateChange(DateTime? newDate)
{
_date=newDate;
// here you can do something when the date changes.
Snackbar.Add($"Date changed to {_date}");
}
}
Here is a snippet which you can play around with: https://try.mudblazor.com/snippet/mYcPFPvLnlyEHeOF
Remove the DateChanged="OnDateChange" and change #bind-Date="DefaultValue.Value" to #bind-Date="DefaultValue". For the property DefaultValue create a getter and setter. Because you have two way binding using #bind-Date="DefaultValue", the setter part gets called every time the value is changed.
I've added some sample code below:
DateTime? _defaultValue = DateTime.Now;
private DateTime? DefaultValue
{
get => _defaultValue;
set
{
_defaultValue = value;
OnDateChange();
}
}

Primefaces calendar component manually entering date beyond maxdate validation fails

I'm trying to add a Calendar input component in an application.
The Calendar input component to pick a datetime and the max date restriction works ie doesn't allow to select future date
Greys out the future date - works fine / User cannot select a future date
Problem is the user can enter the future date manually, it is not validating this field.
User is able to enter the future date manually
5/24/2021
6/2/2020
<p:calendar id="userdate" value="#{calendarView.pricedate}" maxdate="#{calendarView.maxDate}" />
calendarView.java
private Date maxDate = new Date();
public Date getMaxDate() {
return maxDate;
}
public void setMaxDate(Date maxDate) {
this.maxDate = new Date();
}
Primefaces Version
<primefaces.version>6.2</primefaces.version>
How to validate this component when user enters the field value manually, I have added a max date validation attribute.
A simple search in a search engine of your liking
One that tracks you
Or even one that does not track you
Would have shown
Calendar: does not validate the input against mindate/maxdate
With a fix version of 7.0
And to prove it I created a real full [mcve]:
XHMTL:
<h:form>
<p:messages id="messages"/>
<p:calendar id="registrationDate" value="#{testView3582.date}" mindate="#{testView3582.minDate}" maxdate="#{testView3582.maxDate}" navigator="true" yearRange="c-2:c" showButtonPanel="true"
mask="true" styleClass="#{testView3582.maxDate}"/>
<p:commandButton value="submit" update="messages" />
</h:form>
Bean:
#Named(value = "testView3582")
#ViewScoped
public class TestView implements Serializable {
Date date;
Date minDate;
Date maxDate;
#PostConstruct
public void init() {
Calendar cal = Calendar.getInstance();
date = cal.getTime();
cal.add(Calendar.DAY_OF_YEAR, -10);
minDate = cal.getTime();
cal.add(Calendar.DAY_OF_YEAR, +20);
maxDate = cal.getTime();
}
//Getters/Setters omitted
}
With a min/max date of 10 days before now and 10 days past and when manually entering 04/20/20 and pressing submit I get
Next time please
Always search the issue list (contains a workaround for previous versions, see also the other answer)
Always create a real minimal reproducible example
Always try with a newer version (preferably latest)

Bean value not updated before ajax event listener is called

I am having some trouble implementing an ajax listener event which detects when a date is changed on a form. I have a datatable and inside one of the columns I have an <ace:dateTimeEntry> which holds a start date field which is stored in the bean. (Note: alliance is the name of the variable used for the datatable).
<ace:column headerText="Start Date" rendered="#{not alliance.deletePending}">
<ace:dateTimeEntry id="startDateField" value="#{alliance.allianceStartDate}" pattern="dd/MMM/yyyy" renderAsPopup="true" effect="fadeIn">
<ace:ajax execute="#this" render="#this" event="dateSelect" listener="#{allianceViewBean.changeAllianceActiveIndicator}"/>
<ace:ajax execute="#this" render="#this" event="dateTextChange" listener="#{allianceViewBean.changeAllianceActiveIndicator}"/>
</ace:dateTimeEntry>
</ace:column>
I am using an tag which calls a listener method in the bean called
#{allianceViewBean.changeAllianceActiveIndicator}
This is the bean value change method:
public void changeAllianceActiveIndicator(AjaxBehaviorEvent event) {
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
Calendar c = Calendar.getInstance();
c.setTimeZone(TimeZone.getTimeZone("UTC"));
java.util.Date currentDate = c.getTime();
for (AllianceBean bean : carrierAllianceDetails) {
if ((bean.getAllianceStartDate().compareTo(currentDate) < 0)
&& (bean.getAllianceEndDate().compareTo(currentDate) > 0)) {
bean.setActive(true);
} else {
bean.setActive(false);
}
}
}
However, when I debug this method errors occur. The listener correctly reaches the method but the start date value in the bean is not the updated value and it still refers to the old value before the change. If i enter a new value on the form, the value in the bean is always referring to the previously entered date value. The logic is correct in the method but the values being checked are not.
I am not sure how to ensure that the listener method picks up the latest value from the form.
Thanks
This is due to the lifecycle phase you are in. Check the lifecycle phase in which this event comes in. If it is before the UPDATE_MODEL_VALUES phase (e.g. in the PROCESS_VALIDATIONS phase) then your bean values are simply not updated yet. In that case I would recommend to set the phaseId to INVOKE_APPLICATION on the event, queue it and return the method:
public void changeAllianceActiveIndicator(AjaxBehaviorEvent event) {
if (!event.getPhaseId().equals(PhaseId.INVOKE_APPLICATION) {
event.setPhaseId(PhaseId.INVOKE_APPLICATION);
event.queue();
return;
}
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
Calendar c = Calendar.getInstance();
c.setTimeZone(TimeZone.getTimeZone("UTC"));
java.util.Date currentDate = c.getTime();
for (AllianceBean bean : carrierAllianceDetails) {
if ((bean.getAllianceStartDate().compareTo(currentDate) < 0)
&& (bean.getAllianceEndDate().compareTo(currentDate) > 0)) {
bean.setActive(true);
} else {
bean.setActive(false);
}
}
}
Apart from that the code of your method is not ideal either. Setting the default timeZone is pretty obsolete - as well the whole Calendar block. You could just do
Date currentDate = new Date();
I would also recommend to convert the date that is set on the bean to the server timeZone's date - otherwise you are comparing apples and oranges.

How to sort a list that will be localized in JSF output

In my application I have a list of keys (strings), where the user can select one of them. In the user-interface, the keys will be output according to the current locale:
<h:selectOneMenu value="#{bean.selectedKey}">
<f:selectItems value="#{bean.allKeys}" var="_key" itemLabel="#{msgs[_key]}" />
</h:selectOneMenu>
My setup uses a standard resource-bundle configured in faces-config.xml as explained in this answer by BalusC. msgs in the example above is the name of the resource-bundle variable.
What I want now, is the items from the selectOneMenu to be sorted in alphabetic order. Of course the order depends on the used locale. The problem is, I can't/won't do the sorting in the backing-bean, as I don't know how the JSF-page will output the keys.
This problem seems quite generic to me, so I'm wondering what the best practice is to solve this kind of problem.
(Of course the problem is not only applicable to selectOneMenu. Any list/collection that will be output in the user-interface suffers from the same problem.)
You've basically 2 options.
Sort in client side with a little help of JS. I'll for simplicity assume that you're using jQuery.
<h:selectOneMenu ... styleClass="ordered">
$("select.ordered").each(function(index, select) {
var $select = $(select);
$select.find("option").sort(function(left, right) {
return left.text == right.text ? 0 : left.text < right.text ? -1 : 1;
}).appendTo($select);
if (!$select.find("option[selected]").length) {
select.options.selectedIndex = 0;
}
});
Sort in server side wherein you create List<SelectItem> and grab #{msgs} via injection. Assuming that you're using CDI and thus can't use #ManagedProperty("#{msgs}"), you'd need to create a producer for that first. I'll for simplicity assume that you're using OmniFaces (which is also confirmed based on your question history).
public class BundleProducer {
#Produces
public PropertyResourceBundle getBundle() {
return Faces.evaluateExpressionGet("#{msgs}");
}
}
Then you can make use of it as follows in the backing bean associated with <f:selectItems value>:
private static final Comparator<SelectItem> SORT_SELECTITEM_BY_LABEL = new Comparator<SelectItem>() {
#Override
public int compare(SelectItem left, SelectItem right) {
return left.getLabel().compareTo(right.getLabel());
}
};
private List<SelectItem> allKeys;
#Inject
private PropertyResourceBundle msgs;
#PostConstruct
public void init() {
allKeys = new ArrayList<>();
for (String key : keys) {
allKeys.add(new SelectItem(key, msgs.getString(key)));
}
Collections.sort(allKeys, SORT_SELECTITEM_BY_LABEL);
}
And reference it directly without var as follows:
<f:selectItems value="#{bean.allKeys}" />

LocalDateTime format after form Submit

I use the following jQuery function to format my datepicker
$(".datepicker").datepicker({
dateFormat : "MM yy"
}).attr('readonly', true);
Upon selection I can see the text field is set correctly to November 2013. Before form is submitted I am using Spring validation to validate the date with
public class LocalMonthEditor extends PropertyEditorSupport {
#Override
public void setAsText(final String text) throws IllegalArgumentException {
if (!StringUtils.hasText(text)) {
// Treat empty String as null value.
setValue(null);
} else {
LocalDateTime localDateTime = LocalDateTime.parse(text,
DateTimeFormat.forPattern("MMMM yyyy"));
setValue(localDateTime);
}
}
#Override
public void setValue(final Object value) {
super.setValue(value == null || value instanceof LocalDateTime ? value
: new LocalDateTime(value));
}
#Override
public LocalDateTime getValue() {
return (LocalDateTime) super.getValue();
}
#Override
public String getAsText() {
return getValue() != null ? getValue().toString() : "";
}
}
However after form being submitted the text field is changed to 2013-11-01T00:00:00.000. How can I maintain the field to November 2013 ?
First of all, if the data you need is simply a month and a year, why are you using Joda-Time at all? That's like getting in your car to drive to your mailbox at the end of the driveway: extra effort and complexity for no benefit.
Instead, I suggest you choose between:
Track a pair of variables (month, year)
Define your own class with a pair of members (month, year), and track an instance.
Use a String as seems to be your intention: "November 2013" as you seem to be thinking, or a simpler schemes such as "2013-11".
Secondly, because you created an instance of LocalDatetime, at some point toString seems to be called. The default output of toString on a LocalDateTime is output in the standard ISO 8601 format you saw: 2013-11-01T00:00:00.000. A LocalDateTime has a date value and a time value (hence the name), even if the time value may be set to zeros (meaning start of day). So this is a feature, not a bug.
I don't know Spring Validatation nor the rest of your class structure. I'm guessing you are storing a LocalDateTime instance where instead you meant to be (or should be) storing a String instance. You may need to read up on the subject of "model" versus "view". Often we track data behind the scenes differently than we present data to the user. In this case, you probably should be holding a pair of ints or Integers (one for month, one for year) in your model with a String in your view ("November 2013").
You are setting your local time object directly to text field, thats why your getting full date string.convert your date object by using parse() method and set it. Do not create new object for date set your value directly.

Resources