2

What is the best non-framework (JSP/Servlet only) way to create a form such that it can:

  1. Prepopulate with default values / data loaded from a database
  2. Redisplay submitted values which fail validation

It seems fairly straight forward to do one or the other, but creating a form that supports both simultaneously is tricky.

For example using

${param.scheduledDate}

works great for re-displaying a date on validation failure, but can't easily be set programmatically on page load. Conversely,

${myBean.scheduledDate} 

works great for displaying values loaded from a database, but can't re-display data on validation failure since the bean is using an object of type Date, not string.

A few things come to mind, but non really seem all that good:

  • use an intermediate bean with just strings
  • use a servlet filter to set parameters on page load
Ryan
  • 7,499
  • 9
  • 52
  • 61

2 Answers2

3

Do it in the view side. Use the conditional operator ?: in EL:

<input name="foo" value="${fn:escapeXml(empty bean.foo ? param.foo : bean.foo)}" />

Note that I assume that unlike GET the POST request doesn't prepopulate the ${bean} before validation has succeeded.

This verboseness is by the way one of the reasons why MVC frameworks exist. JSF for example handles this fully transparently by the EditableValueHolder interface which is implemented by among others UIInput components.

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • That might work, but it seems it would have some restrictions like you can only use a request scoped bean and bean properties cannot be null/empty. – Ryan Oct 24 '11 at 18:19
  • 1
    @Ryan: why would you like to use a session scoped bean for request scoped data? It'll only result in unintuitive website behaviour (and Bad User eXperience) when the same form is displayed over multiple browser windows/tabs within the same session. You don't want to have that. Use the session scope for real session scoped data only such as the logged-in user, its preferences, locale, etc. JSF has by the way the "view scope" for exactly your purpose (having a browser window/tab-specific scope). See also [this](http://balusc.blogspot.com/2011/09/communication-in-jsf-20.html#ManagedBeanScopes). – BalusC Oct 24 '11 at 18:32
  • @BalusC - I'm not actually using session scope - just trying to find a general solution. I think setting a boolean value in the controller to indicate if the view is a redisplay instead of checking if the bean value is empty might solve my concerns with both scope and property values allowing null. By the way, I actually used JSF with PrimeFaces for my last project. My current project is extremely small and I just want to better understand Java JSP/Servlet programming. No need to try to sell me on JSF. I'm starting to appreciate JSF more now though :) – Ryan Oct 24 '11 at 18:49
  • No need for a boolean. Just do GET actions (displaying initial form) in `doGet()` and do POST actions (submitting the form) in `doPost()` and treat every request as an fully independent request. Any additional state should preferably be passed by request parameters (which is what JSF does with `javax.faces.ViewState` by the way; yes I know that you used JSF, but my answer is more targeted on Stack Overflow community than only you :) Kudos though for wanting to better understand JSP/Servlet, it'll definitely lead to a better understanding of why MVC frameworks exist and how they work). – BalusC Oct 24 '11 at 18:50
  • Wouldn't using "empty bean" be just as good as using "empty bean.foo"? – Ryan Oct 24 '11 at 19:13
  • Well, depends. You might for example have some prepopulated data in readonly/disabled fields and thus really need the `${bean}` as model. You should only not prepopulate its properties with invalid submitted data. – BalusC Oct 24 '11 at 19:16
  • This becomes even trickier when you start to differentiate between a create and an edit (insert vs update in database). It is nice to be able to reuse the same page for both and use a statement such as ${empty bean.beanId ? 'Create' : 'Edit'} (use presence of ID to differentiate), but then the bean must be created on validation failure with an edit. This then brings back the issue with how to handle fields that are actually legitimately allowed to be empty! It seems adding a boolean attribute may be necessary in this case. – Ryan Nov 15 '11 at 15:24
  • Also, with an "edit an existing entity" scenario both the param and the bean will have values on a validation failure so a boolean appears to be required to instruct the view as to which one to use. – Ryan Nov 15 '11 at 15:47
  • Just don't prepare the existing entity in servlet unless validation succeeds. – BalusC Nov 15 '11 at 15:56
  • There are some read-only values used (not input elements), but I guess I could maintain them in hidden input fields so they won't be lost on validation failure redisplay. – Ryan Nov 15 '11 at 16:16
  • 2
    Yes, the ID for example should go in a hidden field. How else would you prepare the same entity on validation success? Not by storing it in session, right? – BalusC Nov 15 '11 at 16:19
  • Yeah, I had the id in a hidden field, but I had the value = ${bean.id}, but now I realize I need ${empty bean ? param.id : bean.id} just like the other fields. This change allows the ID to be maintained on validation failures. – Ryan Nov 15 '11 at 16:30
  • To avoid creating a boolean in the controller servlet for the create vs edit logic I guess the following will work in the view: ${empty bean.id and empty param.id ? 'Create' : 'Edit'} – Ryan Nov 15 '11 at 16:39
0

Forwarding the HTTP request back to the same page that posted the form violates the Post-Redirect-Get best practice and should be avoided. It is possible to save what the user submitted in the session or use URL parameters to save user submitted values and redirect back to the original page but that is not elegant. One of the best solutions for form submission is to use AJAX to submit the form.

Ryan
  • 7,499
  • 9
  • 52
  • 61
  • 1
    This has nothing to do with PRG. PRG is just there to ensure that you get exactly the desired result when you copypaste/share/link the current browser's address bar URL into a new browser session/window/tab. A forward after failed validation doesn't contribute to this problem at all. Food for read: http://stackoverflow.com/a/15523045 (true, JSF targeted, but applies as good on any other webapp). – BalusC Sep 30 '15 at 12:37
  • PRG is about bookmarkability, but also to maintain browser back button functionality and to avoid duplicate form submissions. If you refresh the page after posting a form that did not include a redirect in the response your browser will re-submit the form. Even ignoring PRG / page navigation issues the solution of "use AJAX" has other advantages such as performance when it comes to form submission. – Ryan Oct 06 '15 at 18:48