What I am trying to do is that I am taking timeMillis property that stores time in millisecond(that I got my using System.currentTimeMillis()) and convert it to equivalent days,hours,mins and seconds after substracting it from the current time. The main problem is that whenever the converter timeConverter
is called only getAsString function is invoked , getAsObject is not invoked.
Here is the part of my xhtml file which causing the converter to not run properly.
<c:forEach var="p" items="#{statusBean.statusList}">
<h:form>
<div class="status">
<h:commandLink action="#{friendBean.gotoFriendProfile(p.email)}">
<img src="../images/profilePicture/#{p.picture}" style="height: 29px; width: 29px; "/>
<h:outputText value="#{p.statusBy}:"/>
</h:commandLink>
<h:outputText value="#{p.statusmsg}"/>
<h:outputText value="#{p.timeMillis}">
<f:converter converterId="timeConverter"/>
</h:outputText>
<br/>
<c:forEach var="q" items="#{statusBean.commentList(p.statusId)}">
<div class="barcomment">
<br/>
<h:commandLink action="#{friendBean.gotoFriendProfile(q.email)}">
<img src="../images/profilePicture/#{q.picture}" style="height: 29px; width: 29px; "/>
<h:outputText value="#{q.commentBy}:"/>
</h:commandLink>
<h:outputText value=" #{q.comment}"/>
</div>
</c:forEach>
<br/>
<div class="comment">
<p:inputText value="#{statusBean.comment.comment}" styleClass="box" />
<p:commandLink value="Views" action="#{statusBean.update(p.statusId)}" ajax="false" styleClass="link"/>
</div>
Here is the timeConverter class that I have written.
package com.converter;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
public class TimeConverter implements Converter {
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) {
System.out.println("inside getAsObject");
long time=Integer.parseInt(arg2);
long currentTime=System.currentTimeMillis();
long eclapseTime=time-currentTime;
long secs=eclapseTime/1000;
long days=secs/(60*60*24);
long hours=(secs%(60*60*24))/60*60;
long mins=(secs%(60*60*24)%(60*60))/60;
long secs2=(secs%(60*60*24)%(60*60)%(60));
StringBuffer sb = new StringBuffer();
sb.append(days).append("days").append(hours).append("hours").append(mins).append("mins").append(secs2).append("secs");
String object1 = sb.toString();
return object1;
}
#Override
public String getAsString(FacesContext context, UIComponent component,
Object value) {
System.out.println("inside getAsString");
String value1 = value.toString();
return value1;
}
}
Why exactly is that a problem?
You're only using the converter here in an UIOutput component:
<h:outputText value="#{p.timeMillis}">
<f:converter converterId="timeConverter"/>
</h:outputText>
The getAsString() is been called to convert the Object model value to a String which can be embedded in the generated HTML output (you know, you can't put Java objects plainly in a HTML string).
However, you're nowhere using it in an UIInput component like <h:inputText>, so there is no means of a submitted String value which needs to be converted to the desired Object in the model, so the getAsObject() will obviously never be called.
Everything is working as designed. It look like that your concrete problem is that you should actually perform the job which you did in getAsObject() in the getAsString() instead.
I think that it would help if you give the methods a bit more sensible argument names:
#Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) throws ConverterException {
// Write code here which converts the model value to display value.
// This method will be used when generating HTML output.
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) throws ConverterException {
// Write code here which converts the submitted value to model value.
// This method will be used when processing submitted input values.
}
Related
I am working with settings part in my project. I want to get the form attributes from the DB like maxlength, minlength, pattern etc.
I want to use my bean class variable as a pattern String. Is that Possible? minlength, maxlength attributes works fine using bean class variable, but validateRegex is not working
Bean code
#Service
class CategoryBean {
String categoryNamePattern;
String showForm(){
categoryNamePattern = "([a-zA-Z ]*[a-zA-Z]+[a-zA-Z ]*)";
return "myform.xhtml";
}
}
myform.xhtml With bean class variable for regex pattern
<p:inputText id="name" value="#{categoryBean.selectedCategory.name}">
<f:validateRegex for="name" pattern="#{categeoryBean.categoryNamePattern}"/>
</p:inputText>
myform.xhtml This Works fine with pattern defined inside
<p:inputText id="name" value="#{categoryBean.selectedCategory.name}">
<f:validateRegex for="name" pattern="([a-zA-Z ]*[a-zA-Z]+[a-zA-Z ]*)"/>
</p:inputText>
Finally I did with validator tag. wrote a new validator method
This works fine.
#Service
class CategoryBean {
String catNamePattern;
String showForm(){
categoryNamePattern = "([a-zA-Z ]*[a-zA-Z]+[a-zA-Z ]*)";
return "myform.xhtml";
}
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
if (catNamePattern != null) {
RegexValidator regexValidator = new RegexValidator();
regexValidator.setPattern(categoryNamePattern);
regexValidator.validate(context, component, value);
}
}
}
<p:inputText id="name" value="#{categoryBean.selectedCategory.name}">
<f:validateRegex for="name" pattern="#{categeoryBean.categoryNamePattern}" validator="#{categoryBean.validate}" />
</p:inputText>
This question already has answers here:
Conversion Error setting value for 'null Converter' - Why do I need a Converter in JSF?
(2 answers)
Closed 6 years ago.
Ok, so I get this error and found no solution working for me (I tried around a lot with annotations like #FacesConverter or #Named instead #ManagedBean etc.) If anybody could point me to the right direction, that would be awesome. BalusC mentioned, that this particular error means, that the converter cannot be found, but the Converter is getting called, as I can see in log messages of JBoss (see log.info calls below in the Converter code). At least I don't get NullPointerExceptions from the Converter anymore (although I can't reproduce why). I'm getting a bit frustrated with JSF, but I'm sure it's my fault and I have overseen something obvious?
The code should be simple enough, I want to set zero, one or more Tags for a Link entity (newLink.tags) with means of a selectManyCheckbox:
This is my XHTML form:
<h:form id="create_link_form" rendered="#{authController.loggedIn}">
<h3>Enter a new Link here:</h3>`
<h:panelGrid columns="3" columnClasses="titleCell">
<h:outputLabel for="name" value="Name:"/>
<h:inputText id="name" value="#{newLink.name}"/>
<p:message for="name" errorClass="invalid"/>
<h:outputLabel for="url" value="URL:"/>
<h:inputText id="url" value="#{newLink.url}"/>
<p:message for="url" errorClass="invalid"/>
<h:outputLabel for="description" value="Description:"/>
<h:inputText id="description" value="#{newLink.description}"/>
<p:message for="description" errorClass="invalid"/>
<h:outputLabel for="shared" value="Shared?:"/>
<h:selectBooleanCheckbox id="shared" label="Shared?:" value="#{newLink.shared}"/>
<p:message for="shared" errorClass="invalid"/>
</h:panelGrid>
<h:outputLabel for="tags" value="Tags:"/>
<h:selectManyCheckbox label="Tags:" id="tags" value="#{newLink.tags}" converter="#{tagConverter}">
<f:selectItems value="#{tags}" var="tag" itemLabel="#{tag.name}" itemValue="#{tag}"/>
</h:selectManyCheckbox>
<h:inputHidden id="owner" value="#{newLink.owner}" name="{authController.loggedInUserName}"/>
<p>
<h:panelGrid columns="2">
<h:commandButton id="create" action="#{linkController.create}" value="Create"
styleClass="create">
</h:commandButton>
<p:messages styleClass="messages" autoUpdate="true" globalOnly="true"/>
</h:panelGrid>
</p>
</h:form>
And this is my Converter:
#RequestScoped
#ManagedBean
public class TagConverter implements Converter {
#Inject
private Logger log;
#Inject
private TagService tagService;
#Override
public Object getAsObject(FacesContext fc, UIComponent uic, String value) {
if (value != null && value.trim().length() > 0) {
try {
log.info("TTagConverter.getAsObject() => having " + value);
Long id = Long.parseLong(value);
Tag tag = tagService.getTagWithId(id);
log.info("TagConverter.getAsObject() => Tag converted: " + tag);
return tag;
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Conversion Error", "Not a valid tag."));
}
} else {
return null;
}
}
#Override
public String getAsString(FacesContext fc, UIComponent uic, Object object) {
if (object != null) {
if (object instanceof Tag) {
Long id = ((Tag) object).getId();
return id.toString();
} else {
throw new ConverterException(new FacesMessage("There was an Object type error for a "
+ object.getClass() + " in getAsString(" + object + ")"));
}
} else {
return null;
}
}
}
Your managed bean shouldnt implement it.
Create class and implement converter.
#FacesConverter("myConverter")
public class MyConverter implements Converter{
...
}
in your facelets
<h:selectManyCheckbox label="Tags:" id="tags" value="#{newLink.tags}">
<f:selectItems value="#{tags}" var="tag" itemLabel="#{tag.name}" itemValue="#{tag}"/>
<f:converter converterId="myConverter" />
</h:selectManyCheckbox>
Is it clear?
According to the TLD, convertNumber accepts ValueExpressions for its pattern attribute. But it doesn't seem to work (JSF 1.2 RI):
<h:outputText value="#{Test.numberValue}">
<f:convertNumber pattern="#{Test.numberPattern}" />
</h:outputText>
outputs
0.0210000000000000013045120539345589349977
(Test.numberValue evaluates to 0.021, Test.numberPattern to "0.00%")
If I use a String literal, everything works fine:
<h:outputText value="#{Test.numberValue}">
<f:convertNumber pattern="0.00%" />
</h:outputText>
outputs
2,10%
The h:outputText is part of a h:dataTable column, if that matters.
The h:outputText is part of a h:dataTable column, if that matters.
Found out that it actually matters, please see this question about convertDateTime in a datatable. According to that (thanks to BalusC, as always), this is my solution:
Custom converter:
public class DynamicNumberConverter extends NumberConverter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
setPattern((String) component.getAttributes().get("pattern"));
return super.getAsObject(context, component, value);
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
setPattern((String) component.getAttributes().get("pattern"));
return super.getAsString(context, component, value);
}
}
Markup:
<h:outputText value="#{Test.numberValue}">
<f:converter converterId="DynamicNumberConverter" />
<f:attribute name="pattern" value="#{Test.numberPattern}"/>
</h:outputText>
I try learn JSF and faced with problem.
I did use Servlet 2.5, JDK 1.6 and JSF 2.0.6 (com.sun.faces version from pom file).
I have a simple JSF page that has a <h:inputText/> tag for interaction with user
I expect what user fill this h:inputText then click on h:commandButton and on server side i will get backing bean with updated value.
But in my case lifecycle of JSF breaks on process validations, move to render
response and show to user "Parser error!" message
I.e. for simple h:inputText without any validator and converter i receive error message from server side about parsing of h:inputText value.
After some time i figured out what i can create my own converter which will not modify object, just pass String through himself.
I did add my realization of converter to <h:inputText/> and this work.
Question:
In all examples in books and other tutorials nobody used custom converter for <h:inputText/> if inputText is representation of String value of backing bean.
Why all of this tutorials and examples not working for me without custom converter? Where my mistake?
Source codes:
index.xhtml without converter, not worked for me:
<h:form id="UserForm">
<h:outputText value="Insert your first name:" />
<h:inputText id="userNameID" required="true" value="#{userBean.firstName}">
<f:validateLength minimum="5" maximum="25" />
</h:inputText>
<h:message showSummary="true" showDetail="false" for="userNameID" />
<h:commandButton id="submit" action="/view/validator/response?faces-redirect=true"
value="Submit" />
</h:form>
UserBean.java:
#ManagedBean(name = "userBean")
#SessionScoped
public class UserBean implements Serializable {
private String firstName;
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
MyConverter.java - dummy converter
#FacesConverter(value = "myConverter")
public class MyConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return value;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return value.toString();
}
}
index.xhtml with converter, worked as expected:
<h:form id="UserForm">
<h:outputText value="Insert your first name:" />
<h:inputText id="userNameID" required="true" value="#{userBean.firstName}" converter="myConverter">
<f:validateLength minimum="5" maximum="25" />
</h:inputText>
<h:message showSummary="true" showDetail="false" for="userNameID" />
<h:commandButton id="submit" action="/view/validator/response?faces-redirect=true"
value="Submit" />
</h:form>
The cause of the problem is not visible in the code posted so far, but the key symptom "it fails with a message coming from a so far unidentified converter while it succeeds with an explicit converter" suggests that you've elsewhere in the same project a #FacesConverter(forClass=String.class) which would run automatically on every single String property which doesn't have another converter explicitly specified.
Trying to output a list of items in a datatable, like this:
<t:dataTable value="#{mybean.list}" var="item">
<h:column>
<h:outputText value="#{item.time}">
<f:convertDateTime pattern="yyyy-MM-dd HH:mm:ssZ" timeZone="#{item.timeZone}" />
</h:outputText>
</h:column>
</t:dataTable>
It always formats the time in GMT. It works as expected if I use a string constant or a bean which isn't the datatable variable (like '#{mybean.timeZone}').
Unfortunately, that's the nature of <f:xxx> tags. When the view is to be built, a single instance of the tag is been built where the converter is instantiated. All of its attribtues are been read and set only once. At the moment the view is been built, the #{item} resolves to null (it's only available during rendering of the view), so the timeZone attribute will be null and then default to UTC. When the view is to be rendered, the very same converter instance is been reused for each row of the table.
There are several ways to solve this. I can think of a custom converter or an EL function. I think a custom converter is after all the best as it can then also be reused in input components. The following kickoff example should work out for you (nullchecks and on omitted for brevity):
#FacesConverter("extendedDateTimeConverter")
public class ExtendedDateTimeConverter extends DateTimeConverter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
setPattern((String) component.getAttributes().get("pattern"));
setTimeZone(TimeZone.getTimeZone((String) component.getAttributes().get("timeZone")));
return super.getAsObject(context, component, value);
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
setPattern((String) component.getAttributes().get("pattern"));
setTimeZone(TimeZone.getTimeZone((String) component.getAttributes().get("timeZone")));
return super.getAsString(context, component, value);
}
}
which can be used as
<h:outputText value="#{item.time}">
<f:converter converterId="extendedDateTimeConverter" />
<f:attribute name="pattern" value="yyyy-MM-dd HH:mm:ssZ" />
<f:attribute name="timeZone" value="#{item.timeZone}" />
</h:outputText>
This way the timezone is resolved everytime the converter is invoked instead of during its construction.
Update: the OmniFaces <o:converter> solves exactly this problem without the need for a custom converter.
<h:outputText value="#{item.time}">
<o:converter converterId="javax.faces.DateTime" pattern="yyyy-MM-dd HH:mm:ssZ" timeZone="#{item.timeZone}" />
</h:outputText>