4
votes

is there a way to get Spring MVC forms namespaced for portlets? I don't want to set

<requires-namespaced-parameters>false</requires-namespaced-parameters>

in order to get spring mvc forms working under Liferay 6.2.

I was thinking of overriding the Spring form-taglib so that it puts the portlet-namespace prefix in front of the form field names/ids without actually trying to bind them to bean-properties with a namespace (which will obviously not work) but that seems very time-consuming.

Does anybody know of another way to solve this problem?

Here is an example of a form to show the exact problem:

<portlet:actionURL var="actionURL">
    <portlet:param name="action" value="search"/>
</portlet:actionURL>

<form:form action="${actionURL}" commandName="searchSettings">
    <form:input path="textField"/>
    <form:input path="anotherTextField"/>
    <input type="submit" value="Search"/>
</form:form>

And its corresponding bean would be:

public class SearchSettings {

    private String textField;
    private String anotherTextField;

    // .. getters & setters

}

This will not work under Liferay 6.2 as the form inputs are not namespaced. They should be namespaced like so:

<c:set var="ns"><portlet:namespace/></c:set>

<form:input path="${ns}textField"/>

However this will not work since now Spring will try to bind the formfield to a property

SearchSettings._namespace_portlet_textField

which of course does not exist.

Has anybody come across this problem and found a solution other than override Spring MVCs Form-Taglib? I saw that it's already documented on Spring's JIRA (https://jira.springsource.org/browse/SPR-11176) but I couldn't find much else on it.

Thanks in advance.

2

2 Answers

1
votes

I have now managed to override the Spring form taglib (3.0.7.RELEASE) in order to support the portlet namespaces. If you want to do this you'll have to look for this method in the Tag-classes (for example InputTag):

protected void writeDefaultAttributes(TagWriter tagWriter) throws JspException

You need to override this so it works in the namespace like so:

@Override
protected void writeDefaultAttributes(TagWriter tagWriter) throws JspException {
    writeOptionalAttribute(tagWriter, "name", getNamespace() + getName());
    writeOptionalAttribute(tagWriter, "id", getNamespace() + resolveId());
    super.writeDefaultAttributes(tagWriter);
}

Of course you'll need your own tagdescriptor in order to get the namespace in. Also you'll need to override the

protected int writeTagContent(TagWriter tagWriter) throws JspException;

method, because that's the one calling your writeDefaultAttributes-method.

This works for now, but I am still looking for a better way to go about this.

EDIT:

You can do it quicker with jQuery:

$(document).ready(function() {
    // Alle inputs
    $('input').each(function() {
        var pnamespace = '<portlet:namespace/>';
        $(this).attr('id', pnamespace + this.id);
        $(this).attr('name', pnamespace + this.name);
    });
    // alle selects
    $('select').each(function() {
        var pnamespace = '<portlet:namespace/>';
        $(this).attr('id', pnamespace + this.id);
        $(this).attr('name', pnamespace + this.name);
    });
});

This will put the namespace prefix on every input and select object. Now your portlet forms are namespaced correctly.

1
votes

As an alternative, you could use the aui tag lib. Note that the generated id will be the portlet name space followed by the name of the element.

<%@ taglib uri="http://alloy.liferay.com/tld/aui" prefix="aui" %>
<portlet:actionURL var="saveChartURL" name="saveChart" />
<aui:form action='${saveChartURL}' method='post' name='form'>
<aui:button type='button' name='save' value="Save" />
<aui:input id='json'
            type='textarea'
            name='jsonData'
            label=''
            spellcheck='false'  
            cssClass='json'
            style='disply:none;' />
</aui:form>
<script>
(function(run){run(window.jQuery)}(
    function($){
        $(document).ready(function(){
            var pNS = '<portlet:namespace/>';   
            $('#'+pNS+'save').click(function(){      
                var dataserialized = $('#'+pNS+'form').serialize();
                $.post( "${saveChartURL}", dataserialized );
            });
        }); 
    })
);
</script>

And in the Controller....

@ActionMapping(value="saveChart")
public void saveChart( 
    @RequestParam(defaultValue="{}") String jsonData, 
    @RequestParam(defaultValue="doughnut2d") String chartType,
    PortletPreferences pp) {

    /* TODO do Action */
}