This page contains information on the default XSLT's provided by CForms, and the fi:styling directives they support.
As specified in templating mechanism (be sure to read about it), the form template file is transformed in a form instance xml file. This file contains all the data needed to properly display a form. An XSLT must then be used to transform the form instance to an HTML to be displayed in a browser.
This XSLT does not need to create also all the page layout (that can be added by other XSLT, or aggregating the form with other markup), it should only format the forms field and the form layout.
Cocoon forms already a set of XSLTs that does exactly this, they are "library" stylesheets :
- forms-field-styling.xsl: contains templates that style individual widgets, i.e. templates that translate fi:field, fi:booleanfield fi:action, etc. to HTML.
- forms-page-styling.xsl: contains templates for building high-level page layout effects, such as tabbed panes.
- forms-advanced-field-styling.xsl: contains templates that provide advanced styling of fields, e.g. the "double-listbox" for a multivaluefield. It's indeed an extension of the above basic forms-field-styling.xsl. Furthermore it includes the next stylesheet.
- forms-calendar-styling.xsl: contains the styling of a field with type "date" and provides a visual calendar for easy selection of date. So the calendar is an advanced styling too, but because it has much specific stuff we separated it out of forms-advanced-styling.xsl.
- forms-htmlarea-styling.xsl: contains the styling for the html edit field.
If you have a look at samples, you'll see also another XSLT, forms-samples-styling.xsl. This stylesheet includes other two stylesheets from the default set. Usually, you will make a clone of this stylesheet for your own project, have it include the library XSLTs, and customize what you need by writing templates in this XSLT by giving templates higher priority than those in the library ones.
From the sitemap you only need to reference only your custom XSLT, for example as follows:
The fi:styling elements are specified in the template, and offer a way to customize the way the library XSLTs render widgets. For example :
<ft:widget id="preferredBeer"> <fi:styling list-type="radio"/> </ft:widget>
The fi:styling options for the different widgets are documented in the respective sections about those widgets.
High-level styling with fi:group
It's quite common to want to layout the form. For example, have part of the form displayed in a table with two columns, one with labels the other one with actual fields, while in another part have a single colum with labels followed by fields, and in another part some tabs to group together fields.
We could do this simply writing HTML in the template file, but this is verbose, non reusable, and not easy to style. That's why fi:groups are there. You can layout your form with fi:groups in a simple, efficient, extensible and easy to style way. For example :
<ft:form-template .... > <fi:group> <fi:styling layout="columns"> <fi:items> <ft:widget id="name"/> <ft:widget id="surname"/> <ft:widget id="birthday"/> </fi:items> </fi:group> </ft:form-template>
Will produce a table with two columns, like the following :
The fi:group accept either a layout or a type attribute in its fi:styling element to specify the layout we want. This difference is because the "layout based" groups are quite lightweight, while the "type based" groups are more sophisticated and usually contains other groups.
<fi:group layout="column"> will format its items in a single column. One row will contain the label, the following row the input field.
<fi:group layout="columns"> will format its items in two columns. For each item a row is created, the first column will contain the label, and the second column the input field.
<fi:group layout="row"> will format its items in a single row. One cell will contain the label, the following cell the input field.
<fi:group layout="rows"> will format its items in two rows. The first row will contain all the labels, one each cell, the second row all the input fields, again one each cell.
Some input fields will have a special layout when inside a certain group :
- action, submit and boolanfield widgets does not have a "label" which can be displayed separated from the button or checkbox itself, so they will be placed
- in a single line inside the column and columns layout
- using only one cell in row layout
- using an empty cell in the first row of a rows layout
- multivaluefield with a list-type="double-listbox" will be displayed with a row with a label and another row with the two lists when in a columns group.
- every other fi:group nested inside a fi:group with layout columns will span on both columns
Also layout fi:group can contain other fi:group between their items, and can be used to create quite complex layouts.
There are currently two "type" groups : tabs and choice.
The tabs group will display some tabs, every tab is (usually) a fi:group. For example :
<fi:group> <fi:styling type="tabs"/> <fi:items> <fi:group> <fi:label>Personal info</fi:label> <fi:styling layout="columns"/> <fi:items> <ft:widget id="name"/> <ft:widget id="surname"/> </fi:items> </fi:group> <fi:group> <fi:label>Iternet data</fi:label> <fi:styling layout="columns"/> <fi:items> <ft:widget id="email"/> <ft:widget id="icqaccount"/> </fi:items> </fi:group> </fi:items> </fi:group>
Will display two tabs, one called "Personal info" and the other one "Internet data". When the user clicks on "Personal info" the field name and surname will be displayed, while when the user clicks on "Internet data" the field email and icqaccount will be displayed.
You can specify a widget that will be used to store and retrieve which tab is curently active. This way you can control from the flow which tab will be active when the form will be displayed, or check which tab was active when the user submitted the form. To specify this special field use :
<fi:group> <fi:styling type="tabs"/> <fi:state> <ft:widget id="activetab"/> </fi:state> .... </fi:group>
Obviously you can call the widget whatever you want, and the suggested datatype is integer, since the field will contain a number indicating the active tab, the field must be declared in the definition, and can be used in binding if needed. For example :
<fd:field id="activetab"> <fd:datatype base="integer"/> </fd:field>
The group of type choice is identical to the tabs group, but instead of a row of tabs, a drop down is displayed, and selecting one element of the drop down will display the relative group.
<fi:group> <fi:styling type="tabs"/> <fi:items> <fi:group formsOnShow="personalSelected()"> <fi:label>Personal info</fi:label> ... </fi:group> <fi:group formsOnShow="internetSelected()"> <fi:label>Iternet data</fi:label> ... </fi:group> </fi:items> </fi:group>
Have a look at forms-page-styling.xsl , all fi:groups are formatted there.
The fi:validation-errors tag is used to display all validation errors of all widgets in a form at one location, i.e. a the top of the form.
The fi:validation-errors tag must be a child of a ft:form-template element.
You can customise a message to be shown before and after the errors by adding a child header and/or footer element:
<fi:validation-errors> <header><p>Correct these errors please:</p></header> <footer><p>And then resubmit the form.</p></footer> </fi:validation-errors>