views:

265

answers:

4

I am making a web application using JBoss Seam 2.2.0, and I want to trim my inputs before receiving them, even before the Hibernate Bean Validation phase. Is this possible?

I saw someone using a PhaseListener to do the same functionality. Is this the best way to do it?

A: 

You can use the maxlength attribute. It sets the maximum length in characters of the string accepted by this input component.

 <h:inputText maxlength="100" value="#{ubout.textInput}" />

That will prevent the user using that field to put in too much text.

Martlark
This will not trim the value of the input!
romaintaz
How is this related to trimming inputs? `maxlength` is totally irrelevant in my case.
Hosam Aly
+1  A: 

One suggestion is to trim the text in Javascript, once the user changes the value of the input:

<h:inputText ... onchange="this.value = trim(this.value);"/>

with the Javascript function:

function trim(myString) {
    return myString.replace(/^\s+/g,'').replace(/\s+$/g,'');
}


Edit, regarding your comment:

The solution I suggested is the best way to do that because it is done on the client side. However, as you said in the comment, it will not work if the client's browser does not allow Javascript. As shown here, 95% of the users activate the Javascript on their browsers (and it was in January 2008 !).

However, if you really need to support none Javascript browsers, I suggest that you indeed implement the PhaseListener solution.


Edit2

The solution proposed by Damo with a Convertor is also working, but you will need to specify the converter for every input, which is not needed with the PhaseListener, i.e. you will need to always add converter="#{stringTrimConverter}" for all of your inputs.

romaintaz
Thank you. This is a partial solution, but it won't work for clients who have JavaScript disabled.
Hosam Aly
Ok, then I have edited my answer...
romaintaz
Thanks. Many browsers support JavaScript, but that's on the desktop. Some mobile phone browsers do not support JavaScript (although many do). On the other hand, I don't like to trust values sent from clients, so I'd prefer to do it on both the client and server sides.
Hosam Aly
As for converterers, why do you say that they will trim *after* validation? I thought the JSF life cycle dictated otherwise. Please correct me if I am wrong.
Hosam Aly
Yes, sorry, the conversion is made *before* any validation. I've edited my answer again. As I explained, the convertor solution will work, but you will have to specify the converter (method or converterId) you want to use for every input in your application...
romaintaz
+6  A: 

I use a Converter for this. Works very well.

Page:

<h:inputText value="#{myBean.myValue}" converter="#{stringTrimConverter}"/>

Code:

@Name("stringTrimConverter")
@BypassInterceptors
@Converter
public class StringTrimConverter implements javax.faces.convert.Converter {

    public Object getAsObject(FacesContext context, UIComponent cmp, String value) {

     if(StringUtils.isBlank(value)) {
      return null;
     } else {
      return value;
     }
    }

    public String getAsString(FacesContext context, UIComponent cmp, Object value) {

     if(value != null) {
      return value.toString().trim();
     } 
     return null;
    }

}
Damo
Thanks. This is certainly a possible solution, but I'd have to add the converter by hand to every input field. I hope there can be an easier way.
Hosam Aly
+1  A: 

Perhaps a better way is to extend the <h:inputText>, create your own component that is pretty much the same as <h:inputText> but that trimmes the result by default.

In my opinion though, there should be an attribute in inputText that trimmed by default ie:

<h:inputText value="#{myBean.text}" trim="true"/>

Update:

Ok, so here is how you can create a component that trim's the inputText fields. Note, however that I haven't tested the code, so I am not 100% sure it will work, but it should.

In faces-config.xml

Add your component

<component>
  <component-type>foo.InputControlComponent</component-type>
  <component-class>my.package.foo.InputControl</component-class>
</component>

Create WEB-INF/foo.taglib.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE facelet-taglib PUBLIC
"-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
"http://java.sun.com/dtd/facelet-taglib_1_0.dtd"&gt;
<facelet-taglib>
   <namespace>http://whatever.com/foo&lt;/namespace&gt;
    <tag>
        <tag-name>inputControl</tag-name>
        <component>
            <component-type>foo.InputControlComponent</component-type>
        </component>
    </tag>
 </facelet-taglib>

In web.xml

<context-param>
  <param-name>facelets.LIBRARIES</param-name>
  <param-value>/WEB-INF/foo.taglib.xml</param-value>
</context-param>

InputControl.java

public class InputControl extends UIPanel {

    public InputControl() {
        super();
    }

    private void childrenEncodeBegin(FacesContext context, List<UIComponent> children) {
    for (UIComponent comp : children) {
            if (comp instanceof UIInput) {
                comp = (UIInput) comp;
                ((UIInput) comp).setValue(((UIInput) comp).getValue().toString().trim());
            } 

        // Encode recursively
        if (comp.isRendered() && comp.getChildCount() > 0)
            childrenEncodeBegin(context, comp.getChildren());
    }

    }

    public void encodeBegin(FacesContext context) throws IOException {
        if (getChildren() != null)
            childrenEncodeBegin(context, getChildren());
    }
}

Now in your xhtml you can use it like this:

<foo:inputControl>
  <ui:include src="myForm.xhtml"/>
</foo:inputControl> 
Shervin
This looks like a good idea. Could you add an example on how to do it?
Hosam Aly