Difference between revisions of "Creating a custom form field type"

From Joomla! Documentation

(Improved accuracy)
(Updated contents)
Line 1: Line 1:
 
[[JForm]], a feature introduced in Joomla 1.6, lets you easily create HTML forms (<code><form></code>). Forms created using JForm consist of [[Form field|form fields]], implemented as [[JFormField|JFormFields]]. There is a JFormField for each different field type you can find in a form, such as a text field type and a date field type. JForm supports a large selection of standard field types. For a full list, see [[Standard form field types]].
 
[[JForm]], a feature introduced in Joomla 1.6, lets you easily create HTML forms (<code><form></code>). Forms created using JForm consist of [[Form field|form fields]], implemented as [[JFormField|JFormFields]]. There is a JFormField for each different field type you can find in a form, such as a text field type and a date field type. JForm supports a large selection of standard field types. For a full list, see [[Standard form field types]].
  
Joomla 1.6 makes it possible to override and/or extend these field types. Here is how to do that.
+
Joomla 1.6 makes it possible to extend standard field types or define your own. For example, if your component manages phone book entries, you might want to define a form field type that outputs a select list of cities. There are several advantages to defining a custom form field type:
  
== Step 1: Location of files ==
+
* You will be able to mix standard field types with your custom field type in a JForm-based form.
 +
* You will eventually have a reusable code package that can be used easily throughout your code.
 +
* Extensions that collaborate with your extension will be able to create form fields without meddling with your database tables and other internals.
  
* The default form fields are located in "joomla/libraries/joomla/form/fields/"
+
== Location of files ==
* The overridden fields are usually located in "administrator/components/<name of your component>/models/fields", you can specify this or other path from your code:
+
 
 +
* The standard form field types are located in <code>joomla/libraries/joomla/form/fields/</code>. You should not store custom fields there, nor should you have to use this path in your own code, but the standard types are usually good examples.
 +
* The custom field types that belong to your component are usually located in <code>administrator/components/<name of your component>/models/fields</code>. You can specify this or another path in your code:
 
<source lang="php">JForm::addFieldPath(JPATH_COMPONENT . DS . 'models' . DS . 'fields');</source>
 
<source lang="php">JForm::addFieldPath(JPATH_COMPONENT . DS . 'models' . DS . 'fields');</source>
* The XML files with form fields are usually located in "administrator/components/<name of your component>/models/forms". Use something like the following snippet to specify a path to your forms:
+
* The XML files that define forms are usually located in <code>administrator/components/<name of your component>/models/forms</code>. Use something like the following snippet to specify a path to your forms:
 
<source lang="php">JForm::addFormPath(JPATH_COMPONENT . DS . 'models' . DS . 'forms');</source>
 
<source lang="php">JForm::addFormPath(JPATH_COMPONENT . DS . 'models' . DS . 'forms');</source>
  
== Step 2: Setting up base ==
+
== Naming conventions and skeleton ==
  
The first file to create is your base class. The base class is your starting point for overriding JFormField.
+
In this section, <ComponentName> represents the camel-cased name of your component and <TypeName> represents the camel-cased name of your form field type. The field's class should be placed in <code>administrator/components/<name of your component>/models/fields/<name of your field>.php</code>, and look like this:
  
Create a new file called myform.php and place it in the folder administrator/components/<name of your component>/models/fields. Add the following code to the file:
 
 
<source lang="php"><?php
 
<source lang="php"><?php
 
// Check to ensure this file is included in Joomla!
 
// Check to ensure this file is included in Joomla!
 
defined('_JEXEC') or die('Restricted access');
 
defined('_JEXEC') or die('Restricted access');
  
abstract class JFormFieldMyForm extends JFormField {
+
jimport('joomla.form.formfield');
 +
 
 +
class JFormField<FieldName> extends JFormField {
 
 
protected $type = 'MyForm';  
+
protected $type = '<FieldName>';
}
+
 
 +
public function getLabel() {
 +
// code that returns HTML that will be shown as the label
 +
}
 +
 
 +
public function getInput() {
 +
// code that returns HTML that will be shown as the form field
 +
}
 +
}  
 
</source>
 
</source>
  
 
Important lines:
 
Important lines:
* abstract class JFormField'''MyForm''' extends JFormField
+
* <code>class JFormField'''<FieldName>''' extends JFormField {</code>
The name of the file is myform.php this means the name of the class must be the same
+
*: Here we create our subclass called <FieldName> and make it extend JFormField. The class name must always be the same as the filename (in camel case).
* protected $type = ''' 'MyForm'''';
+
* <code>protected $type = '<FieldName>';</code>
The $type must also have the same name as your class
+
*: The field class must know its own type through the variable $type. This is the same name as your class name
 +
* <code>public function getLabel() {</code>
 +
*: This function will be called to create the label that belongs to your field and must return a HTML string containing it. You do not have to define this function; if you leave it out, the standard method of creating labels will be used.
 +
* <code>public function getInput() {</code>
 +
*: This function will be called to create the field itself and must return a HTML string containing it.
 +
 
 +
=== Grouping custom field types ===
  
== Step 3: Creating a field override ==
+
'''Warning: this information is partially incorrect and needs to be improved.'''
  
With the base class in place we can now create our field overrides.  
+
Custom field types can be grouped by using an underscore in the field name. A field class with a name for example like "JFormFieldMy_randomField" must be stored in <code>administrator/components/<name of your component>/models/fields/my/randomField.php</code>. We can prefix our form field names with some group name, then we put an underscore and then a name of a field.
 +
 
 +
== An example custom field type ==
 +
 
 +
Suppose you're working on your component named ''com_phonebook'' and you want to define a field that contains cities. Create the file <code>administrator/components/com_phonebook/models/fields/city.php</code> and write something similar to the following:
  
Create a new file called mylist.php and place it in the folder administrator/components/<name of your component>/models/fields. Add the following code to the file:
 
 
<source lang="php"><?php
 
<source lang="php"><?php
 
// Check to ensure this file is included in Joomla!
 
// Check to ensure this file is included in Joomla!
 
defined('_JEXEC') or die('Restricted access');
 
defined('_JEXEC') or die('Restricted access');
  
jimport('joomla.form.helper');
+
jimport('joomla.form.formfield');
JFormHelper::loadFieldClass('MyForm');
 
  
class JFormFieldMyText extends JFormFieldMyForm {
+
class JFormFieldCity extends JFormField {
 
 
protected $type = 'MyText';
+
protected $type = 'City';
 +
 
 +
// getLabel() left out
  
 
public function getInput() {
 
public function getInput() {
$field = JFormHelper::loadFieldType('Text');
+
return '<select id="'.$this->id.'" name="'.$this->name.'">'.
// Workaround
+
      '<option value="1" >New York</option>'.
$field->setForm($this->form);
+
      '<option value="2" >Chicago</option>'.
$field->setup($this->element, $this->value, $this->group);
+
      '<option value="3" >San Francisco</option>'.
 
+
      '</select>';
return $field->getInput();
 
 
}
 
}
 
}  
 
}  
 
</source>
 
</source>
  
Important lines:
+
=== Using the custom field type ===
* JFormHelper::loadFieldClass('MyForm');
 
Here we load our base class
 
* class JFormField'''MyText''' extends JFormField'''MyForm''' {
 
Here we create our override called MyText and make it extend MyForm, our base class. The class name must always be the same as the filename
 
* protected $type = ''' 'MyText'''';
 
Same as the base class the field class also needs a $type set, this is the same name as your class name
 
* public function getInput() {
 
A field override class must always have the public function getInput()
 
 
 
Notice: This example contains a workaround until bug 21644 (http://joomlacode.org/gf/project/joomla/tracker/?action=TrackerItemEdit&tracker_item_id=21644) has been fixed.
 
  
=== Grouping custom field types ===
+
To use the field type City, we need to update the XML file that contains the form fields. Open your XML file located in <code>administrator/components/com_phonebook/models/forms</code> and add the field in the usual way:
  
Custom field types can be grouped by using an underscore in the field name. A field class with a name for example like "JFormFieldMy_randomField" must be stored in "administrator/components/<name of your component>/models/fields/my/randomField.php". We can prefix our form field names with some group name, then we put an underscore and then a name of a field.
+
<source lang="xml"><field name="title" type="City" label="JGLOBAL_TITLE"
 
+
description="JFIELD_TITLE_DESC"
== Step 4: Using the override field ==
 
 
 
To use the override field MyText, we need to update the XML file that contains the form fields. Open your XML file located in administrator/components/<name of your component>/models/forms and find a field that is of the type text. This example is from article.xml:
 
<source lang="xml"><field name="title" type="text" label="JGLOBAL_TITLE"
 
description="JFIELD_TITLE_DESC" class="inputbox" size="30"
 
 
required="true" /></source>
 
required="true" /></source>
 +
The type name is cAsE-sEnSiTiVe.
  
We change this field to look like this:
+
== Overriding getLabel() ==
<source lang="xml"><field name="title" type="MyText" label="JGLOBAL_TITLE"
 
description="JFIELD_TITLE_DESC" class="inputbox" size="30"
 
required="true" /></source>
 
Notice how type="text" changed to type="'''MyText'''". The MyText here refers to the class made in step 3. The type name is cAsE-sEnSiTiVe.
 
  
With these changes in place, your override is done and ready to use.
+
'''Warning: this information is partially incorrect and needs to be improved.'''
 
 
== Overriding getLabel() ==
 
  
 
There are 2 ways of overriding the getLabel() method:
 
There are 2 ways of overriding the getLabel() method:

Revision as of 09:50, 7 June 2011

JForm, a feature introduced in Joomla 1.6, lets you easily create HTML forms (<form>). Forms created using JForm consist of form fields, implemented as JFormFields. There is a JFormField for each different field type you can find in a form, such as a text field type and a date field type. JForm supports a large selection of standard field types. For a full list, see Standard form field types.

Joomla 1.6 makes it possible to extend standard field types or define your own. For example, if your component manages phone book entries, you might want to define a form field type that outputs a select list of cities. There are several advantages to defining a custom form field type:

  • You will be able to mix standard field types with your custom field type in a JForm-based form.
  • You will eventually have a reusable code package that can be used easily throughout your code.
  • Extensions that collaborate with your extension will be able to create form fields without meddling with your database tables and other internals.

Location of files[edit]

  • The standard form field types are located in joomla/libraries/joomla/form/fields/. You should not store custom fields there, nor should you have to use this path in your own code, but the standard types are usually good examples.
  • The custom field types that belong to your component are usually located in administrator/components/<name of your component>/models/fields. You can specify this or another path in your code:
JForm::addFieldPath(JPATH_COMPONENT . DS . 'models' . DS . 'fields');
  • The XML files that define forms are usually located in administrator/components/<name of your component>/models/forms. Use something like the following snippet to specify a path to your forms:
JForm::addFormPath(JPATH_COMPONENT . DS . 'models' . DS . 'forms');

Naming conventions and skeleton[edit]

In this section, <ComponentName> represents the camel-cased name of your component and <TypeName> represents the camel-cased name of your form field type. The field's class should be placed in administrator/components/<name of your component>/models/fields/<name of your field>.php, and look like this:

<?php
// Check to ensure this file is included in Joomla!
defined('_JEXEC') or die('Restricted access');

jimport('joomla.form.formfield');

class JFormField<FieldName> extends JFormField {
	
	protected $type = '<FieldName>';

	public function getLabel() {
		// code that returns HTML that will be shown as the label
	}

	public function getInput() {
		// code that returns HTML that will be shown as the form field
	}
}

Important lines:

  • class JFormField<FieldName> extends JFormField {
    Here we create our subclass called <FieldName> and make it extend JFormField. The class name must always be the same as the filename (in camel case).
  • protected $type = '<FieldName>';
    The field class must know its own type through the variable $type. This is the same name as your class name
  • public function getLabel() {
    This function will be called to create the label that belongs to your field and must return a HTML string containing it. You do not have to define this function; if you leave it out, the standard method of creating labels will be used.
  • public function getInput() {
    This function will be called to create the field itself and must return a HTML string containing it.

Grouping custom field types[edit]

Warning: this information is partially incorrect and needs to be improved.

Custom field types can be grouped by using an underscore in the field name. A field class with a name for example like "JFormFieldMy_randomField" must be stored in administrator/components/<name of your component>/models/fields/my/randomField.php. We can prefix our form field names with some group name, then we put an underscore and then a name of a field.

An example custom field type[edit]

Suppose you're working on your component named com_phonebook and you want to define a field that contains cities. Create the file administrator/components/com_phonebook/models/fields/city.php and write something similar to the following:

<?php
// Check to ensure this file is included in Joomla!
defined('_JEXEC') or die('Restricted access');

jimport('joomla.form.formfield');

class JFormFieldCity extends JFormField {
	
	protected $type = 'City';

	// getLabel() left out

	public function getInput() {
		return '<select id="'.$this->id.'" name="'.$this->name.'">'.
		       '<option value="1" >New York</option>'.
		       '<option value="2" >Chicago</option>'.
		       '<option value="3" >San Francisco</option>'.
		       '</select>';
	}
}

Using the custom field type[edit]

To use the field type City, we need to update the XML file that contains the form fields. Open your XML file located in administrator/components/com_phonebook/models/forms and add the field in the usual way:

<field name="title" type="City" label="JGLOBAL_TITLE"
	description="JFIELD_TITLE_DESC"
	required="true" />

The type name is cAsE-sEnSiTiVe.

Overriding getLabel()[edit]

Warning: this information is partially incorrect and needs to be improved.

There are 2 ways of overriding the getLabel() method:

  1. Wrap it in your own tags
  2. Modify it completely

Wrapping the label[edit]

To wrap a form label you can add the following function to your myform.php file:

public function getLabel() {
     return '<span style="text-decoration: underline;">' . parent::getLabel() . '</span>';
}

This code will underline your form labels.

Writing your own label[edit]

To write your own label you can add the following function to your myform.php file:

public function getLabel() {
	// Initialize variables.
	$label = '';
	$replace = '';

	// Get the label text from the XML element, defaulting to the element name.
	$text = $this->element['label'] ? (string) $this->element['label'] : (string) $this->element['name'];

	// Build the class for the label.
	$class = !empty($this->description) ? 'hasTip' : '';
	$class = $this->required == true ? $class.' required' : $class;
		
	// Add replace checkbox
	$replace = '<input type="checkbox" name="update['.$this->name.']" value="1" />';
		
	// Add the opening label tag and main attributes attributes.
	$label .= '<label id="'.$this->id.'-lbl" for="'.$this->id.'" class="'.$class.'"';

	// If a description is specified, use it to build a tooltip.
	if (!empty($this->description)) {
		$label .= ' title="'.htmlspecialchars(trim(JText::_($text), ':').'::' .
				JText::_($this->description), ENT_COMPAT, 'UTF-8').'"';
	}

	// Add the label text and closing tag.
	$label .= '>'.$replace.JText::_($text).'</label>';
	
	return $label; 
}

This example will add a checkbox before the label.