Skip to main content
Category:

in this tuto, I'll show you How to create a custom Autocomplete on text fields using the Drupal 8 Form API.

To use Autocomplete on text fields, just add property '#autocomplete_route_name' to a text field in a form. The route controller for this route must return an array of options for autocomplete, as a \Symfony\Component\HttpFoundation\JsonResponse object.

Step 1 - Define your module

create mymodule.info.yml file like this:

name: 'My Module'
description: 'My Module'
package: 'Custom'
type: module
core: 8.x 
core_version_requirement: ^8 || ^9

 

Step 2 - Define the routes

create mymodule.routing.yml file like this:

mymodule.autocomplete:
  path: '/autocomplete/articles'
  defaults:
    _controller: '\Drupal\mymodule\Controller\JsonApiArticlesController::handleAutocomplete'
    _format: json
  requirements:
    _permission: 'access content'

mymodule.form_autocomplete:
  path: '/autocomplete-form'
  defaults:
    _form:  '\Drupal\mymodule\Form\MyAutocompleteForm'
    _title: 'Autocomplete Form'
  requirements:
    _permission: 'access content'

 

Step 3 - Define The form and the fileds

create src/Form/MyAutocompleteForm.php file like this:

<?php

namespace Drupal\mymodule\Form;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Entity\Element\EntityAutocomplete;

/**
 * Class MyAutocompleteForm
 * @package Drupal\mymodule\Form
 */
class MyAutocompleteForm extends FormBase
{

  /**
   * {@inheritdoc}
   */
  public function getFormId()
  {
    return 'my_autocomplete_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state)
  {
    $form['article'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Autocomplete Articles'),
      '#autocomplete_route_name' => 'mymodule.autocomplete',
    ];
    $form['actions'] = ['#type' => 'actions'];
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Save'),
    ];
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state)
  {
    $article_id = EntityAutocomplete::extractEntityIdFromAutocompleteInput($form_state->getValue('article'));
    \Drupal::messenger()->addMessage('Article ID is ' . $article_id);
  }


}

 

Step 4 - Add Controller and return JSON response

create src/Controller/JsonApiArticlesController.php file like this:

<?php

namespace Drupal\mymodule\Controller;

use Drupal\Core\Entity\Element\EntityAutocomplete;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Component\Utility\Xss;

/**
 * Class JsonApiArticlesController
 * @package Drupal\mymodule\Controller
 */
class JsonApiArticlesController
{

  /**
   * @return JsonResponse
   */
  public function handleAutocomplete(Request $request)
  {
    $results = [];
    $input = $request->query->get('q');
    if (!$input) {
      return new JsonResponse($results);
    }
    $input = Xss::filter($input);
    $query = \Drupal::entityQuery('node')
      ->condition('type', 'article')
      ->condition('title', $input, 'CONTAINS')
      ->groupBy('nid')
      ->sort('created', 'DESC')
      ->range(0, 10);
    $ids = $query->execute();
    $nodes = $ids ? \Drupal\node\Entity\Node::loadMultiple($ids) : [];
    foreach ($nodes as $node) {
      $results[] = [
        'value' => EntityAutocomplete::getEntityLabels([$node]),
        'label' => $node->getTitle().' ('.$node->id().')',
      ];
    }
    return new JsonResponse($results);
  }

}

 

And this is my final output:

custom autoComplete

 

If you want to select an entity, then there is a way easier way to do that. Drupal 8 has a standard entity_autocomplete field type, just specify your form element like this:

Usage examples:

Provide a basic autocomplete element that matches node titles from all bundles:

$form['my_element'] = array(
  '#type' => 'entity_autocomplete',
  '#target_type' => 'node',
  '#default_value' => $entity, // The #default_value can be either an entity object or an array of entity objects.
);

If we want to restrict the matches to a single or a set of bundles, we can use the 'target_bundles' selection setting:

$form['my_element'] = array(
  '#type' => 'entity_autocomplete',
  '#target_type' => 'node',
  '#selection_handler' => 'default', // Optional. The default selection handler is pre-populated to 'default'.
  '#selection_settings' => array(
    'target_bundles' => array('article', 'page'),
  ),
);

If we want to allow an input of multiple entity labels into the element (commonly known as "tagging" fields), we set the '#tags' property to TRUE (its default value is FALSE):

$form['my_element'] = array(
  '#type' => 'entity_autocomplete',
  '#target_type' => 'node',
  '#tags' => TRUE,
);

If we 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:

$form['my_element'] = array(
  '#type' => 'entity_autocomplete',
  '#target_type' => 'taxonomy_term',
  '#autocreate' => array(
    'bundle' => 'tags', // Required. The bundle name for the new entity.
    'uid' => <a valid user ID>, // Optional. The user ID for the new entity, if the target entity type implements \Drupal\user\EntityOwnerInterface. Defaults to the current logged-in user.
  ),
);

 

Example:

 $entity =  \Drupal\user\Entity\User::loadMultiple(); // Load all user entities
 foreach ($entity as $key => $user) {
    if($user->id() === $member){
       $member = $user; //the user entity which I want is now stored in member
       break;
     }
 }

$form['member'] = array(
      '#type' => 'entity_autocomplete',
      '#target_type' => 'user',
      '#title' => $this->t('Member'),
      '#required' => 'true',
      '#default_value' => $member,


);

Riadh Rahmi

Senior Web Developer PHP/Drupal & Laravel

I am a senior web developer, I have experience in planning and developing large scale dynamic web solutions especially in Drupal & Laravel.

Web Posts

Search

Page Facebook