Developer Area/Core Subsystems/Form API (Pieforms): Difference between revisions
From Mahara Wiki
< Developer Area | Core Subsystems
Brettwilkins (talk | contribs) (Created page with "Pieforms is a development of the Mahara project, that has since been made into its own project on [http://sourceforge.net/projects/pieforms/ <span style="white-space: nowrap">[[I…") |
No edit summary |
||
Line 4: | Line 4: | ||
<div id="section_1"> | <div id="section_1"> | ||
=== Concepts === | ===Concepts=== | ||
; To create a form, you need three or four things | ; To create a form, you need three or four things | ||
Line 14: | Line 14: | ||
</div><div id="section_2"> | </div><div id="section_2"> | ||
=== A simple form === | ===A simple form=== | ||
Here is a simple form definition: | Here is a simple form definition: | ||
<div class="plugin tightenable"><code><font color="#000000"> <font color="#0000bb">$form </font><font color="#007700" | <div class="plugin tightenable"><code><font color="#000000"> <font color="#0000bb">$form </font><font color="#007700">= array(<br /> </font><font color="#dd0000">'name' </font><font color="#007700">=> </font><font color="#dd0000">'testform'</font><font color="#007700">,<br /> </font><font color="#dd0000">'method' </font><font color="#007700">=> </font><font color="#dd0000">'post'</font><font color="#007700">,<br /> </font><font color="#dd0000">'action' </font><font color="#007700">=> </font><font color="#dd0000"><nowiki>''</nowiki></font><font color="#007700">,<br /> </font><font color="#dd0000">'elements' </font><font color="#007700">= array(<br /> </font><font color="#dd0000">'login' </font><font color="#007700">=> array(<br /> </font><font color="#dd0000">'type' </font><font color="#007700">=> </font><font color="#dd0000">'fieldset'</font><font color="#007700">,<br /> </font><font color="#dd0000">'legend' </font><font color="#007700">=> </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'login'</font><font color="#007700">),<br /> </font><font color="#dd0000">'elements' </font><font color="#007700">=> array(<br /> </font><font color="#dd0000">'login_username' </font><font color="#007700">=> array(<br /> </font><font color="#dd0000">'type' </font><font color="#007700">=> </font><font color="#dd0000">'text'</font><font color="#007700">,<br /> </font><font color="#dd0000">'title' </font><font color="#007700">=> </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'username'</font><font color="#007700">),<br /> </font><font color="#dd0000">'description' </font><font color="#007700">=> </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'usernamedesc'</font><font color="#007700">),<br /> </font><font color="#dd0000">'help' </font><font color="#007700">=> </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'usernamehelp'</font><font color="#007700">),<br /> </font><font color="#dd0000">'rules' </font><font color="#007700">=> array(<br /> </font><font color="#dd0000">'required' </font><font color="#007700">=> </font><font color="#0000bb">true<br /> </font><font color="#007700">)<br /> ),<br /> </font><font color="#dd0000">'login_password' </font><font color="#007700">=> array(<br /> </font><font color="#dd0000">'type' </font><font color="#007700">=> </font><font color="#dd0000">'password'</font><font color="#007700">,<br /> </font><font color="#dd0000">'title' </font><font color="#007700">=> </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'password'</font><font color="#007700">),<br /> </font><font color="#dd0000">'description' </font><font color="#007700">=> </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'passworddesc'</font><font color="#007700">),<br /> </font><font color="#dd0000">'help' </font><font color="#007700">=> </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'passwordhelp'</font><font color="#007700">),<br /> </font><font color="#dd0000">'value' </font><font color="#007700">=> </font><font color="#dd0000"><nowiki>''</nowiki></font><font color="#007700">,<br /> </font><font color="#dd0000">'rules' </font><font color="#007700">=> array(<br /> </font><font color="#dd0000">'required' </font><font color="#007700">=> </font><font color="#0000bb">true<br /> </font><font color="#007700">)<br /> )<br /> )<br /> ),<br /><br /> </font><font color="#dd0000">'submit' </font><font color="#007700">=> array(<br /> </font><font color="#dd0000">'type' </font><font color="#007700">=> </font><font color="#dd0000">'submit'</font><font color="#007700">,<br /> </font><font color="#dd0000">'value' </font><font color="#007700">=> </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'login'</font><font color="#007700">)<br /> )<br /> )<br /> );<br /><br /> function </font><font color="#0000bb">testform_validate</font><font color="#007700">(</font><font color="#0000bb">Pieform $form</font><font color="#007700">, </font><font color="#0000bb">$values</font><font color="#007700">) {<br /> if (!</font><font color="#0000bb">$form</font><font color="#007700">-></font><font color="#0000bb">get_error</font><font color="#007700">(</font><font color="#dd0000">'login_username'</font><font color="#007700">) && !</font><font color="#0000bb">validate_username</font><font color="#007700">(</font><font color="#0000bb">$values</font><font color="#007700">[</font><font color="#dd0000">'login_username'</font><font color="#007700">])) {<br /> </font><font color="#0000bb">$form</font><font color="#007700">-></font><font color="#0000bb">set_error</font><font color="#007700">(</font><font color="#dd0000">'login_username'</font><font color="#007700">, </font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'usernamenotvalid'</font><font color="#007700">));<br /> }<br /> }<br /><br /> function </font><font color="#0000bb">testform_submit</font><font color="#007700">(</font><font color="#0000bb">$values</font><font color="#007700">) {<br /> global </font><font color="#0000bb">$SESSION</font><font color="#007700">;<br /> </font><font color="#0000bb">try </font><font color="#007700">{<br /> </font><font color="#0000bb">log_user_in</font><font color="#007700">(</font><font color="#0000bb">$values</font><font color="#007700">);<br /> </font><font color="#0000bb">$SESSION</font><font color="#007700">-></font><font color="#0000bb">add_ok_msg</font><font color="#007700">(</font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'userloggedinok'</font><font color="#007700">));<br /> }<br /> </font><font color="#0000bb">catch </font><font color="#007700">(</font><font color="#0000bb">SQLException $e</font><font color="#007700">) {<br /> </font><font color="#0000bb">$SESSION</font><font color="#007700">-></font><font color="#0000bb">add_err_msg</font><font color="#007700">(</font><font color="#0000bb">get_string</font><font color="#007700">(</font><font color="#dd0000">'couldnotloguserin'</font><font color="#007700">));<br /> }<br /> </font><font color="#0000bb">redirect</font><font color="#007700">(</font><font color="#0000bb">get_config</font><font color="#007700">(</font><font color="#dd0000">'wwwroot'</font><font color="#007700">));<br /> }<br /><br /></font><font color="#0000bb">$smarty </font><font color="#007700">= </font><font color="#0000bb">smarty</font><font color="#007700">();<br /></font><font color="#0000bb">$smarty</font><font color="#007700">-></font><font color="#0000bb">assign</font><font color="#007700">(</font><font color="#dd0000">'form'</font><font color="#007700">, </font><font color="#0000bb">pieform</font><font color="#007700">(</font><font color="#0000bb">$form</font><font color="#007700">));<br /></font><font color="#0000bb">$smarty</font><font color="#007700">-></font><font color="#0000bb">display</font><font color="#007700">(</font><font color="#dd0000">'template.tpl'</font><font color="#007700">);<br /></font> </font> </code></div> | ||
Here is a description of each part. | Here is a description of each part. | ||
Line 24: | Line 24: | ||
<div id="section_3"> | <div id="section_3"> | ||
==== The $form array ==== | ====The $form array==== | ||
This array contains the definition of the form. In this example the form is hard coded, but there's nothing to stop it from being generated dynamically. The array specifies some metadata for the whole form, like its name, method and action target, and then a list of elements that are on the form. Every form must have a name, this name is used to call the callback functions. | This array contains the definition of the form. In this example the form is hard coded, but there's nothing to stop it from being generated dynamically. The array specifies some metadata for the whole form, like its name, method and action target, and then a list of elements that are on the form. Every form must have a name, this name is used to call the callback functions. | ||
Line 32: | Line 32: | ||
</div><div id="section_4"> | </div><div id="section_4"> | ||
==== The testform_validate function ==== | ====The testform_validate function==== | ||
This function is named after the form name, with <code>_validate</code> on the end. It takes an array of the submitted values, and the form that is being validated, and can perform whatever checks are required that cannot be otherwise handled by the inbuilt form rules. If an error is detected, the method <code>$form->set_error</code> is called, to set an error message on the field. | This function is named after the form name, with <code>_validate</code> on the end. It takes an array of the submitted values, and the form that is being validated, and can perform whatever checks are required that cannot be otherwise handled by the inbuilt form rules. If an error is detected, the method <code>$form->set_error</code> is called, to set an error message on the field. | ||
Line 40: | Line 40: | ||
</div><div id="section_5"> | </div><div id="section_5"> | ||
==== the testform_submit function ==== | ====the testform_submit function==== | ||
This function is named after the form name, with <code>_submit</code> on the end. It takes an array of the submitted values that have already been validated successfully according to the validation rules set on the form. From here you can do almost anything, but the last thing that you should do is somehow display another page, otherwise the form will be re-presented to the user. | This function is named after the form name, with <code>_submit</code> on the end. It takes an array of the submitted values that have already been validated successfully according to the validation rules set on the form. From here you can do almost anything, but the last thing that you should do is somehow display another page, otherwise the form will be re-presented to the user. | ||
Line 48: | Line 48: | ||
</div><div id="section_6"> | </div><div id="section_6"> | ||
==== The pieform($form) function call ==== | ====The pieform($form) function call==== | ||
This call builds the form, validates and submits the form if it has been submitted, and returns the HTML for the form. So in the example above the form is simply assigned to a smarty template variable, which will be in the template wherever you want the form to be. | This call builds the form, validates and submits the form if it has been submitted, and returns the HTML for the form. So in the example above the form is simply assigned to a smarty template variable, which will be in the template wherever you want the form to be. | ||
Line 54: | Line 54: | ||
</div></div><div id="section_7"> | </div></div><div id="section_7"> | ||
=== Form API Terms === | ===Form API Terms=== | ||
Pieforms has several components, a basic description of each is given here. | Pieforms has several components, a basic description of each is given here. | ||
Line 60: | Line 60: | ||
<div id="section_8"> | <div id="section_8"> | ||
==== Elements ==== | ====Elements==== | ||
Pieform '''elements''' are similar to the widgets you have in normal HTML forms, and in fact all HTML widgets have element implementations in Pieforms. However, Pieforms does not limit you to using just the standard HTML widgets - you can write your own elements that have very advanced behaviour, are comprised of multiple widgets, or do almost anything you like. | Pieform '''elements''' are similar to the widgets you have in normal HTML forms, and in fact all HTML widgets have element implementations in Pieforms. However, Pieforms does not limit you to using just the standard HTML widgets - you can write your own elements that have very advanced behaviour, are comprised of multiple widgets, or do almost anything you like. | ||
Line 72: | Line 72: | ||
</div><div id="section_9"> | </div><div id="section_9"> | ||
==== Rules ==== | ====Rules==== | ||
Rules are used to validate elements. Given the element's value, they check if the value matches whatever condition they check. As of 2006/11/11 there are only four rules - required, minlength, maxlength and email - but again like elements, they are pluggable, so many more can be written. | Rules are used to validate elements. Given the element's value, they check if the value matches whatever condition they check. As of 2006/11/11 there are only four rules - required, minlength, maxlength and email - but again like elements, they are pluggable, so many more can be written. | ||
Line 82: | Line 82: | ||
</div><div id="section_10"> | </div><div id="section_10"> | ||
==== Renderers ==== | ====Renderers==== | ||
Renders are used to change the containers that elements are rendered inside. For example, there is a 'table' renderer which puts each element inside a table row, and a 'div' renderer that puts them inside a | Renders are used to change the containers that elements are rendered inside. For example, there is a 'table' renderer which puts each element inside a table row, and a 'div' renderer that puts them inside a | ||
Line 94: | Line 94: | ||
</div></div><div id="section_11"> | </div></div><div id="section_11"> | ||
=== How they fit together === | ===How they fit together=== | ||
Here are some statements that might help you describe how a form is put together using the form API. | Here are some statements that might help you describe how a form is put together using the form API. | ||
Line 110: | Line 110: | ||
</div><div id="section_12"> | </div><div id="section_12"> | ||
=== Other features of the Form API === | ===Other features of the Form API=== | ||
; As well as customisable elements, renderers and rules, the form API has the following features | ; As well as customisable elements, renderers and rules, the form API has the following features | ||
Line 124: | Line 124: | ||
</div><div id="section_13"> | </div><div id="section_13"> | ||
=== Major TODO items === | ===Major TODO items=== | ||
* Finish off javascript error setting, preferably so that the error HTML is exported by renderers as Mochikit DOM code, and that the 'error' class is set on the elements. | * Finish off javascript error setting, preferably so that the error HTML is exported by renderers as Mochikit DOM code, and that the 'error' class is set on the elements. |
Revision as of 16:13, 11 Mayıs 2011
Pieforms is a development of the Mahara project, that has since been made into its own project on File:Http.pngsourceforge. It provides a unified way to create, validate and process forms all with a common look and feel, with support for pluggable elements, renderers and validation rules.
Concepts
- To create a form, you need three or four things
- :* A hash containing the form definition. Basically, this is a big array describing overall form properties, and the elements for the form.
- An optional callback function that can be used to validate a form submission.
- A callback function that handles the submission of the form, given that all of the values submitted have been validated
- A call to the form() function, passing the form definition, which manages the entire form building, validating, submitting and displaying process.
A simple form
Here is a simple form definition:
$form = array(
'name' => 'testform',
'method' => 'post',
'action' => '',
'elements' = array(
'login' => array(
'type' => 'fieldset',
'legend' => get_string('login'),
'elements' => array(
'login_username' => array(
'type' => 'text',
'title' => get_string('username'),
'description' => get_string('usernamedesc'),
'help' => get_string('usernamehelp'),
'rules' => array(
'required' => true
)
),
'login_password' => array(
'type' => 'password',
'title' => get_string('password'),
'description' => get_string('passworddesc'),
'help' => get_string('passwordhelp'),
'value' => '',
'rules' => array(
'required' => true
)
)
)
),
'submit' => array(
'type' => 'submit',
'value' => get_string('login')
)
)
);
function testform_validate(Pieform $form, $values) {
if (!$form->get_error('login_username') && !validate_username($values['login_username'])) {
$form->set_error('login_username', get_string('usernamenotvalid'));
}
}
function testform_submit($values) {
global $SESSION;
try {
log_user_in($values);
$SESSION->add_ok_msg(get_string('userloggedinok'));
}
catch (SQLException $e) {
$SESSION->add_err_msg(get_string('couldnotloguserin'));
}
redirect(get_config('wwwroot'));
}
$smarty = smarty();
$smarty->assign('form', pieform($form));
$smarty->display('template.tpl');
Here is a description of each part.
The $form array
This array contains the definition of the form. In this example the form is hard coded, but there's nothing to stop it from being generated dynamically. The array specifies some metadata for the whole form, like its name, method and action target, and then a list of elements that are on the form. Every form must have a name, this name is used to call the callback functions.
More information about the $form array is available on a separate page.
The testform_validate function
This function is named after the form name, with _validate
on the end. It takes an array of the submitted values, and the form that is being validated, and can perform whatever checks are required that cannot be otherwise handled by the inbuilt form rules. If an error is detected, the method $form->set_error
is called, to set an error message on the field.
There are some inbuilt rules available for every element, such as "required", "minlength" and "maxlength". Both elements in the example form are required. These checks are performed before the validate function is called and will automatically set errors, which is why the check to get_error is made.
the testform_submit function
This function is named after the form name, with _submit
on the end. It takes an array of the submitted values that have already been validated successfully according to the validation rules set on the form. From here you can do almost anything, but the last thing that you should do is somehow display another page, otherwise the form will be re-presented to the user.
Most of the time you will display another page by using the File:Http.pngredirect function, which takes you to another page. To set a success/fail message, you can use the $SESSION
object to add messages, and they will be displayed on the next page that the user visits.
The pieform($form) function call
This call builds the form, validates and submits the form if it has been submitted, and returns the HTML for the form. So in the example above the form is simply assigned to a smarty template variable, which will be in the template wherever you want the form to be.
Form API Terms
Pieforms has several components, a basic description of each is given here.
Elements
Pieform elements are similar to the widgets you have in normal HTML forms, and in fact all HTML widgets have element implementations in Pieforms. However, Pieforms does not limit you to using just the standard HTML widgets - you can write your own elements that have very advanced behaviour, are comprised of multiple widgets, or do almost anything you like.
For example, there is a 'text' element, which represents a simple HTML
widget. But there is also a 'date' widget, which is currently rendered as three widgets for year, month, day, a 'wysiwyg' element which represents a full WYSIWYG editor, and a 'userlist' element that is made up of two boxes, with controls to move usernames from one to the other, and even allows ajax searching of the lists!
It's really easy to write your own elements, and they can be reused on any form that needs them. More information about elements, including how to write your own, is available.
Rules
Rules are used to validate elements. Given the element's value, they check if the value matches whatever condition they check. As of 2006/11/11 there are only four rules - required, minlength, maxlength and email - but again like elements, they are pluggable, so many more can be written.
Rules may apply differently to different elements, so elements can define a function that specifies how a rule applies to them, which allows some flexibility in rule reuse. (todo: actually, this is a lie - it only applies to the 'required' rule at the moment, and needs to be implemented at some point).
More information on rules? is available.
Renderers
Renders are used to change the containers that elements are rendered inside. For example, there is a 'table' renderer which puts each element inside a table row, and a 'div' renderer that puts them inside a
One thing that the Mahara project has done has added support for contextual help for each element. The renderer will render question marks beside each form element, which when clicked, reveal contextual help for the element. You could add almost any extra information to each element, to assist your form output - for example, a description, help text or raw HTML to be output at a certain point for each element.
Renderers? have their own page for more information (can you tell I'm running out of ways to say the same thing? :)
How they fit together
Here are some statements that might help you describe how a form is put together using the form API.
A form is made up of elements.
Elements are like standard HTML widgets, except they can be more complicated and comprised of many widgets if necessary.
Elements are validated by rules?.
Each element in a form is rendered inside some boilerplate HTML by a renderer?.
When forms are submitted, they are validated using the rules for each element. If validation passes, the form submit callback is called with the values. If validation fails, the form is redisplayed, with the renderer drawing the error messages as returned by the rule for each element next to it.
Other features of the Form API
- As well as customisable elements, renderers and rules, the form API has the following features
- :* send the form by POST or GET
- send the form to different pages for processing (as long as the form definition is available in both the sending and receiving scripts)
- support for cancel buttons - in effect, submit buttons with no validation applied to them
- auto focus of fields in a form on page load, including focussing the first element with an error if there is one
- ajax submission of the form, including display of success/failure notices
- automatic handling of tabindex for each element
- support for just rendering the form and not handling submission if required
- HTML 4.01 compliance
Major TODO items
- Finish off javascript error setting, preferably so that the error HTML is exported by renderers as Mochikit DOM code, and that the 'error' class is set on the elements.
- Add some more element types, like autocomplete. Implement 'wysiwyg' properly. Change the date picker to user a calendar.
- Handle multiple submit buttons
- Handle multipage forms
- Handle a tabbed interface type form