n Custom Autocomplete on text fields using the Drupal 8 & 9 | CodimTh

Please Disable Your Browser Adblock Extension for our site and Refresh This Page!

our ads are user friendly, we do not serve popup ads. We serve responsible ads!

Refresh Page
Skip to main content
On . By CodimTh
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