Hi Daniel, Thanks for your thoughts, I appreciate!
On Sat, 4 May 2002 17:39:23 +0200, "Daniel Fagerstrom" <[EMAIL PROTECTED]> wrote: > [EMAIL PROTECTED] wrote: > > Hi Ivelin, > > > > On Mon, 29 Apr 2002 08:22:55 -0500, "Ivelin Ivanov" > > <[EMAIL PROTECTED]> 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 Your description provides a good way to integrate the two concepts. After spending some more time looking at the XMLForm, I have some fundamental questions: - why one needs to have flow when using XMLForm? XMLForm seems to be able to handle flow control just fine without the flow layer. It uses a finite state machine to describe it. - why do you need XMLForm when you have flow control? You can do the same things XMLForm does in the flow layer. ;) Now seriously, I think there's a lot of overlap between the two implementations right now. What I think we need is a way to describe, using Schematron, just as in XMLForm, the relationships between elements of the XML instance document. A stylesheet could then generate the JavaScript validation scripts, a set for the client browser, and another one for the server, very similar to the one for the client. The flow between pages can then be implemented using flow scripts. In the above approach, I'm obviously biased towards the flow layer, so I'd appreciate some more thoughts on it. What I'd also like to see is a way to reuse most of Ivelin's work, as I don't have much experience with it. -- As with how to code "prev" and "next" when using the flow script, I think there's a nicer solution to that if you take advantage of the continuations. The flow layer organizes related continuations in a tree. The root of the tree is the first continuation created after a function is invoked using <map:call function="..."> from the sitemap. Each time a new sendPage is invoked, a new Web continuation node is added in the tree, with the parent being the previous continuation the computation resumed from. In the jpath.xsl logicsheet you can access any ancestor Web continuation you want. Usually you simply use <jpath:continuation/> to generate the action for an HTML form. This accesses the current continuation created by sendPage(), which is a leaf continuation, and is accessed using the index 0 in the tree. To go upwards on the tree, you can use positive indices. Thus, to access the previous page in browsing order, you just do <jpath:continuation select="1"/>. To go two pages back, you say <jpath:continuation select="2"/> and so on. So in the sample code you gave above, there's no need for gotos and any of that. Just code the application the way you want it to, as with the cocoonSurvey() function, and place the appropriate markers in your XSP pages and things should work just fine. Regards, -- Ovidiu Predescu <[EMAIL PROTECTED]> http://www.geocities.com/SiliconValley/Monitor/7464/ (GNU, Emacs, other stuff) --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, email: [EMAIL PROTECTED]