Change record status: 
Project: 
Introduced in branch: 
11.x
Introduced in version: 
11.3.0
Description: 

A new object-oriented API has been added for working with form and render elements.

The API builds on top of existing Render/Form element plugins and supports casting to and from an object.

The objects are provided as a convenience for working with render arrays and provide IDE integration so that supported keys can be auto-completed.

Once the form/render arrays have been modified using the new API, in the first instance they must be cast back to render arrays to continue to work with Drupal. Future releases will build support for these objects as first class returns.

Examples

In all the examples below $elementInfoManager is the element info plugin manager.
This can be retrieved from the service container using \Drupal::service(Drupal\Core\Render\ElementInfoManagerInterface::class) or alternatively injected using available dependency injection approaches.
If you're working inside a class that extends from \Drupal\Core\Form\FormBase, this is also available as $this->elementInfoManager()

Create a new submit button

$submit = $elementInfoManager->fromClass(\Drupal\Core\Render\Element\Submit::class))
$submit->value = $this->t('Submit');

If you're using an IDE, you should get auto-complete suggestions on the properties you can use on the object

Screenshot showing the value and submit properties being auto-completed for a submit element

Alter an existing form

Adding a new child element.

$form_obj = $elementInfoManager->fromRenderable($form);
$checkbox  = $form_obj->createChild('field_name', \Drupal\Core\Render\Element\Checkbox::class);
$checkbox->title = $this->t('Do you like this new API?');
$checkbox->required = TRUE;

Removing a child element

$form_obj = $elementInfoManager->fromRenderable($form);
// No soup for you.
$form_obj->removeChild('soup');

Iterating over children

$form_obj = $elementInfoManager->fromRenderable($form);
$titles = [];
foreach ($form_obj->getChildren() as $child) {
  // Children are objects too.
  $titles[] = $child->title;
}

Getting a single child

$form_obj = $elementInfoManager->fromRenderable($form);
$form_obj->getChild('submit')->value = $this->t('Save');

Casting back to a render array

At this point in time, form build methods and the like still expect a render array.
So the new objects are only for developer convenience while building and working with form elements.
To cast the object back to a render array to be returned from a form builder function, use the toRenderable method


public function buildForm(array $form, FormStateInterface $form_state): array {
  $form_obj = $this->elementInfoManager()->fromRenderable($form);
  $submit =  $form_obj->createChild('submit');
  $submit->value = $this->t('Submit');
  return $form_obj->toRenderable();
}

Adding support for custom element plugins

Adding support to a custom/contrib element plugin is a matter of ensuring that any custom # keys are documented as @property keys on the plugin class.

See an example from core

Impacts: 
Module developers
Site templates, recipes and distribution developers