Het maken van een aangepast formulierveld type

From Joomla! Documentation

Revision as of 06:36, 10 August 2020 by FuzzyBot (talk | contribs) (Updating to match new version of source page)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Other languages:
Deutsch • ‎English • ‎Nederlands • ‎español • ‎français
Joomla! 
3.x
serie
Copyedit.png
This Article Needs Your Help

This article is tagged because it NEEDS REVIEW. You can help the Joomla! Documentation Wiki by contributing to it.
More pages that need help similar to this one are here. NOTE-If you feel the need is satistified, please remove this notice.


JForm, een functie geïntroduceerd in Joomla! 2.5, stelt u in staat eenvoudig HTML-formulieren te maken (<form>). Formulieren gemaakt met JForm bestaan uit formuliervelden, geïmplementeerd als JFormField. Er is een JFormField voor elk veldtype in een formulier, zoals een tekstveld en een datumveld. JForm ondersteunt een grote selectie aan standaard veldtypen. Voor een volledige lijst, zie Standaard formulierveld typen.

Joomla! maakt het mogelijk om de standaard veldtypen uit te breiden of om uw eigen te definiëren. Bijvoorbeeld, als uw component een telefoonboek beheert, zou u een veldtype kunnen definiëren dat een lijst van steden geeft. Er zijn verschillende voordelen als u een aangepast veldtype definieert:

  • U kunt standaard veldtypen samen gebruiken met uw aangepaste veldtype in een JForm-formulier.
  • U zult uiteindelijk een herbruikbaar code pakket hebben gecreëerd dat gemakkelijk gebruikt kan worden in de rest van uw code.
  • Extensies die samenwerken met uw extensie zullen in staat zijn om formuliervelden te maken zonder zich te bemoeien met uw database tabellen en andere interne zaken.

Een formulier veldtype is gedefinieerd in een class die een (niet noodzakelijkerwijs directe) subclass van JFormField moet zijn. Om correct te werken, moet de class tenminste drie methoden definiëren:

  • public function getLabel()
  • Deze functie wordt aangeroepen voor het maken van het label dat bij uw veld hoort en moet een HTML-tekenreeks teruggeven die het veld bevat. Omdat JFormField een klaar-om-te-gebruiken getLabel() implementatie definieert, definieren de aangepaste veldtypes meestal niet hun eigen getLabel(). Als je het leeg laat, zal de overgeërfde methode van het maken van labels worden gebruikt. Het wordt aanbevolen om de getLabel() methode voor consistentie en snelheid weg te laten, tenzij u echt de HTML-code van het label wilt wijzigen.
  • public function getInput()
  • Deze functie wordt aangeroepen om het veld zelf te maken en moet een HTML-tekenreeks teruggeven. Dit is ook waar de meeste verwerking plaatsvindt. In ons telefoonboek voorbeeld (veld stad), zal deze functie een lijst met beschikbare steden moeten opzoeken en de HTML <select> teruggeven met de steden ingevoegd als <option>.
  • public function getValue()
    Deze functie zal gebruikt worden om de veldwaarde aan te roepen. De waarde wordt verkregen vanuit de functie LoadFormData in het model.

Binnen uw code, moet u de attributen verwerken van de door de gebruikers van de velden in het XML formulier ingestelde definitie. Sommige van deze attributen zijn toegankelijk via protected member variabelen van JFormField. Het name attribuut is bijvoorbeeld in uw code beschikbaar als $this->name. Net zo zijn label, description, default, multiple en class beschikbaar als eigenschappen van $this. Andere parameters die u heeft gedefinieerd kunnen benaderd worden via de $this->element array: het attribuut size zal zitten in $this->element['size'].

Welke class wordt een subclass?

Om een formulier veldtype bruikbaar te laten zijn in JForm, moet het een subclass van JFormField zijn. Het hoeft echter geen direct kind van die class te zijn: u kunt ook een subclass maken van een bestaand (standaard of eigen) formulier veldtype en daardoor handige code overerven.

Als uw formulier veldtype ongeveer gelijk is aan een bestaand type, zou u een subclass van dat type moeten maken. Maak, vooral als uw formulier veldtype een list is, dan een subclass JFormFieldList. U hoeft alleen een override te maken van de getOptions() method om de opties te laten zien; de getInput() method converteert deze opties naar HTML.

Laad, om een subclass te maken van een bestaand type, bijvoorbeeld JFormFieldList, het dor het volgende toe te voegen na jimport('joomla.form.formfield');:

jimport('joomla.form.helper');
JFormHelper::loadFieldClass('list');

Als uw formulier veldtype ongelijk elk ander bestaand type is, maak dan een subclass direct van JFormField aan.

Locatie van bestanden

  • De standaard formulier veldtypes stan in libraries/joomla/form/fields/. U moet hier geen eigen velden opslaan. U hoeft dit pad ook niet in uw code te gebruiken, maar de standaard types zijn in het algemeen goed voorbeelden.
  • De eigen veldtypes die bij uw component horen staan meestal in administrator/components/<naam van uw component>/models/fields. U kunt dit of een ander pad in uw code opnemen:
JForm::addFieldPath(JPATH_COMPONENT . '/models/fields');
  • De XML bestanden die formulieren definiëren staan meestal in administrator/components/<naam van uw component>/models/forms. Gebruik iets als het volgende fragment om een pad naar uw formulieren te definiëren:
JForm::addFormPath(JPATH_COMPONENT . '/models/forms');

Naam-afspraken en skelet

In deze sectie, vertegenwoordigt <ComponentName> de met hoofdletters geschreven naam van uw component en <FieldName> vertegenwoordigt de met hoofdletters geschreven naam van uw formulier veldtype. De class van het veld class moet staan in administrator/components/<NaamVanUwComponent>/models/fields/<naam van uw veld>.php en ziet er zo uit:

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

jimport('joomla.form.formfield');

// The class name must always be the same as the filename (in camel case)
class JFormField<FieldName> extends JFormField {

	//The field class must know its own type through the variable $type.
	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
	}
}

Het groeperen van eigen veldtypes

Waarschuwing: deze informatie is gedeeltelijk onjuist en moet verbeterd worden.

Eigen veldtypes kunnen gegroepeerd worden door een underscore in de veldnaam te gebruiken. Een veld class met bijvoorbeeld als naam "JFormFieldMy_randomField" moet opgeslagen worden in administrator/components/<naam van uw component>/models/fields/my/randomField.php. We kunnen onze formulier veldnamen laten voorafgaan van een groepsnaam, daarna een underscore en daarna de naam van het veld.

Een voorbeeld van een eigen veldtype

Stel dat u werkt aan uw component genaamd com_phonebook en u wilt een veld definiëren dat steden bevat. Maak het bestand administrator/components/com_phonebook/models/fields/city.php en schrijf iets als het volgende:

<?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>';
	}
}

Een meer geavanceerde aanpak is de JFormFieldList class uitbreiden. Stel u wilt een drop-down lijst maken met steden uit de database gebaseerd op dynamische condities, dan kan dat op de volgende manier:

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

JFormHelper::loadFieldClass('list');

class JFormFieldCity extends JFormFieldList {

	protected $type = 'City';

	public function getOptions() {
                $app = JFactory::getApplication();
                $country = $app->input->get('country'); //country is the dynamic value which is being used in the view
                $db = JFactory::getDbo();
                $query = $db->getQuery(true);
                $query->select('a.cityname')->from('`#__tablename` AS a')->where('a.country = "'.$country.'" ');
		$rows = $db->setQuery($query)->loadObjectlist();
                foreach($rows as $row){
                    $cities[] = $row->cityname;
                }
                // Merge any additional options in the XML definition.
		$options = array_merge(parent::getOptions(), $cities);
                return $options;
	}
}

Bovenstaande voorbeeld toont een eenvoudige query die een lijst van steden uit een tabel toont waarbij de naam van de stad behoort tot het respectievelijk land. U kunt een drop-down maken op basis van meer complexe query's.

Setting the Values of a List Option and Using JSON or an API Instead of a Database Call

If you want to use an API call instead of a database call to build a custom list item, use the following code.

This field was created in a module. To get it to work it needs to be saved to

  • mod_modulename/models/fields/stackexchangesites.php

The naming convention is important as it is used within our function name.

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

// call the list field type
JFormHelper::loadFieldClass('list');

// The class name must always be the same as the filename (in camel case)
// extend the list field type
class JFormFieldStackexchangesites extends JFormFieldList
{
    //The field class must know its own type through the variable $type.
    protected $type = 'Stackexchangesites';

    // get the options for the list field
    public function getOptions()
    {
       // insert your JSON here or else call an API
        $json = {"api_site_parameter":"meta.stackoverflow","site_url":"https://meta.stackoverflow.com"~}
        // decode the JSON
        $sites = json_decode($json, true);
       
        // use a for each to iterate over the JSON
        foreach($sites['items'] as $site)
        {
           // choose the element of the JSON we want, and set it as a variable so we can use it in our array.
            $site = $site['api_site_parameter'];
            $site_url = $site['site_url'];
            // set an array and start adding values to it.  Set another array within our array to set our value / text items.
            $stackExchangesSitesOptions[] = array("value" => $site, "text" => $site_url);
        }

        // Merge any additional options in the XML definition.
        $options = array_merge(parent::getOptions(), $stackExchangesSitesOptions);
        return $options;
    }
}

On the front end simply calling the parameter gets us the value.

$stackexchangesites = $params->get('stackexchangesites');

Valkuilen

Het laden van een extra veld kan resulteren in een Fatal Error, als er een core veld bestaat met dezelfde bestandsnaam en het extra veld het core veld 'uitbreid'.

Beschouw een bestand testfields/radio.php dat bevat

<?php

class TestFormFieldRadio extends JFormFieldRadio {}

Het aanroepen van JFormHelper::loadFieldClass('radio') zal resulteren in een Fatal error: Class 'JFormFieldRadio' not found.

Er kunnen hier twee redenen voor zijn.

  1. JLoader kan JFormFieldRadio niet automatisch laden, want de class naam (JFormField*) komt niet overeen de padnaam (joomla/form/fields/* - let op het meervoud van fields).
  1. JFormHelper kan JFormFieldRadio niet laden, want custom paden worden eer gescanned en het gevraagde veldtype ('radio') wordt gevonden voor de core classes worden bereikt

Oplossing

Gebruik het core-veld bestand direct:

<?php
require_once JPATH_LIBRARIES . '/joomla/form/fields/radio.php';

class TestFormFieldRadio extends JFormFieldRadio {}

en gebruik JFormHelper::loadFieldClass naar behoren met 'test.radio' in plaats van 'radio'.

Met behulp van het aangepaste veldtype

Gelinkt aan een formulier

Om het veldtype te gebruiken, moeten we het XML bestand bijwerken dat het formulierveld bevat. Open uw XML bestand in administrator/components/com_phonebook/models/forms en voeg het veld op de gebruikelijke manier toe:

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

De attribuutnaam is HoOfDlEtTeRgEvOeLiG.

Bovendien moet u misschien het veld-pad naar de bovenliggende <fieldset> toevoegen:

<fieldset addfieldpath="/administrator/components/<component name>/models/fields">

Niet aan een formulier gelinkt

Als u bijvoorbeeld het veld nodig heeft als drop-down in een component als admin/site filter.

//Get custom field
JFormHelper::addFieldPath(JPATH_COMPONENT . '/models/fields');
$cities = JFormHelper::loadFieldType('City', false);
$cityOptions=$cities->getOptions(); // works only if you set your field getOptions on public!!

Overschrijven (overriden) getLabel()

Zoals genoemd in de sectie Formulierveld type class eisen, definiëren eigen formulier veldtypes in het algemeen hun eigen getLabel(). Als u een eigen label wilt aanmaken, kunt u toch gebruik maken van de getLabel() dat ieder veldtype class overerft van JFormField, door het bijvoorbeeld als volgt te definiëren:

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

Deze code zal uw formulierlabels onderstrepen. (Let op: Als uw doel is om formulierlabels te onderstrepen is het gebruik van CSS de aanbevolen manier.)

Als u iets compleet anders wilt doen, kunt u het ook volledig overriden:

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; 
}

Dit voorbeeld zal een keuzevakje in het label toevoegen.

Sample Component Code

Below is the code for a small component which you can install and run and can adapt to experiment further with custom fields. The component includes two custom fields

  1. A "City" field as described above (except that the options are hard-coded rather than being selected from a database). We'll also allow multiple Cities to be selected and pre-select two of them as default.
  2. A custom "time" field which maps to the HTML "time" input type and includes underlining the label and support for setting a minimum and maximum time.

Create the following five files in a folder called com_custom_fields. Then zip up this folder to create com_custom_fields.zip. Install this component on your Joomla instance. Once installed, navigate on your browser to your Joomla site and add to the URL the parameter &option=com_custom_fields. This should display the form with the two custom fields. You can submit the form and see the HTTP POST parameters using the browser development tools but the component doesn't contain any code which handles those parameters.

(As described in Basic form guide, the approach below isn't the recommended way to design Joomla components but it's written in this minimalist fashion to focus on the custom fields aspects).

com_custom_fields.xml Manifest file for the component

<?xml version="1.0" encoding="utf-8"?>
<extension type="component" version="3.1.0" method="upgrade">

	<name>com_custom_fields</name>
	<version>1.0.0</version>
	<description>Custom Fields demo component</description>
	
	<administration>
	</administration>

	<files folder="site">
		<filename>custom_fields.php</filename>
		<filename>form_definition.xml</filename>
		<filename>City.php</filename>
		<filename>Time.php</filename>
	</files>
</extension>

custom_fields.php The main code file which is run when an HTTP GET or POST is directed towards this component.

<?php
defined('_JEXEC') or die('Restricted Access');

use Joomla\CMS\Form\Form;

$form = Form::getInstance("sample", __DIR__ . "/form_definition.xml", array("control" => "jform"));
Form::addFieldPath(__DIR__);
?>
<form action="<?php echo JRoute::_('index.php?option=com_custom_fields'); ?>"
    method="post" name="adminForm" id="adminForm" enctype="multipart/form-data">

	<?php echo $form->renderField('mytime');  ?>
	
	<?php echo $form->renderField('mycity');  ?>

	<button type="submit">Submit</button>
</form>

form_definition.xml File containing the XML for the form definition, basically the two custom fields.

<?xml version="1.0" encoding="utf-8"?>
<form name="myForm">
	<field
		name="mytime"
		type="time"
		label="Time:"
		class="inputbox"
		min="09:00"
		max="17:30"
		required="true" />
	<field name="mycity" 
		type="City"
		label="City:"
		required="true"
		multiple="true"
		class="inputbox" />
</form>

City.php PHP code for the City custom field.

<?php
defined('_JEXEC') or die('Restricted access');
use Joomla\CMS\Form\FormHelper;

FormHelper::loadFieldClass('list');

class JFormFieldCity extends JFormFieldList {

	protected $type = 'City';

	public function getOptions() {
		$cities = array(
					array('value' => 1, 'text' => 'New York'),
					array('value' => 2, 'text' => 'Chicago'),
					array('value' => 3, 'text' => 'San Francisco'),
					);
		// Merge any additional options in the XML definition.
		$options = array_merge(parent::getOptions(), $cities);

		// pre-select values 2 and 3 by setting the protected $value property
		$this->value = array(2, 3);

		return $options;
	}
}

Time.php PHP code for the time custom field.

<?php
defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Form\FormField;

class JFormFieldTime extends FormField
{
	protected $type = 'time';

	protected function getInput()
	{
		// get relevant attributes which were defined in the XML form definition
		$attr = !empty($this->class) ? ' class="' . $this->class . '"' : '';
		$attr .= !empty($this->element['min']) ? ' min="' . $this->element['min'] . '"' : '';
		$attr .= !empty($this->element['max']) ? ' max="' . $this->element['max'] . '"' : '';

		// set up html, including the value and other attributes
		$html = '<input type="time" name="' . $this->name . '" value="' . $this->value . '"' . $attr . '/>';

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