class Select2 in Select 2 8
Same name in this branch
- 8 src/Element/Select2.php \Drupal\select2\Element\Select2
- 8 src/Plugin/better_exposed_filters/filter/Select2.php \Drupal\select2\Plugin\better_exposed_filters\filter\Select2
Provides an select2 form element.
Properties:
- #cardinality: (optional) How many options can be selected. Default is unlimited.
- #autocomplete_options_callback: (optional) A callback to return all valid currently selected options. - #autocomplete_route_callback: (optional) A callback that sets the #autocomplete_route_name and autocomplete_route_parameters keys on the render element.
Simple usage example:
$form['example_select'] = [
'#type' => 'select2',
'#title' => $this->t('Select element'),
'#options' => [
'1' => $this->t('One'),
'2' => [
'2.1' => $this->t('Two point one'),
'2.2' => $this->t('Two point two'),
],
'3' => $this->t('Three'),
],
];
If you want to prevent the rendering of all options and fetch the options via
ajax instead, you can use the '#autocomplete' property. It's also needed to
specify which entities are available with '#target_type',
'#selection_handler' and '#selection_settings'.
@code
$form['my_element'] = [
'#type' => 'select2',
'#title' => $this->t('Select element'),
'#options' => [
'1' => $this->t('One'),
'2' => $this->t('Two'),
'3' => $this->t('Three'),
],
'#autocomplete' => TRUE,
'#target_type' => 'node',
// The selection handler is optional and pre-populated to 'default'.
'#selection_handler' => 'default',
'#selection_settings' => [
'target_bundles' => ['article', 'page'],
],
];
If you want to allow an input of an entity label that does not exist yet but
can be created "on the fly" on form submission, the '#autocreate' property
can be used:
@code
// #autocreate should be an array where the 'bundle' key is required and
// should be the bundle name for the new entity.
// The 'uid' key of the #autocreate array is optional and defaults to the
// current logged-in user. It should be the user ID for the new entity,
// if the target entity type implements \Drupal\user\EntityOwnerInterface.
$form['my_element'] = [
'#type' => 'select2',
'#target_type' => 'taxonomy_term',
'#autocreate' => [
'bundle' => 'tags',
'uid' => <a valid user ID>,
],
];
The render element sets a bunch of default values to configure the select2
element. Nevertheless all select2 config values can be overwritten with the
'#select2' property.
@code
$form['my_element'] = [
'#type' => 'select2',
'#select2' => [
'allowClear' => TRUE,
],
];
<h3>Plugin annotation</h3>
@code
@FormElement("select2")
Hierarchy
- class \Drupal\Component\Plugin\PluginBase implements DerivativeInspectionInterface, PluginInspectionInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
- class \Drupal\Core\Render\Element\RenderElement implements ElementInterface
- class \Drupal\Core\Render\Element\FormElement implements FormElementInterface
- class \Drupal\Core\Render\Element\Select
- class \Drupal\select2\Element\Select2 uses Select2Trait
- class \Drupal\Core\Render\Element\Select
- class \Drupal\Core\Render\Element\FormElement implements FormElementInterface
- class \Drupal\Core\Render\Element\RenderElement implements ElementInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
Expanded class hierarchy of Select2
See also
static::getValidSelectedOptions().
static::setAutocompleteRouteParameters().
https://select2.org/configuration/options-api
1 file declares its use of Select2
- Select2Test.php in tests/
src/ Unit/ Element/ Select2Test.php
2 string references to 'Select2'
- select2.info.yml in ./
select2.info.yml - select2.info.yml
- select2.schema.yml in config/
schema/ select2.schema.yml - config/schema/select2.schema.yml
7 #type uses of Select2
- Select2AjaxForm::buildForm in tests/
modules/ select2_form_test/ src/ Form/ Select2AjaxForm.php - Form constructor.
- Select2AutocompleteForm::buildForm in tests/
modules/ select2_form_test/ src/ Form/ Select2AutocompleteForm.php - Form constructor.
- Select2OptgroupForm::buildForm in tests/
modules/ select2_form_test/ src/ Form/ Select2OptgroupForm.php - Form constructor.
- Select2Test::testAutocompleteOptions in tests/
src/ Kernel/ Element/ Select2Test.php - Tests that in autocomplete are only the default options rendered.
- Select2Test::testEmptyOption in tests/
src/ Kernel/ Element/ Select2Test.php - Tests that an empty option is added or not.
File
- src/
Element/ Select2.php, line 97
Namespace
Drupal\select2\ElementView source
class Select2 extends Select {
use Select2Trait;
/**
* {@inheritdoc}
*/
public function getInfo() {
$info = parent::getInfo();
$class = get_class($this);
// Apply default form element properties.
$info['#target_type'] = NULL;
$info['#selection_handler'] = 'default';
$info['#selection_settings'] = [];
$info['#autocomplete'] = FALSE;
$info['#autocreate'] = [];
$info['#empty_value'] = '';
$info['#cardinality'] = 0;
$info['#pre_render'][] = [
$class,
'preRenderAutocomplete',
];
$info['#pre_render'][] = [
$class,
'preRenderOverwrites',
];
$info['#element_validate'][] = [
$class,
'validateEntityAutocomplete',
];
$info['#select2'] = [];
return $info;
}
/**
* {@inheritdoc}
*/
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
// Potentially the #value is set directly, so it contains the 'target_id'
// array structure instead of a string.
if ($input !== FALSE && is_array($input)) {
$input = array_map(function ($item) {
return isset($item['target_id']) ? $item['target_id'] : $item;
}, $input);
return array_combine($input, $input);
}
return parent::valueCallback($element, $input, $form_state);
}
/**
* Form element validation handler for entity_autocomplete elements.
*
* @param array $element
* The render element.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state object.
* @param array $complete_form
* The form array.
*/
public static function validateEntityAutocomplete(array &$element, FormStateInterface $form_state, array &$complete_form) {
if ($element['#target_type'] && !$element['#autocreate']) {
$value_callable = isset($element['#autocomplete_options_callback']) ? $element['#autocomplete_options_callback'] : NULL;
if (!$value_callable || !is_callable($value_callable)) {
$value_callable = '\\Drupal\\select2\\Element\\Select2::getValidSelectedOptions';
}
$value = [];
$input_values = call_user_func_array($value_callable, [
$element,
$form_state,
]);
foreach ($input_values as $id => $input) {
$value[] = [
'target_id' => $id,
];
}
$form_state
->setValueForElement($element, $value);
}
}
/**
* {@inheritdoc}
*/
public static function processSelect(&$element, FormStateInterface $form_state, &$complete_form) {
// Fill the options, because in autocomplete we cleared them and for the
// validation the at least selected options are needed.
if ($element['#autocomplete']) {
$value_callable = isset($element['#autocomplete_options_callback']) ? $element['#autocomplete_options_callback'] : NULL;
if (!$value_callable || !is_callable($value_callable)) {
$value_callable = '\\Drupal\\select2\\Element\\Select2::getValidSelectedOptions';
}
$element['#options'] = call_user_func_array($value_callable, [
$element,
$form_state,
]);
}
// We need to disable form validation, because with autocreation the options
// could contain non existing references. We still have validation in the
// entity reference field.
if ($element['#autocreate'] && $element['#target_type']) {
unset($element['#needs_validation']);
// Add back auto_create values.
$values = is_array($element['#value']) ? $element['#value'] : [
$element['#value'],
];
foreach ($values as $key => $value) {
if (is_string($key) && substr($key, 0, 4) === "\$ID:") {
// Set option and remove ID from label.
$element['#options'][$key] = substr($value, 0, 4) === "\$ID:" ? substr($value, 4) : $value;
}
elseif (!$element['#multiple'] && substr($value, 0, 4) === "\$ID:") {
$element['#options'][$value] = substr($value, 4);
}
}
}
if (!$element['#multiple'] && !isset($element['#options'][$element['#empty_value']])) {
$empty_option = [
$element['#empty_value'] => '',
];
$element['#options'] = $empty_option + $element['#options'];
}
// Set the type from select2 to select to get proper form validation.
$element['#type'] = 'select';
return $element;
}
/**
* Get an array of currently selected options.
*
* @param array $element
* The render element.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state object.
*
* @return array
* Key => entity ID, Value => entity label.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
protected static function getValidSelectedOptions(array $element, FormStateInterface $form_state) {
$handler_settings = $element['#selection_settings'] + [
'target_type' => $element['#target_type'],
'handler' => $element['#selection_handler'],
];
$value = is_array($element['#value']) ? $element['#value'] : [
$element['#value'],
];
return $value ? static::getValidReferenceableEntities($value, $handler_settings) : [];
}
/**
* {@inheritdoc}
*/
public static function preRenderSelect($element) {
$element = parent::preRenderSelect($element);
$required = isset($element['#states']['required']) ? TRUE : $element['#required'];
$multiple = $element['#multiple'];
if ($multiple) {
$element['#attributes']['multiple'] = 'multiple';
$element['#attributes']['name'] = $element['#name'] . '[]';
}
$current_language = \Drupal::languageManager()
->getCurrentLanguage();
$current_theme = \Drupal::theme()
->getActiveTheme()
->getName();
$select2_theme_exists = \Drupal::service('library.discovery')
->getLibraryByName($current_theme, 'select2.theme');
// Placeholder should be taken from #placeholder property if it set.
// Otherwise we can take it from '#empty_option' property.
$placeholder_text = $required ? new TranslatableMarkup('- Select -') : new TranslatableMarkup('- None -');
$placeholder = [
'id' => '',
'text' => $placeholder_text,
];
if (!empty($element['#empty_value'])) {
$placeholder['id'] = $element['#empty_value'];
}
if (!empty($element['#placeholder'])) {
$placeholder['text'] = $element['#placeholder'];
}
elseif (!empty($element['#empty_option'])) {
$placeholder['text'] = $element['#empty_option'];
}
// Defining the select2 configuration.
$settings = [
'multiple' => $multiple,
'placeholder' => $placeholder,
// @todo Enable allowClear for multiple fields. https://github.com/select2/select2/issues/3335.
'allowClear' => !$multiple && !$required,
'dir' => $current_language
->getDirection(),
'language' => $current_language
->getId(),
'tags' => (bool) $element['#autocreate'],
'theme' => $select2_theme_exists ? $current_theme : 'default',
'maximumSelectionLength' => $multiple ? $element['#cardinality'] : 0,
'tokenSeparators' => $element['#autocreate'] ? [
',',
] : [],
'selectOnClose' => $element['#autocomplete'],
'width' => '100%',
];
$element['#attributes']['class'][] = 'select2-widget';
$element['#attributes']['data-select2-config'] = $settings;
// Adding the select2 library.
$element['#attached']['library'][] = 'select2/select2';
$element['#attached']['library'][] = 'select2/select2.i18n.' . $current_language
->getId();
if ($select2_theme_exists) {
$element['#attached']['library'][] = $current_theme . '/select2.theme';
}
return $element;
}
/**
* Attach autocomplete behavior to the render element.
*/
public static function preRenderAutocomplete($element) {
if (!$element['#autocomplete']) {
return $element;
}
$value_callable = isset($element['#autocomplete_route_callback']) ? $element['#autocomplete_route_callback'] : NULL;
if (!$value_callable || !is_callable($value_callable)) {
$value_callable = '\\Drupal\\select2\\Element\\Select2::setAutocompleteRouteParameters';
}
$element = call_user_func_array($value_callable, [
&$element,
]);
// Reduce options to the preselected ones and bring them in the correct
// order.
$options = OptGroup::flattenOptions($element['#options']);
$values = isset($element['#value']) ? $element['#value'] : $element['#default_value'];
$values = is_array($values) ? $values : [
$values,
];
$element['#options'] = [];
foreach ($values as $value) {
if (isset($options[$value])) {
$element['#options'][$value] = $options[$value];
}
}
/** @var \Drupal\Core\Access\AccessManagerInterface $access_manager */
$access_manager = \Drupal::service('access_manager');
$access = $access_manager
->checkNamedRoute($element['#autocomplete_route_name'], $element['#autocomplete_route_parameters'], \Drupal::currentUser(), TRUE);
if ($access && $access
->isAllowed()) {
$url = Url::fromRoute($element['#autocomplete_route_name'], $element['#autocomplete_route_parameters'])
->toString(TRUE);
// Provide a data attribute for the JavaScript behavior to bind to.
$element['#attributes']['data-select2-config'] += [
'minimumInputLength' => 1,
'ajax' => [
'delay' => 250,
'url' => $url
->getGeneratedUrl(),
],
];
}
return $element;
}
/**
* Sets the autocomplete route parameters.
*
* @param array $element
* The render element.
*
* @return array
* The render element with autocomplete route parameters.
*/
protected static function setAutocompleteRouteParameters(array &$element) {
$complete_form = [];
$element = EntityAutocomplete::processEntityAutocomplete($element, new FormState(), $complete_form);
$element['#autocomplete_route_name'] = 'select2.entity_autocomplete';
return $element;
}
/**
* Allows to modify the select2 settings.
*/
public static function preRenderOverwrites($element) {
if (!$element['#multiple']) {
$empty_option = [
$element['#empty_value'] => '',
];
$element['#options'] = $empty_option + $element['#options'];
}
// Allow to overwrite the default settings and set additional settings.
foreach ($element["#select2"] as $key => $value) {
$element['#attributes']['data-select2-config'][$key] = $value;
}
$element['#attributes']['data-select2-config'] = Json::encode($element['#attributes']['data-select2-config']);
return $element;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
DependencySerializationTrait:: |
protected | property | An array of entity type IDs keyed by the property name of their storages. | |
DependencySerializationTrait:: |
protected | property | An array of service IDs keyed by property name used for serialization. | |
DependencySerializationTrait:: |
public | function | 1 | |
DependencySerializationTrait:: |
public | function | 2 | |
FormElement:: |
public static | function | Adds autocomplete functionality to elements. | |
FormElement:: |
public static | function | #process callback for #pattern form element property. | |
FormElement:: |
public static | function | #element_validate callback for #pattern form element property. | |
MessengerTrait:: |
protected | property | The messenger. | 29 |
MessengerTrait:: |
public | function | Gets the messenger. | 29 |
MessengerTrait:: |
public | function | Sets the messenger. | |
PluginBase:: |
protected | property | Configuration information passed into the plugin. | 1 |
PluginBase:: |
protected | property | The plugin implementation definition. | 1 |
PluginBase:: |
protected | property | The plugin_id. | |
PluginBase:: |
constant | A string which is used to separate base plugin IDs from the derivative ID. | ||
PluginBase:: |
public | function |
Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
3 |
PluginBase:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
PluginBase:: |
public | function | Determines if the plugin is configurable. | |
PluginBase:: |
public | function | Constructs a \Drupal\Component\Plugin\PluginBase object. | 92 |
RenderElement:: |
public static | function | Adds Ajax information about an element to communicate with JavaScript. | |
RenderElement:: |
public static | function | Adds members of this group as actual elements for rendering. | |
RenderElement:: |
public static | function | Form element processing handler for the #ajax form property. | 1 |
RenderElement:: |
public static | function | Arranges elements into groups. | |
RenderElement:: |
public static | function |
Sets a form element's class attribute. Overrides ElementInterface:: |
|
Select2:: |
public | function |
Returns the element properties for this element. Overrides Select:: |
|
Select2:: |
protected static | function | Get an array of currently selected options. | |
Select2:: |
public static | function | Attach autocomplete behavior to the render element. | |
Select2:: |
public static | function | Allows to modify the select2 settings. | |
Select2:: |
public static | function |
Prepares a select render element. Overrides Select:: |
|
Select2:: |
public static | function |
Processes a select list form element. Overrides Select:: |
|
Select2:: |
protected static | function | Sets the autocomplete route parameters. | |
Select2:: |
public static | function | Form element validation handler for entity_autocomplete elements. | |
Select2:: |
public static | function |
Determines how user input is mapped to an element's #value property. Overrides Select:: |
|
Select2Trait:: |
protected static | function | Validates an array of IDs. | |
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. |