Developer Area/Core Subsystems/Form API (Pieforms)/Form APIElements

From Mahara Wiki
Jump to: navigation, search

There are many elements that can be used in the form API, and it's easy to write new ones if necessary. This page describes the API for creating your own types, and details how each of the current types work.


The element API

Elements are stored in lib/form/elements/*.php, the filename being the name of the element as it should be referred to in the type section of the element array. The files should follow the library template as defined in BasicPHPFileTemplates. The subpackage name should be "form-element".

The file must contain one mandatory function, and some optional functions.

form_render_$type($element, Form $form)

This function returns the HTML to build the element. It takes the element array to build and the form the element is to be built for as arguments. There are some helper methods available for populating common attributes of any widgets in the element, such as tabindex, name, id etc.

Here is the form_render_text function, which renders a standard

element:
/**
* Provides a basic text field input.
*
* @param array $element The element to render
* @param Form  $form    The form to render the element for
* @return string        The HTML for the element
*/
function form_render_text($element, $form) {
    return
'         . Form::element_attributes($element)
        .
' value="' . hsc($form->get_value($element)) . '">';
}

In this function you can see the use of the two methods that help with creating attributes for the element - the static method Form::element_attributes and the method $form->get_value.

Form::element_attributes takes the element that the function was passed, and returns a standard HTML attribute string. This allows all elements to instantly support the following attributes without having to do any work:

  • accesskey
  • class
  • dir
  • disabled
  • id
  • lang
  • maxlength (only if a maxlength rule is declared on the element)
  • name
  • onclick
  • readonly
  • size
  • style
  • tabindex

The second paramter to element_attributes is an array of elements to ignore when building the attribute string. So if your elements do not use some, remove them this way.

The "value" attribute is not included, as how the value for an element is put into the HTML varies quite significantly depending on the element, and is also form dependent if the form has been submitted.

$form->get_value is how you get the value for the element. This takes into account the following things, in order:

  • Whether a callback function to get the value has been defined (see below)
  • Whether the 'value' key of the $element array has been specified
  • Whether the value has been submitted with the form
  • Whether the 'defaultvalue' key of the $element array has been specified

Ignoring the first option temporarily, this allows people to specify a 'value' attribute that becomes the value regardless of whether the element is changed when the form is posted, which is useful for hidden elements, form submit buttons and markup. It also allows them to set a default value, which will be overridden by any submitted data.

form_get_value_$type($element, Form $form)

This function is optional, and defines behaviour for getting the value of the $element from the request. This allows elements to do things like return a synthesised value if they are composed of many form widgets. For example, the 'date' element consists of three dropdown boxes, but the form_get_value_date function gets those value and returns a unix timestamp.

Elements implementing this should probably support 'value' and 'defaultvalue' in the same way as the behaviour above.

form_get_value_js_$type($element, Form $form)

This function is optional. When forms are submitted by AJAX, the value of each element needs to be retrieved via javascript to be placed in the request. By default, document.forms[formname].elements[elementname].value is used for the value, which works for most standard elements, however some elements (like the date picker) may be comprised of multiple values that need to be sent. This function gives these elements a way to specify the javascript required.

The alternative to having to specify this function (and also the PHP-based form_get_value_$type function), is to make the element serialise its value to a hidden input field named after the element, which is possible with a little javascript magic. This sidesteps the need for these functions, at the cost of making the element unusable for those with javascript turned off. Given that Mahara requires javascript however, that cost is eradicated.

The javascript returns needs to populate the javascript array field data['elementname'] with the correct value. Here is an example for the 'radio' element:

function form_get_value_js_radio($element, Form $form) {
    
$formname = $form->get_name();
    
$name = $element['name'];
    return <<
    var radio = document.forms['$formname'].elements['$name'];
    for (var i = 0; i < radio.length; i++)
{
        if (radio
[i].checked) {
            data
['$name'] = radio[i].value;
            break;
        
}
    
}

EOF;
}

NOTE: The javascript needs to make the data array have the values that are expected by the form_get_value_$type function (if there is one), or have the same type of value that would otherwise be expected by a user of the element in a non AJAX form.

form_render_${type}_set_attributes

This function is optional, and takes an element of its type and returns the element. This is a hook so that the elements can change, add or delete various indexes from the element, or do validation.

Example:

function form_render_select_set_attributes($element) {
    
$element['rules']['validateoptions'] = true;
    return
$element;
}

In this example, all select element automatically have the 'validateoptions' rule applied to them, which ensures that the only options that can be submitted come from the options inserted into the select to start with.

Elements

The following section lists all of the supported elements and their behaviour.

cancel

The cancel element is for a cancel button. When pressed, the cancel callback will be called, rather than the sumbit one, and no validation will take place.

It takes no special configuration values.

checkbox

This renders a simple checkbox, and automatically handles how checkbox values are not sent if they are not checked on form submission.

If the 'value' index is not empty, the checkbox is assumed to be checked. The checkbox element also has a 'checked' index, which behaves like the 'defaultvalue' index otherwise would. (note for later, this might be changed for consistency depending on developers' opinion).

date

This renders three select boxes, for choosing the year, month and day. Later on, it may render a calendar picker.

The value returned is a unix timestamp representation of the selected date.

The following configuration indexes apply to the date element:

  • minyear: The smallest year that should be in the 'year' dropdown
  • maxyear: The largest year that should be in the 'year' dropdown

The date element supports 'value' and 'defaultvalue' also, both of which should be an array in the form array($year, $month, $day).

fieldset

This renders a group of elements inside an HTML

element. The elements to render inside are specified by the 'elements' index.

Rather than have a 'title', fieldsets have a 'legend' index, which is used to render the fieldset legend.

Fieldsets do not have any of the other standard attributes, such as IDs or names, tab indexes etc.

file

This renders a basic

. Most likely broken right now when retrieving the value, harass Nigel about it if you need it.

hidden

This renders a

element for scalars, or multiple elements if the value is an array. This allows arrays to be transparently passed around between pages, which is incredibly useful for things like the transient login form, or for select elements that have are multiple selects.

Possibly contains more attributes than necessary when rendered right now, but that will not affect its general usage.

password

This renders an

element. This does not listen to 'defaultvalue' for security reasons, but its handling of values may need to be revamped later.

radio

This renders a series of

elements. The radio buttons should be specified in the 'options' index, in the form $value => $text.

select

This renders a

dropdown list, and supports multiple selections. The options should be specified in the 'options' index in the form $value => $text.

If there is only one option in the list, by default it will be "collapsed", so that only the readable version of the option is displayed, and a hidden input element will contain the value. If you wish to disable this behaviour, set the 'collapseifoneoption' field to false.

If the element is a multiple select, set the 'multiple' index to true. You can also set the size of the select using the 'size' attribute.

Example:

'select' => array(
        
'type' => 'select',
        
'title' => get_string('select'),
        
'options' => array(
            
1 => get_string('onebanana'),
            
2 => get_string('twobananas'),
            
3 => get_string('threebananas')
        ),
        
'defaultvalue' => 2
    
)

// a multiple select
    
'multiselect' => array(
        
'type' => 'select',
        
'title' => get_string('select'),
        
'multiple' => true,
        
'options' => array(
            
1 => get_string('onebanana'),
            
2 => get_string('twobananas'),
            
3 => get_string('threebananas')
        ),
        
'defaultvalue' => array(2, 3),
        
'size' => 3
    
)

submit

This renders an

element. Clicking one of these buttons will submit the form, and trigger the validation and submission callbacks (the submission one only if the validation succeeds).

submitcancel

This renders a submit button and a cancel button next to one another. The cancel function will need to be called '${formname}_cancel_submit'.

textarea

This renders a

. You can set the size either by using 'rows'/'cols', or 'width'/'height' (specified in pixels). If width and height are used, a value for rows and columns is automatically synthesised, to preserve HTML compliance.</p>

</div><div id="section_19"><a id="text"></a><h4 class="editable"><a name="text"></a>text</h4>
<p class="tightenable">This renders a basic <input type="text"> element, that works exactly as expected.</p>
</div><div id="section_20"><a id="wysiwyg"></a><h4 class="editable"><a name="wysiwyg"></a>wysiwyg</h4>
<p class="tightenable">In future, this will render a wysiwyg textarea based on the users' preferences. For now, it just renders a textarea.</p>
</div><div id="section_21"><a id="userlist"></a><h4 class="editable"><a name="userlist"></a>userlist</h4>
<p class="tightenable">This renders an element that allows selection of a subset of users of Mahara. It it rendered as two multiselect boxes coupled with a search feature, and users move "potential" users from the left box to the box on the right (selected members).</p>
<p class="tightenable">This element does <strong>NOT</strong> honour the 'value' attribute, nor any of the standard attributes (given it is a multi-element type element). It does however honour the 'defaultvalue' attribute.</p>
<p class="tightenable">Values passed to this element (via defaultvalue) and returned by the element (after a post/get) are <strong>always</strong> arrays (possibly the empty array) of user ids.</p>
<p class="tightenable">If 'filter' is set to false, the select box used to choose groups/communities for filtering is displayed.</p>
<p class="tightenable">If the 'required' rule is set, there must be at least one 'value' in the right hand side select list upon submission.</p>
<p class="tightenable">Example:</p>
<code><font color="#000000"> <font color="#dd0000">'userlist' </font><font color="#007700">=> array(<br />
        </font><font color="#dd0000">'type' </font><font color="#007700">=> </font><font color="#dd0000">'userlist'</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">'userlist'</font><font color="#007700">)<br />
        </font><font color="#dd0000">'defaultvalue' </font><font color="#007700">=> array(</font><font color="#0000bb">1</font><font color="#007700">, </font><font color="#0000bb">2</font><font color="#007700">, </font><font color="#0000bb">5</font><font color="#007700">),<br />
        </font><font color="#dd0000">'filter' </font><font color="#007700">=> </font><font color="#0000bb">false</font><font color="#007700">,<br />
        </font><font color="#dd0000">'rules' </font><font color="#007700">=> array(</font><font color="#dd0000">'required' </font><font color="#007700">=> </font><font color="#0000bb">true</font><font color="#007700">)<br />
    )<br />
);</font></font></code></div></div></html>