I am storing a phone number as a string in our database. It would be stored like "1234567890".
I want to display that to the user and format it like (12) 3456-7890
How can I do this with JSF 2.0?
I tried the following, but it doesn't work:
<h:outputText value="1234567890">
<f:convertNumber pattern="(##) ####-####"/>
</h:outputText>
The <f:convertNumber> uses DecimalFormat under the covers and this isn't designed with phone numbers in mind.
You'd need to create a custom Converter and do the desired job in getAsString() implementation using the usual String methods such as substring() and friends.
#FacesConverter("phoneConverter")
public class PhoneConverter implements Converter{
#Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) {
String phoneNumber = (String) modelValue;
StringBuilder formattedPhoneNumber = new StringBuilder();
// ...
return formattedPhoneNumber.toString();
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
// Conversion is not necessary for now. However, if you ever intend to use
// it on input components, you probably want to implement it here.
throw new UnsupportedOperationException("Not implemented yet");
}
}
Use it as follows:
<h:outputText value="1234567890" converter="phoneConverter" />
As BalusC answered, you need a converter to do so. I would suggest the following though: instead of writing your own formatting logic, use a library like libphonenumber. This will do all the hard work for you. Best practice here would be storing the phone number in your model in E.164 format.
Related
In my JSF 2.2 application, when I create JSF form, the problem is that I can put spaces in the beginning of <h:inputText> when I insert it from my form and it will return these spaces with my value that I inserted in it. So I should handle each value with mystring.tirm() to void the spaces. Can I use any something else to return this value without spaces? I know about converter and JavaScript, so can you give me another option to use? I don't want to use any converter and JavaScript.
I don't want to use any converter and JavaScript
There's no magic here. You have to write code to do the job. I gather that your concern is more that you don't want to repeat the same conversion job over every single input field.
In that case, just register a converter specifically on String.class via the forClass attribute of the #FacesConverter like below.
#FacesConverter(forClass=String.class)
public class TrimConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
String trimmed = (submittedValue != null) ? submittedValue.trim() : null;
return (trimmed == null || trimmed.isEmpty()) ? null : trimmed;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) {
return (modelValue != null) ? modelValue.toString() : "";
}
}
This way you don't need to declare the converter in every single input field. It will get applied transparently on every String model value which doesn't already have an explicit converter registered.
The JavaScript way is not recommended as it runs entirely at the client side and endusers can easily disable and manipulate JavaScript. The JSF converter runs at server side and its outcome is not manipulatable by the enduser.
I thing the real problem is in somewhere else. I faced with same thing in my forms (using postgresql and JSF 2.1)
When I created a field type "char(20)". In this case i got unnecessary spaces in h:inputText.
Then I changed the field type to "character varying(20)". In this case there were no spaces in h:inputText.
Here is the explanation for that; Postgresql doc
Is there any way to string JSF h:outPutTextValue ? my string is A-B-A03 ,i just want to display last 3 characters .does openfaces have any avialable function to do this ?
Thanks
You could use a Converter for this job. JSF has several builtin converters, but no one suits this very specific functional requirement, so you'd need to create a custom one.
It's relatively easy, just implement the Converter interface according its contract:
public class MyConverter implements Converter {
#Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) throws ConverterException {
// Write code here which converts the model value to display value.
}
#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 won't be used in h:outputText, but in UIInput components only.
}
}
Provided that you're using JSF 2.0 (your question history confirms this), you can use the #FacesConverter annotation to register the converter. You can use the (default) value attribute to assign it a converter ID:
#FacesConverter("somethingConverter")
(where "something" should represent the specific name of the model value you're trying to convert, e.g. "zipcode" or whatever it is)
so that you can reference it as follows:
<h:outputText value="#{bean.something}" converter="somethingConverter" />
For your particular functional requirement the converter implementation can look like this (assuming that you actually want to split on - and return only the last part, which makes so much more sense than "display last 3 characters"):
#FacesConverter("somethingConverter")
public class SomethingConverter implements Converter {
#Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) throws ConverterException {
if (!(modelValue instanceof String)) {
return modelValue; // Or throw ConverterException, your choice.
}
String[] parts = ((String) modelValue).split("\\-");
return parts[parts.length - 1];
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) throws ConverterException {
throw new UnsupportedOperationException("Not implemented");
}
}
You can try and use the fn:substring functions from JSTL:
${fn:substring('A-B-A03', 4, 7)}
If your string comes from a bean you can add an extra getter to return the trimmed version:
private String myString = "A-B-A03";
public String getMyStringTrimmed()
{
// You could also use java.lang.String.substring with some ifs here
return org.apache.commons.lang.StringUtils.substring(myString, -3);
}
Now you can use the getter in your JSF page:
<h:outputText value="#{myBean.myStringTrimmed}"/>
This question already has answers here:
Validation Error: Value is not valid
(3 answers)
Closed 6 years ago.
I am using the managedBean userHome in requestScope, in which the entity 'user' is going to be persist.
The user has the leader column which is mapped in ManyToOne relation.My Code looks like this
#ManagedBean
#RequestScoped
public class UserHome {
private User user = new User();
// Getters and Setters
private List<SelectItem> selectItems = new ArrayList<SelectItem>();
public UserHome() {
for(User user: availableLeaders) {
selectItems.add(new SelectItem(user.getName(), user));
}
}
public void persis();
}
User.java
public class User {
#Id
#Column
private Integer id;
#Column
privat String name;
#ManyToOne
private User leader;
}
I am trying to get the value of this leader through h:selectOneMenu like this
<h:selectOneMenu value="#{userHome.user.leader}" converter="userConverter">
<f:selectItems value="#{userHome.selectItems}"/>
</h:selectOneMenu>
My converter looks like this
#FacesConverter(forClass = User.class, value="userConverter")
public class UserConverter implements Converter {
private Map<String, User> userValues = new HashMap<String, User>();
public UserConverter() {
init();
}
#Override
public Object getAsObject(FacesContext context, UIComponent component,
String value) {
return userValues.get(value);
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
System.out.println("RAJASEKARAN "+value);
return ((User)value).getName();
}
public void init() {
UserHome userHome = new UserHome();
for(User user:userHome.availableLeaders()) {
userValues.put(user.getName(), user);
}
}
}
While try to save the user I am getting the error UserEdit:j_idt18: Validation Error: Value is not valid
Adding to BalusC's answer: after the postback, you need to make sure that the User instances are either exactly the same ones as you used for rendering the select items, or that you implement equals for your User class.
The code doesn't show where availableLeaders comes from, but if this is fetched from a DB on-demand, then the converter will not convert to the exact same object instance that's in the list that JSF resolves via #{userHome.selectItems}.
After the conversion, JSF will check whether the converted instance can be found in that list using the equals() method.
You've constructed the SelectItem the wrong way. As per the class' documentation, the 1st argument should represent the item value (which is to be converted and submitted) and the 2nd argument should represent the item label (which is to be displayed in list). But you specified them the other way round.
Fix it accordingly:
selectItems.add(new SelectItem(user, user.getName()));
If that still doesn't fix the problem, then it means that the equals() method of User class is not (properly) implemented. JSF will use it to validate the selected User against any of the item values of the list after conversion.
Unrelated to the concrete problem, it may be useful to know that <f:selectItems> in JSF2 offers you the possibility to build the list without the need to build a list of SelectItem manually. Here's an example which achieves exactly the same:
<f:selectItems value="#{userHome.availableLeaders}" var="user"
itemValue="#{user}" itemLabel="#{user.name}" />
This allows you to get rid of the additional selectItems property and the loop in the bean constructor.
I'm new to facelets and I have generated a project using netbeans but I struggling with the tag.
I have
<h:selectOneMenu id="country" value="#{organisation.organisation.country}" title="Country" >
<f:selectItems value="#{country.countryItemsAvailableSelectOne}"/>
</h:selectOneMenu>
In the select I get classpath.Country[iso=GB] which I can see is an object but I really want to see the the country.prinableName value.
I've looked at this for half a day and have drawn a blank
Thanks for any help
Since you're talking about Facelets, I'll assume JSF 2.x.
To start, HTML is one and all String. JSF generates HTML. By default, non-String Java objects are by toString() method converted to their String representation while JSF generates the HTML. To properly convert between those Java objects and String, you need a Converter.
I assume that your Country object has already the equals() method properly implemented, otherwise validation will later fail with "Validation error: Value not valid" because the selected object doesn't return true on testing the equals() for any of the available items.
I'll also make a little change in the naming since #{country} is a confusing managed bean name because it does apparently not represent an instance of the Country class. I'll call it Data which should hold application wide data.
#ManagedBean
#ApplicaitionScoped
public class Data {
private static final List<Country> COUNTRIES = populateItSomehow();
public List<Country> getCountries() {
return COUNTRIES;
}
// ...
}
I'll assume that the Country class has two properties code and name. I'll assume that the managed bean which receives the selected country has a private Country country property. In your <f:selectItems>, you need to loop over #{data.countries} and specify the country object as item value and the country name as item label.
<h:selectOneMenu value="#{bean.country}">
<f:selectItems value="#{data.countries}" var="country" itemValue="#{country}" itemLabel="#{country.name}" />
</h:selectOneMenu>
Now, you need to create a Converter for the Country class. We'll convert based on the country code which is unique for every country (right?). In the getAsString() you implement code which converts the Java object to its unique String representation which is to be used in HTML. In getAsObject() you implement code which converts the unique HTML String representation back to the Java object.
#FacesConverter(forClass=Country.class)
public class CountryConverter implements Converter {
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return (value instanceof Country) ? ((Country) value).getCode() : null;
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null) {
return null;
}
Data data = context.getApplication().evaluateExpressionGet(context, "#{data}", Data.class);
for (Country country : data.getCountries()) {
if (country.getCode().equals(value)) {
return country;
}
}
throw new ConverterException(new FacesMessage(String.format("Cannot convert %s to Country", value)));
}
}
The #FacesConverter will register it automatically in JSF and JSF will automatically use it whenever it encounters a value expression of the Country type. Ultimately, you end up with country code as item value and country name as item label. JSF will convert the submitted country code back to a fullworthy Country object upon form submission.
In JSF 1.x the principle is not much different. In this blog you can find two basic kickoff examples: Objects in h:selectOneMenu.
What happened to you, selectOneMenu call the toString() method for all given objects.
You've to use selectitems or a simple converter to manage that. A very simple example:
price.xhtml:
<h:selectOneMenu id="priceMenu" value="#{priceBean.selectedPrice}">
<f:selectItems value="#{priceBean.prices}" />
</h:selectOneMenu>
PriceBean.java:
..
private String selectedPrice;
..
public String getSelectedPrice() {
return selectedPrice;
}
public void setSelectedPrice(String newPrice) {
selectedPrice = newPrice;
}
..
public List<SelectItem> getPrices() {
List<SelectItem> retVal = new ArrayList<SelectItem>();
retVal.add(new SelectItem("2"));
retVal.add(new SelectItem("4"));
retVal.add(new SelectItem("6"));
return retVal;
}
Further informations about the SelectItem. If you want to use a specially object directly, for example an object called Price, you have to use a converter. Here an example is shown and here.
If you add editable="true" in your
<h:selectOneMenu value="#{bean.country}">
Then you'll get an unexpected String value (not from getAsString()) in the converter method:
public Object getAsObject(FacesContext context, UIComponent component, String value) { }
I have tried working with the grouped selections with something like this :
<h:selectOneMenu value="#{selectionLabBean.oneSelectMenuGroup}"
id="SelectOneMenuGroup" >
<f:selectItems value="#{selectionLabBean.heroGroupList}" />
</h:selectOneMenu>
<p:message for="SelectOneMenuGroup" />
where the heroGroupList is something like this :
SelectItem[] heroArr = new SelectItem[] {
new SelectItem("Paladin"),
...
};
heroListWithGrouping.add(
new SelectItemGroup("Human",
"A collection of human race Heroes",
false,
heroArr
)
);
.....
And i'm left wondering if i can do this kind of grouping with POJOs instead of SelectItem objects ?
If i couldnt achieve this, i think i have to somehow convert my domain objects or my query results into arrays of SelectItem to make it work.
Any ideas ?
That's indeed not possible when you want to use SelectItemGroup. You need to convert from collection of POJO's to List<SelectItem> in a double for-loop during bean's (post)construction.
#PostConstruct
public void init() {
List<HeroRace> heroRaces = getItSomehowFromDatabase();
this.heroGroupList = new ArrayList<SelectItem>();
for (HeroRace heroRace : heroRaces) {
SelectItemGroup group = new SelectItemGroup(heroRace.getName()); // Human, etc
List<SelectItem> heroes = new ArrayList<SelectItem>();
for (Hero hero : heroRace.getHeroes()) {
heroes.add(new SelectItem(hero.getName()); // Paladin, etc
}
group.setSelectItems(heroes.toArray(new SelectItem[heroes.size()]));
this.heroGroupList.add(group);
}
}
You could also use Hero as item value
heroes.add(new SelectItem(hero, hero.getName()); // Paladin, etc
so that you can bind #{selectionLabBean.oneSelectMenuGroup} to a Hero type instead of String. But then you need to supply a Converter. That part is already answered by Amorfis.
Yes, you can return List or array of POJOs instead of SelectItems. You'll need converter for this to work, but it's not a big deal. So, converter first:
#FacesConverter(forClass=Hero.class)
public class HeroConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return new Hero(value);
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return ((Hero)value).getName();
}
}
Now if you return list of Heroes to <f:selectItems>, you have options in HTML where label is Hero.toString(), and value is returned from HeroConverter.getAsString().
One more thing. If you submit some value for this selection, JSF converts it to object and checks (by equals() method) if this object was in list of objects for selection. So in case above, you'll need to override equals() in Hero to check if names are equal. Another solution is not to create new instance in getAsObject, but to keep somewhere list of available Heroes and return this list to <f:selectionItems> and return object from this list in getAsObject().