Dear Wiki user, You have subscribed to a wiki page or wiki category on "Cocoon Wiki" for change notification.
The following page has been changed by JCKermagoret: http://wiki.apache.org/cocoon/DesignPattern/FormGeneration ------------------------------------------------------------------------------ - ## page was renamed from ECrudDesignPattern-FormGeneration - = Form generation = + = Intent = + "Write once, use many" paradigm applied to forms. Create form parts once, that you can aggregate and reuse across many forms, in many applications without any neither change nor redundancy. + + = Motivation = + Forms usually contains some identical information (System information, Author, Date and time concepts, ...) and differ only a little from mode to mode (create, update, search, detail). If we use forms framework in a quick approach, we usually define form (and configuration files) for each mode, and redundancy and complexity increase a lot, while maintenability decreases a lot too... The infernal spiral (direct french translation :-). + + We can have a more elaborate approach to avoid all these drawbacks. I will try to explain you this in the following lines. + + = Architecture = http://www.bluexml.org/static/images/form-generation-dp.gif - * Form definition : describes a form from the fields it contains + * Form definition : describes the fields contained in the form - * Form binding : describes the xml object you want to obtain. According the framework you use, you may convert it in Java object too for example + * Form binding : describes the xml object you want to obtain. According the framework you use, you may convert it in Java object too, for example - * Form template : describes how you want your fields to be viewed + * Form template : describes how you want your fields to be displayed - * Form filtering (see FilteringDesignPattern) : filters the 3 files above thanks to additional tags (the filters). This additional information may be useful after you read the current one. + * Filtering (see DesignPattern/Filtering) : filters and adapts files above thanks to additional tags (the filters) + = Advanced concepts = + To avoid redundancy, increase maintenance and reduce complexity, it may be a good idea : + * to implement class concept (at the field level), that will be aggregated and will define form repositories + * to define a metabind file for the binding and template file, associated with filter tags (see DesignPattern/Filtering) + * to define meta widgets in this metabind file, that will aggregate binding and templating tags in logical units you can reuse in different forms (like class for field and form definition) + + Just before starting, here is the directory structure : + {{{ + http://www.bluexml.org/static/images/class-form-generation-dp.gif + ||http://www.bluexml.org/static/images/doctype-form-generation-dp.gif + ||http://www.bluexml.org/static/images/metawidget-generation-dp.gif + }}} + == Class concept implementation == + First, we have a form repository that lists the form classes we need. Form repositories are stored in cud/doctypes directory. Above, we create a calendar doctype, composed of a few classes : + {{{ + <fd:form + xmlns:fd="http://apache.org/cocoon/forms/1.0#definition" + xmlns:fb="http://apache.org/cocoon/forms/1.0#binding" + xmlns:i18n="http://apache.org/cocoon/i18n/2.1" + xmlns:filter="http://bluexml.org/filter/1.0"> + + <fd:widgets id="calendar"> + <fd:new id="Base"/> + <fd:new id="Authoring"/> + <fd:new id="Classification"/> + <fd:new id="Calendar"/> + <fd:new id="Date"/> + <fd:new id="Time"/> + <fd:new id="Alarm"/> + <fd:new id="Security"/> + <fd:new id="NewVersion"/> + <fd:new id="Versions"/> + </fd:widgets> + + </fd:form> + }}} + Classes are in reality form definition. You will find above the form definition for the Calendar class. You may notice dynamic list. + {{{ + <fd:form xmlns:fd="http://apache.org/cocoon/forms/1.0#definition" + xmlns:fb="http://apache.org/cocoon/forms/1.0#binding" + xmlns:i18n="http://apache.org/cocoon/i18n/2.1" + xmlns:filter="http://bluexml.org/filter/1.0"> + + <fd:widgets> + <fd:class id="Calendar"> + <fd:widgets> + <fd:field id="title" required="true"> + <fd:datatype base="string"/> + </fd:field> + <fd:field id="description"> + <fd:datatype base="string"/> + </fd:field> + <fd:field id="type"> + <fd:datatype base="string"/> + <fd:selection-list src="cocoon:/process-selection-list" dynamic="true"/> + </fd:field> + <fd:field id="location"> + <fd:datatype base="string"/> + <fd:selection-list src="cocoon:/process-selection-list-specific?xpath=/document[(meta/doctype='ae_entity')and(meta/type='lieu')]&order=meta/name&value=$doc/meta/id&label=fn:string(fn:concat($doc/meta/name,%20'%20-%20',%20$doc/meta/city))" dynamic="true"/> + </fd:field> + <fd:field id="link"> + <fd:datatype base="string"/> + </fd:field> + <fd:field id="visibility"> + <fd:datatype base="string"/> + <fd:selection-list src="cocoon:/process-selection-list" dynamic="true"/> + </fd:field> + <fd:field id="calendar"> + <fd:datatype base="string"/> + <fd:selection-list src="cocoon:/process-selection-list" dynamic="true"/> + </fd:field> + </fd:widgets> + </fd:class> + </fd:widgets> + </fd:form> + }}} + With this first file, we'll have this final form definition. + + == Form binding and template == + I decided to mix binding and template because they are very tied. Structure and presentation are often (but not always) related. Moreover, it reduces compexity and increases maintenability. The use of meta widgets and filter tags help a lot to achieve this goal too. This way, one file only is necessary. + + === Metawidgets === + They are only a group of binding, template and filter tags. + + For example, you maybe use often DateTime widgets, that displays date and time. To avoid redundancy, you can define the following metawidget : + + {{{ + <?xml version="1.0" encoding="ISO-8859-1"?> + <!-- BX CPT --> + <fd:form + xmlns:fd="http://apache.org/cocoon/forms/1.0#definition" + xmlns:fb="http://apache.org/cocoon/forms/1.0#binding" + xmlns:i18n="http://apache.org/cocoon/i18n/2.1" + xmlns:filter="http://bluexml.org/filter/1.0" + xmlns:meta="http://bluexml.org/meta/1.0"> + + <meta:widget id="Time"> + <meta:group label="Début" styling-style="width: 300px;" styling-layout="rowWithLabel"> + <fb:value id="dtstart" path="dtstart"> + <fd:convertor datatype="date" type="formatting"/> + </fb:value> + <meta:group label="à " styling-layout="row"> + <fb:value id="tstart_hh" path="tstart_hh"/>h + <fb:value id="tstart_mm" path="tstart_mm"/>m + <fb:value id="tstart_ss" path="tstart_ss"/>s + </meta:group> + </meta:group> + <meta:group label="Fin" styling-style="width: 300px;" styling-layout="rowWithLabel"> + <fb:value id="dtend" path="dtend"> + <fd:convertor datatype="date" type="formatting"/> + </fb:value> + <meta:group label="à " styling-layout="row"> + <fb:value id="tend_hh" path="tend_hh"/>h + <fb:value id="tend_mm" path="tend_mm"/>m + <fb:value id="tend_ss" path="tend_ss"/>s + </meta:group> + </meta:group> + </meta:widget> + + </fd:form> + }}} + + To include this set of definition (31 lines), you just have to put the following line in your metabind file : + {{{ + <meta:new id="DateTime"/> + }}} + + === Metabind file === + In the example below, a calendar doctype is defined. Usual bindings for DateTime (among others) are reused through the DateTime metawidget. + + {{{ + <fb:context + xmlns:fd="http://apache.org/cocoon/forms/1.0#definition" + xmlns:fb="http://apache.org/cocoon/forms/1.0#binding" + xmlns:fi="http://apache.org/cocoon/forms/1.0#instance" + xmlns:ft="http://apache.org/cocoon/forms/1.0#template" + xmlns:filter="http://bluexml.org/filter/1.0" + xmlns:meta="http://bluexml.org/meta/1.0" + path="document"> + + <fb:context path="meta"> + <meta:group + styling-type="categories" + label="General" + state="internal-tab-state"> + + <meta:group styling-layout="columns" label="General"> + <meta:new id="General"/> + <meta:new id="DateTime"/> + </meta:group> + <meta:group styling-type="fieldset" styling-layout="columns" label="Compl."> + <fb:value id="visibility" path="visibility"/> + <fb:value id="location" path="location"/> + <fb:value id="link" path="link"/> + </meta:group> + <meta:new id="Alarm"/> + <meta:new id="Classification"/> + <meta:new id="NewVersion"/> + <meta:new id="System"/> + </meta:group> + </fb:context> + <meta:new id="Versions"/> + <ft:widget id="action.ok"/> + </fb:context> + }}} + + === Form binding === + + From this metabind file, we create the following form binding (by just changing the last parameter) : + * [http://www.bluexml.org/crud-engine/form/cud/getMFM(BindingURI-atlanticevent-sysadmin-create) create] + * [http://www.bluexml.org/crud-engine/form/cud/getMFM(BindingURI-atlanticevent-sysadmin-update) update] + * [http://www.bluexml.org/crud-engine/form/cud/getMFM(BindingURI-atlanticevent-sysadmin-search) search] + + From this same metabind file, we may have different form binding for user's role (but it is not configured here). + + === Form template === + + From this metabind file, we create this form template (create, update, search). + * [http://www.bluexml.org/crud-engine/form/cud/getMFM(TemplateURI-atlanticevent-sysadmin-create) create form template] - [http://www.bluexml.org/static/images/screenshot-form-sysadmin-create.gif "real app's screenshot"] + * [http://www.bluexml.org/crud-engine/form/cud/getMFM(TemplateURI-atlanticevent-sysadmin-update) update] - [http://www.bluexml.org/static/images/screenshot-form-sysadmin-update.gif "real app's screenshot"] + * [http://www.bluexml.org/crud-engine/form/cud/getMFM(TemplateURI-atlanticevent-sysadmin-search) search] - [http://www.bluexml.org/static/images/screenshot-form-sysadmin-search.gif "real app's screenshot"] + + = Code = + Here is the global idea. All the code is part of the BlueXML project, located in http://www.bluexml.org. Just download the code in the download section. + + You may have a look through the cvs browser at : + * [http://www.bluexml.org/viewcvs/viewcvs.cgi/cms/src/blocks/core/crud-engine/webapp/feature/form/cud/ "cud directory feature"] - for sitemap, + * [http://www.bluexml.org/viewcvs/viewcvs.cgi/cms/src/data/project/webapp/common/coplets/ged/cud/ "cud directory configuration"] - for common configuration files (other directories are for specific data for each customer) + + = Conclusion = + Here is the global idea. + Maybe we could implement this at the Forms framework core ? +
