CQ5 and Sling Forms options and gotchas

A question I get a lot from CQ5 developers is: how do we write an MVC app using CQ5 or Sling? Well, there are many ways but as with these things, it depends on what features you need.  Do you want multi-form pages?  Do you want to pass data around?  Do you want validation?  The possibilities are endless.

If you look at the sling website, you can see you can build a quick form based application (46 line blog) and leverage the SlingPostServlet to handle simple sling operations like adding, updating, deleting nodes. See [1] for more info on the SlingPostServlet.

But what if you want to build your own form with bespoke processing logic and redirect to custom pages? Or better yet, what you want to build some form on CQ5 where you are also using the configurable CQ5 form and field components (or their derivatives)? There are many options. I’ll start by describing a few.

1. Use sling URLs in the form action which then gets handled by the script or class based on sling resource resolution. This is a general form that is the basis of all other options. One slight problem with this approach is that you can see the URL in the browser location changing to the URL path to the component itself (the action path).  For example, you’re going from a page.html to a page/_jcr_content/form.html.  This is undesirable because you don’t want the form action to contain all the view code of the whole result page.  If you don’t want action URLs like that, you can change the action path to the same as the page’s path but that will probably mean you have to put the POST.jsp in the page’s component folder which may or may not be something you want to do, especially if you want reusable form logic that can be put on different pages or sites.

2. Use the CQ5 forms components (and friends) to create forms, actions, and constraints.

The first one involves setting a path in the action which will be handled by the appropriately resolved component JSP or Servlet.  For example

<form action="<%resource.getPath()+".html"%>" method="POST" enctype="multipart/form-data">
...
</form>

If your component is /apps/myapp/myform/ and you put a myform.POST.jsp, then it will get handled there.  You’re also free to put in any other path which can be backed by a component’s POST.jsp or a child class of SlingAllMethodsServlet which can handle the call based on specified resourceType, method, selectors, and extensions.

The follow up from this, what happens after I’ve handled the form and want to the user to be taken to another page with some results.  In typical Java web frameworks, you’d be allowed to do a dispatcher.forward or include and that would just work.  In CQ5 or Sling, that won’t work as expected because the request method is still POST in this handler JSP or class. So when you do a dispatcher.forward, the SlingPostServlet (or some other registered script or servlet handling POST) comes into play again and will probably be trying to store form attributes to a certain node depending on how POST is handled by that path.  In order to get around this problem, you can can do two things:

1. Use a response.sendRedirect and pass data to the resulting page using query parameters or the less recommended session variables.

2. Write child request and response classes (inheriting SlingHttpServletRequestWrapper and SlingHttpServletResponseWrapper) which contain an overriden getMethod() always returning “GET”.  Then you can use the request dispatcher and pass those objects in and any additional data using request.setAttribute().  This is the mechanism that CQ5’s Forms “framework” is using.  This ensures that the next servlet or script in the chain is resolved by Sling using the HTTP GET method instead of POST.

Now onto using CQ5 form and field components. The detailed documentation of developing CQ5 forms are here [2]

The basic steps are:

1a. You use the OOTB CQ5 form start component and set it to a specific action in the config dialog.

OR

1b.  You have a hardcoded HTML form like above but with a hidden inputfield with name set to “:formstart” and optionally another input variable with name “:redirect”. The value should be the path to an action “component” (really it’s a folder and not a cq:Component).  The redirect variable is the path that you would like to end up at if all things go well.

2. Create an action folder (unless you’re using one of the default actions in CQ5 at foundation/components/form/actions) somewhere. In the action folder there are various JSPs that carry out different steps. The simplest case is to put a post.POST.jsp or a forward.jsp.

3. Put small bit of logic in a post.POST.jsp and either write stuff to the page OR do your redirect or forward to the success page.

OR

3a. Put a forward.jsp where you call FormsHelper.setForwardPath to forward to some other servlet or component to do the actual processing.  The line after the setForwardPath could optionally be FormsHelper.setRedirectToReferrer which would send the call to the original form page.

What this does is that the form URL does not change upon submission until you actually do the redirect. Even data is passed via request.setAttribute, that’s taken are of at the server side.

That leaves validation.  So CQ5 forms has a concept of constraints which are different validation types which you can set to the form element components.  If you choose them, then you get the benefit of the input being validated by a regex pattern and if you call LayoutHelper.printErrors, then it prints out a configurable error message.  You can use the default ones or build your own.  Adding clientvalidation.jsp or servervalidation.jsp takes care of specifying the regex patterns that will be used by the framework to evaluate the inputs. See examples in foundation/components/forms/constraints.  One thing I don’t like is the default client side validation uses alert() instead of printing the error beside the component like the server side validation. This can be remedied by overrides but it seems an awful lot of overrides or something common to all form fields which exist already.

If you choose not to use the CQ5 form elements or constraints, you can make your own and choose your own validation mechanisms and print them out however you want.  One option I’ve explored is using the OVal framework (need to install the OVal bundle in Felix).  See [3].  Basically it allows you to write POJOs which have annotations on the fields or methods and define what validation should apply to them. Then if you call

List<ConstraintViolation> violations = validator.validate(bo);

Then you can get a list of messages associated with each violation.

There might be other ways to pass data around forms and controllers using session variables, cookies, or other exotic ways using AJAX or window names and each will depend on your needs of security, volume of data, or other criteria.

For more examples of the things above, look through the foundation/components/forms/ folder as well as the founation/src/impl folder.

Happy exploring.

[1] http://sling.apache.org/site/manipulating-content-the-slingpostservlet-servletspost.html

[2] http://dev.day.com/docs/en/cq/current/developing/developing-forms.html

[3] http://oval.sourceforge.net/userguide.html

– Sarwar Bhuiyan

Advertisements
CQ5 and Sling Forms options and gotchas

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s