cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Daniel Fagerstrom" <daniel.fagerst...@swipnet.se>
Subject RE: Flow and XMLForm
Date Sat, 04 May 2002 15:39:23 GMT
ovidiu@apache.org wrote:
> Hi Ivelin,
>
> On Mon, 29 Apr 2002 08:22:55 -0500, "Ivelin Ivanov"
> <ivelin@apache.org> wrote:
>
> > one of the action items on your list a couple weeks ago was to
> > evaluate the possibility of integration between Schecoon and the
> > XMLForm framework. Have you had time to do that yet?
>
> I looked at the code in scratchpad, but it's unclear to me how this
> integration could proceed with the current incarnations of XMLForm and
> the flow layer. I still need to spend more time thinking about this.
>
> Do you have any ideas about it?

I'm not Ivelin, but I have some ideas anyhow ;)

Background
----------

The aim of XMLForm, AFAIU, is to build and edit an xml document (called the
instance), subject to constraints from some schema (XMLSchema, Schematron,
...), through a sequence of form pages. The instance is either a
dom-document or a Java bean-structure or a mix. XMLForm consist, AFAIU, of
three main components:

* Form - is responsible for the instance and validation of it. Form objects
are stored in request attributes for one page forms and session attributes
for wizards (multi page forms). A Form can be populated from the request
parameters.

* XMLFormTransformer - takes a form descriptor, (similar to XForms) as input
and  fill it with data and error messages from a Form object that is
referred in an attribute "view".

* AbstractXMLFormAction, (WizardAction) - creates the Form object if
necessary and populates it with data based on the request parameters. It can
also take care of flow handling and checkbox state.


XMLForm - flow layer integration
--------------------------------

As we assume that there is anything to integrate, I guess that it is obvious
that the flow layer should be responsible for the flow handling. For wizards
there are two levels of flow handling: if a form is filled in in a faulty
way it should be resend with the faulty content and error messages. There is
also page flow where the choice of the next form page might depend on
earlier input.

The next question is: who should be responsible for handling the Form
object? In XMLForm, actions that inherit from AbstractXMLFormAction, e.g.
WizardAction is responsible for this. In an XMLForm - flow layer
integration, handling of the Form object should IMHO be the responsibility
for the flow layer. After all, the aim of a wizard flow script is to
(possibly through several form pages) update the state of an instance. If
this should be obvious from reading the flow script, the handling of the
Form object should be explicit in it.

One-page forms
--------------

To take care of a one-page form we need to:
1. Create and configure a Form object.
2. Create and send an html form page and prefill it with data from the form
object, return address (continuation), and possibly add error messages.
3. Read the input from a POST, populate the Form object with the input and
go back to step 2. if the input is invalid.

This could be coded as:

function getUserInput(form, view)
{
  do {
    sendPage("wizard/"+ view + ".html", {"id" : form.getId()});
    form.populate(cocoon.request());
  } while (!form.valid());
}

Here, the parameter "form" is a Form object and "view" is a string. I don't
follow the interfaces in XMLForm in every detail. Note that thanks to the
continuations, we can group together the form page code, followed by the
code that takes care of its output and not the other way around: take care
of input from last form followed by generating the next form page, as in the
sitemap.

<map:match pattern="*.html">
  <map:generate src="{1}.xml"/>
  <map:transform type="xmlform">
    <map:parameter name="id" value="{id}"/>
    <map:parameter name="action" value="{continuation}"/>
  <map:transform/>
  <map:transform src="stylesheets/wizard2html.xsl"/>
  <map:transform src="stylesheets/xmlform2html.xsl"/>
  <map:serialize type="html"/>
</map:match>

This is like in the example sitemap for XMLForm, with the differences that:
We have no XMLFormAction, its responsibilities are moved to the flow layer.
The XMLFormTransformer uses the continuation parameter, (from the flow
layer) for setting the "action" attribute of the form, and "id" for setting
the "id" attribute for referring to the Form object. The later part is not
necessary but I think it gives a better SoC if the form descriptors doesn't
know what instance they are supposed to update, if they know, you can't
reuse e.g. a "fill in user data form" between wizards.

Multi page forms
----------------

A wizard could be coded like:

function cocoonSurvey()
{
  form = new Form(...);

  getUserInput(form, "userIdentity");
  getUserInput(form, "deployment");
  getUserInput(form, "system");
  if (form.getValue("/system/os") == "Linux")
    getUserInput(form, "linuxDetails");
  else if (form.getValue("/system/os") == "MacOSX")
    getUserInput(form, "macOSXDetails");
  else if (form.getValue("/system/os") == "Windows")
    getUserInput(form, "windowsDetails");
  getUserInput(form, "confirm");

  storeResults(form, db);
}

Where the form constructor get default values, schema etc as input. I also
assume, (as I guess one generally assume while using the flow layer?), that
there is a submit button on each form page. If one want to edit earlier form
pages in the flow one is supposed to press submit on the current one before
using the backward button on the browser if one want to save the content of
the current form.

In Ivelins example there is a next and a previous button in the form pages.
If we want something like that in the flow layer the code gets a little bit
less neat:

...
  userIdentity: getUserInput(form, "userIdentity");
  deployment: getUserInput(form, "deployment");
  if (cocoon.request.getParameter("action") == "previous")
    goto userIdentity;
  system: getUserInput(form, "system");
  if (cocoon.request.getParameter("action") == "previous")
    goto deployment;
...

I'm not certain about the goto syntax in JS, but I guess you get the idea.
Is there a better way to code it?

I think there are at least three ways to attack this problem, (except for
writing something as ugly as the above code):
* Don't care about it and use a "submit" button instead of "next" and
"previous" buttons - I think this is the cleanest solution and I tend to
prefer it. On the other hand, after having met all kind of lousy
implementations of multi page forms I am a little bit reluctant to use the
"backward" button in my browser while filling in forms, I guess most people
have similar experience.
* Using finite state machines for describing the flow :(
* Adding some ingenious mechanism to the flow layer that handle
"previous"-buttons automagically.

Conclusion
----------

If I didn't miss anything important above, I think that XMLForm and the flow
layer can be integrated with a fairly small effort. In the example code
above I used some small additions to the Form class, but I don't think any
of them are necessary, the only thing that is necessary is to insert the
continuation address in the action attribute in the html form. This could be
done either in the XMLTransformer, as above or in one of the following
XSLTTransformers or maybe even by writing the form descriptors in XSP.

What do you think?

/Daniel Fagerstrom



---------------------------------------------------------------------
To unsubscribe, e-mail: cocoon-dev-unsubscribe@xml.apache.org
For additional commands, email: cocoon-dev-help@xml.apache.org


Mime
View raw message