protected function ComponentSectionForm::buildFormElement in Module Builder 8.3
Builds the form element for a data item.
This is called recursively for complex and multi-valued data items.
Parameters
array &$form: The parent form element (or the entire form), passed by reference. The data item's element is placed with an array key that is its machine name.
\Drupal\Core\Form\FormStateInterface $form_state: The form state.
\MutableTypedData\Data\DataItem $data: The data item.
3 calls to ComponentSectionForm::buildFormElement()
- ComponentSectionForm::buildComplexFormElement in src/
Form/ ComponentSectionForm.php - Builds a form element with multiple child elements.
- ComponentSectionForm::buildMultipleDeltaFormElement in src/
Form/ ComponentSectionForm.php - Builds a multi-valued form element.
- ComponentSectionForm::componentPropertiesForm in src/
Form/ ComponentSectionForm.php - Add form elements for the specified component properties.
File
- src/
Form/ ComponentSectionForm.php, line 162
Class
- ComponentSectionForm
- Generic form for entering a section of data for a component.
Namespace
Drupal\module_builder\FormCode
protected function buildFormElement(&$form, FormStateInterface $form_state, DataItem $data) {
$element = [];
// Determine whether to handle multiple data as a single element or a set
// of deltas.
// Multiple-valued data gets a set of items that can be added and removed
// with AJAX buttons...
$use_multiple_deltas = $data
->isMultiple();
// ... with exceptions: simple data with options that is multiple-
// valued is just a SELECT element.
if (!$data
->isComplex() && $data
->hasOptions()) {
$use_multiple_deltas = FALSE;
}
// ... multiple simple data without options is shown as a text area.
if (!$data
->isComplex()) {
$use_multiple_deltas = FALSE;
}
// Case 1: multiple deltas each handled as a separate form element, within
// a details wrapper.
if ($use_multiple_deltas) {
$element = $this
->buildMultipleDeltaFormElement($form, $form_state, $data);
}
elseif ($data
->getDefinition()
->isComplex()) {
$element = $this
->buildComplexFormElement($form, $form_state, $data);
}
else {
// Case 3A: element with options.
if ($data
->hasOptions()) {
$options = [];
$options_have_descriptions = FALSE;
foreach ($data
->getOptions() as $value => $option) {
$options[$value] = $option
->getLabel();
if ($description = $option
->getDescription()) {
// Set the description on each value. This is a not-terribly-well
// documented feature in FormAPI. This relies on us not clobbering
// $element further on!
$element[$value]['#description'] = $description;
$options_have_descriptions = TRUE;
}
}
$options_count = count($options);
if ($data
->isMultiple()) {
$element_type = 'checkboxes';
// Build up a default value array for the checkboxes from the data's
// delta items.
$default_value = [];
foreach ($data as $delta => $delta_item) {
$default_value[$delta_item->value] = $delta_item->value;
}
}
else {
if ($options_count > 8 && !$options_have_descriptions) {
$element_type = 'select';
$default_value = $data->value;
}
else {
$element_type = 'radios';
$default_value = $data->value;
}
}
natcasesort($options);
$element += [
'#type' => $element_type,
'#title' => $data
->getLabel(),
'#description' => $data
->getDescription(),
'#default_value' => $default_value,
'#options' => $options,
// ARGH why isn't this happening automatically like it's supposed to?
'#empty_option' => $data
->isRequired() ? $this
->t('- Select -') : $this
->t('- None -'),
'#empty_value' => NULL,
];
// Special handling for injected services: textfield with autocomplete.
if (in_array($data
->getName(), [
'injected_services',
'container_services',
'mocked_services',
])) {
$element['#type'] = 'textfield';
// This needs to be massive to allow lots of services!
$element['#maxlength'] = 512;
$element['#description'] .= ' ' . $this
->t("Enter a comma-separated list of names.");
$element['#default_value'] = implode(', ', $default_value);
$element['#autocomplete_route_name'] = 'module_builder.autocomplete';
$element['#autocomplete_route_parameters'] = [
'property_address' => $data
->getAddress(),
];
// Remove the options, as it makes FormAPI think the value must be
// compared against them.
unset($element['#options']);
}
if ($data
->isVariantProperty()) {
// Put this above the 'Update variant properties' button; compare
// with the weight set on that.
$element['#weight'] = -20;
$wrapper_id = Html::getId($data
->getParent()
->getAddress() . '-mutable-wrapper');
$variant_property_form_address = explode(':', $data
->getAddress());
$variant_property_address = array_merge($variant_property_form_address);
$values = $form_state
->getValues();
$variant_value = NestedArray::getValue($values, $variant_property_address);
if (isset($variant_value)) {
$data
->set($variant_value);
}
}
}
elseif ($data
->getType() == 'boolean') {
$element += [
'#type' => 'checkbox',
'#title' => $data
->getLabel(),
'#description' => $data
->getDescription(),
'#default_value' => $data->value,
];
}
elseif ($data
->isMultiple()) {
$element += [
'#type' => 'textarea',
'#title' => $data
->getLabel(),
'#description' => $data
->getDescription(),
'#default_value' => implode("\n", $data
->export()),
];
}
else {
$element += [
'#type' => 'textfield',
'#title' => $data
->getLabel(),
'#description' => $data
->getDescription(),
'#default_value' => $data->value,
];
}
// Make form elements required if the data is required, unless there is
// a default, in which case, either the JS will set it, or data validation
// will set it on submission, so there's no need to force the user to
// enter something.
if ($data
->isRequired() && !$data
->getDefault()) {
$element['#required'] = TRUE;
}
$element['#attributes']['data-typed-data-address'] = $data
->getAddress();
// dsm($data->getDefault());
// Note need parentheses around the assignment because of precedence
// relative to &&.
if (($default = $data
->getDefault()) && $default
->getType() == 'expression') {
$expression = $default
->getExpressionWithAbsoluteAddresses($data);
// Prefix custom EL functions with the JS namespace.
// TODO: Would be nice to get the names from the EL rather than
// hardcode them!
$expression = str_replace('get(', 'DataAddressExpressionLanguage.get(', $expression);
$expression = str_replace('machineToClass(', 'DataAddressExpressionLanguage.machineToClass(', $expression);
$expression = str_replace('machineToLabel(', 'DataAddressExpressionLanguage.machineToLabel(', $expression);
$expression = str_replace('stripBefore(', 'DataAddressExpressionLanguage.stripBefore(', $expression);
$dependencies = $default
->getDependencies();
if (!empty($dependencies)) {
// CHEAT; for now only ever one dependency!
// TODO: this only works for addresses that go up only one level!
$dependencies[0] = str_replace('..:', $data
->getParent()
->getAddress() . ':', $dependencies[0]);
}
$element['#attached']['drupalSettings']['moduleBuilder']['typedDataDefaults']['defaults'][$this
->getFormElementNameFromData($data)] = [
'dependencies' => $dependencies,
'expression' => $expression,
];
foreach ($dependencies as $dependency) {
$element['#attached']['drupalSettings']['moduleBuilder']['typedDataDefaults']['reactions'][$dependency] = $this
->getFormElementNameFromData($data);
}
}
}
$element['#tree'] = TRUE;
$form_key = $data
->getName();
$form[$form_key] = $element;
}