I am a green bird in JSF, now I was puzzled by the text of it.
when I assign the value of the outputText with multi-spaces, the result shown in IE has only one space.
ex: the code is like
<h:outputText id="name" value="aa (multi-spaces here) bbb" ></h:outputText>
the result text shown in IE is "aa bbb"
can anyone tell me why the spaces disappear without trace?
This behaviour is defined by the HTML spec:
For all HTML elements except PRE, sequences of white space separate "words" (we use the term "word" here to mean "sequences of non-white space characters"). When formatting text, user agents should identify these words and lay them out according to the conventions of the particular written language (script) and target medium.
Note that if you are using XHTML, there are some differences in how attributes and the code point U+000C are handled.
For most text, sequences of white space are not rendered any differently from a single space.
Since this is for a outputText control, you can use a one-way converter for a no-break space solution:
package myconverters;
// imports
public class SpacePreserver implements Converter {
private static final char NO_BREAK_SPACE = '\u00A0';
public String getAsString(FacesContext context, UIComponent component,
Object value) {
if (component instanceof EditableValueHolder) {
throw new IllegalArgumentException(
"Cannot use SpacePreserver converter on editable controls.");
}
return value == null ? null : value.toString().replace(' ', NO_BREAK_SPACE);
}
public Object getAsObject(FacesContext context, UIComponent component,
String value) {
throw new UnsupportedOperationException("Output converter only");
}
}
This can be defined (among other ways) using a faces-config.xml entry:
<converter>
<converter-id>spacePreserver</converter-id>
<converter-class>myconverters.SpacePreserver</converter-class>
</converter>
You can then add this to your output control:
<h:outputText id="text1" value="a b c" converter="spacePreserver" />
This code was tested using JSF 1.1 with a UTF-8 encoded JSP 2.0 view. Note that use of a no-break space will prohibit line-wrapping.
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
I am using a PrimeFaces' pickList with a custom converter.
JSF:
<p:pickList converter="costsConverter" value="#{offerController.costsAsDualListModel}" var="cost" itemLabel="#{cost}" itemValue="#{cost}" />
offerController.costsAsDualListModel looks like this:
public DualListModel<Cost> getCostsAsDualListModel() {
DualListModel<Cost> costsDualList;
List<Cost> costsSource = new ArrayList<Cost>();
List<Cost> costsTarget = new ArrayList<Cost>();
for (Cost c : costs) {
costsSource.add(c);
}
costsDualList = new DualListModel<Cost>(costsSource, costsTarget);
return costsDualList;
}
And my custom converter looks like this:
public String getAsString(FacesContext context, UIComponent component, Object object) {
if (object == null) {
return "";
}
Integer no = ((Cost) object).getNo();
String valueOf = String.valueOf(no);
return valueOf;
}
getAsString() is called and valueOf is correct but inside my picklist I still see the objects and not the return value fo getAsString().
I tried to use <f:converter converterId="costsConverter"/> within the picklist element. Same issue. Also I registered the converter in faces-config.xml:
<converter>
<converter-id>costsConverter</converter-id>
<converter-class>com.my.converter.CostsConverter</converter-class>
</converter>
What could be the problem?
You have a wrong understanding of values in components like picklists, selectonemenus, etc. These values are never displayed there but the labels are. And since converters are for values, not labels, you'll never see the converted value but the labels and everything behaves as it should. Just use itemLabel="#{cost.no}" and everything should be fine (display wise).
See e.g. how it is used in these two Q/A that also use a converter
How to write a custom converter for <p:pickList>
Primefaces Picklist Converter
Silly question, but that's my situation. I am having editable PrimeFaces selectOneMenu where inputField has following restriction:
lower and upper limit of number typed in
predefined text allowed
when decimal number is being typed allow only 2 decimal numbers
All is good except the last one with decimal number restriction. What it means is that I can't type there 1.111 but only 1.11. Change event keyUp for selectOneMenu is sadly added to the tag select but not to input.
Any ideas how to solve?
This calls for a custom validator. Create one which checks for the predefined values, if no match is found, check the number format. Basic example:
#FacesValidator("myValidator")
public class MyValidator implements Validator
{
private List<String> predefinedValues = Arrays.asList("my", "list");
#Override
public void validate(FacesContext context, UIComponent component, Object value)
throws ValidatorException
{
String valueStr = (String) value;
// Check if value is predefined
if (predefinedValues.contains(valueStr)) {
return;
}
// If not predefined, check number format
if (! valueStr.matches("^\\d+(\\.\\d\\d?)?$")) {
throw new ValidatorException(new FacesMessage("Value is invalid!"));
}
// Check number limits...
}
}
The validator can be used in your XHTML as:
<p:selectOneMenu editable="true" ...>
...
<f:validator validatorId="myValidator" />
</p:selectOneMenu>
As an alternative you could use jQuery to find the input field and bind a keypress listener to it. See for example: Jquery: filter input on keypress. However, I would keep the validator in place. Users could paste text for example.
See also:
How to perform validation in JSF, how to create a custom validator in JSF
Regular expression for floating point numbers
I have a property which can possibly contain \n characters. I would like to check in the rendered attribute of a JSF component if the property contains \n and if so, then don't render the component. How can I achieve this?
actually the values is saved as null in database, but its coming as "\n\n" value in the service side.
Provided that you mean that the \n represents the sole value, then you can use fn:trim() for that.
<h:outputText value="#{bean.value}" rendered="#{not empty fn:trim(bean.value)}" />
Alternatively, create a converter which immediately trims the submitted value before the model get polluted with unwanted characters:
<h:inputTextarea value="#{bean.value}" converter="trimConverter" />
#FacesConverter("trimConverter")
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() : "";
}
}
and then just do
<h:outputText value="#{bean.value}" rendered="#{not empty bean.value}" />
By the way, in the comments you specified escape="false". If this input is coming from an enduser via <textarea>, be aware that you're opening a huge XSS attack hole here. See also How to implement a possibility for user to post some html-formatted data in a safe way?
I'm trying to implement a <p:selectManyCheckbox> but I'm having no success.
Now I have the following architecture:
Course - have many Disciplines
Discipline - belongs to none, one or many Courses.
In Course class I have two ArrayList<Discipline>:
public class CourseMBean{
(...)
// Stores all disciplines
private static ArrayList<Discipline> allDisciplines;
// Stores only the disciplines that's already associated with this course.
private static ArrayList<Discipline> courseDisciplines;
(get and set for the arraylists)
(...)
}
All data comes from a MYSQL DB, but that isn't the question. Now I want create a new Course, so I don't have anything in courseDisciplines.
I want show allDisciplines in checkboxes, and want that when user select one checkbox, the object Discipline of this checkbox be added in courseDisciplines - and when unselect one checkbox, remove the discipline from the courseDsiciplines.
My JSF 2.0 code is following:
<p:selectManyCheckbox id="disciplines" value="#{courseMBean.allDisciplines}" layout="grid" columns="2">
<f:selectItems value="#{courseMBean.courseDisciplines}" />
</p:selectManyCheckbox>
This actually shows all disciplines without any selected checkboxes, what's right. But when I select some checkboxes and submit the form I try to print the elements inside courseDisciplines, and this don't show anything in console.
What I'm doing wrong?
when I select some checkboxes and submit the form I try to print the elements inside courseDisciplines
As the courseDisciplines actually represents the available items not the selected items, it seems that you misunderstood some basic concepts around the UISelectMany and UISelectItem(s) components.
The <f:selectItem(s)> (from the UISelectItem(s) family) represent the available items. It are exactly those items which are shown in the UI and which the enduser has to choose from.
The value attribute of <p:selectManyCheckbox> (from the UISelectMany family, like <h:selectManyCheckbox> and <h:selectManyMenu>) represent the (pre)selected items. If this is null or empty during first display of the form, then nothing is preselected. Or if this contains some preselected items, then only those available items which are equal() will be preselected.
When the enduser has changed the selection in the UI and submits the form, then all selected items will end up in the value attribute of UISelectMany component. The UISelectItem(s) remains unchanged.
Here's a basic kickoff example:
<p:selectManyCheckbox value="#{bean.selectedItems}">
<f:selectItems value="#{bean.availableItems}" />
</p:selectManyCheckbox>
<p:commandButton value="Submit" action="#{bean.submit}" />
<p:messages autoUpdate="true" />
private List<String> selectedItems; // +getter +setter
private List<String> availableItems; // +getter (no setter necessary!)
#PostConstruct
public void init() {
availableItems = new ArrayList<String>();
availableItems.add("one");
availableItems.add("two");
availableItems.add("three");
}
public void submit() {
System.out.println("Selected items: " + selectedItems);
}
(all other <p:selectManyXxx> and <h:selectManyXxx> components work exactly the same)
When a complex Javabean object like Discipline comes into the picture, then you need to make sure that there's a Converter for that so that JSF can properly convert between it and String for usage in generated HTML output and as HTTP request parameter (HTML and HTTP namely can't pass around nor hold Java objects, but only character sequences which are in Java represented by String).
This is perhaps your root problem. You said that nothing is printed to the console on submit. But it could be as good the case that the whole submit() method is actually never being invoked. You're not explicit enough on this. If the whole action method is indeed never invoked (i.e. a debug breakpoint doesn't hit there, or another System.out.println() printing a static string is never shown in console), then you've actually most likely a conversion error. If you have used <h|p:message(s)> the right way, or have paid attention to server log about queued but undisplayed faces messages, then you should have noticed it.
In that case, you need to implement a Converter which converts between Discipline and String.
#FacesConverter(forClass=Discipline.class)
public class DisciplineConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
// Write code here to convert from String to Discipline.
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) {
// Write code here to convert from Discipline to String.
}
}
More than often the DB ID is being used as String representation. See also the section "Complex object as selected item" of this answer on a related question: How to populate options of h:selectOneMenu from database?