D8 autocomplete search items in custom module

search

 

This script will help display the results of a search by keyword instantly via an ajax call. It can be applied to various search types. From user point of view it creates a good user experience and efficient working flow.

For instance we apply this search in products and services module and payroll module to quickly find the data to be reviewed or edited.

 

 

1) create the form

The search form is very simple and is made from a text field and <div> to display the results. We do not need to "submit" the form as the search results are returned by an ajax call.

/**
 * @file
 * Contains \Drupal\MyModule\Form\SearchProductsForm.
 */

namespace Drupal\MyModule\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

/**
 * Provides a form to search items
 */
class SearchProductsForm extends FormBase {

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

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {

          $form['name'] = array(
              '#type' => 'textfield',
              '#id' => t('product-search-form'),
              '#size' => 50,
              '#attributes' => array('placeholder'=>t('Enter item code, barcode or name')),
              '#attached' => ['library' => array('MyModule/MyModule.autocomplete')],
            );
          
          $form['list_items'] = array(
            '#type' => 'item',
            '#markup' => "<div id='product-search-result'></div>",
          );   
        
    return $form;  

  }

The form is located in mymodule/src/From.

You can notice that the library MyModule.autocomplete is attached to the search box. Let's create the library script now.

2) Library and autocomplete script

The library is declared in mymodule.libraries.yml file at the root of our custom module:

MyModule.autocomplete:
  version: 1
  css:
    theme:
      css/MyModule.css: {}
  js:
    js/MyModule_autocomplete.js: {}
  dependencies:
    - core/jquery
    - core/drupal

The main reference in the library is MyModule_autocomplete.js file that manage the ajax call and display of results. This file will contain the following script:

(function ($, Drupal, drupalSettings) {

  Drupal.behaviors.ek_products_autocomplete = {
    attach: function (context, settings) {

      jQuery('#product-search-form').keyup(function() {
      
        var term = jQuery('#product-search-form').val();

        jQuery.ajax({
          dataType: "json",
          url: drupalSettings.path.baseUrl + "autocomplete_ajax" ,
          data: { option: "image", q: term },
          success: function (data) {
              var content = '';
              var i = 0;
              for(;data[i];) {
                  
                  var editUrl = "<a class='product_image-link' href='" + drupalSettings.path.baseUrl
                          + "item/" + data[i]['id'] + "'>" + data[i]['picture'] + "</a>";                  
                  content += "<p>" + editUrl + "  " + data[i]['name'] + "</p>";
                  i++;
              }
              
              jQuery('#product-search-result').html(content);

          }
          });      
      });   
    }
  };
})(jQuery, Drupal, drupalSettings);

 

Few comments here.

First the URL is pointing to "autocomplete_ajax"; this route must exist in your routing file (i.e mymodule.routing.yml):

MyModule_autocomplete_ajax:
  path: '/autocomplete_ajax'
  defaults:
    _controller: '\Drupal\mymodule\Controller\ProductsController::autocomplete'
  requirements:
    _permission: 'view_products'

We include some class properties that will be defined in a file MyModule.css included in the library declaration above.

The result is returned as a json format from the controller but will be displayed as html format in the browser.

 

3) the Controller

The controller function (i.e autocomplete()) will query the database and return the results. In the simplified version below, we do not describe the actual database query that may vary from structure to structure. The result returned is pretty simple and in our case we return 3 elements when 'image' option is declared: picture, name (made of different information) and id.

public function autocomplete(Request $request) {

        $term = $request->query->get('q');
        $option = $request->query->get('option');

        /*
        * do the DB query here filtered by $term: $data
        */
        
        $return = array();
        while ($result = $data->fetchObject()) {

            if (strlen($result->description) > 30) {
                $desc = substr($result->description, 0, 30) . "...";
            } else {
                $desc = $result->description;
            }
            
            if($option == 'image') {
                $line = [];
                if ($result->uri) {
                         $pic = "<img class='product_thumbnail' src='"
                        . file_create_url($result->uri) . "'>";
                    } else {
                        $pic = '[]';
                    }
                    $line['picture'] = isset($pic) ? $pic : '';
                    $line['name'] = $result->id . " " . $result->itemcode . " " . $result->barcode . " " . $desc . " " .$result->supplier_code;
                    $line['id'] = $result->id;
                    
                    $return[] = $line;
                
            } else {
                $return[] = $result->id . " " . $result->itemcode . " " . $result->barcode . " " . $desc . " " .$result->supplier_code;
            }
           
        }
        return new JsonResponse($return);
}

 

4) Display

You can now create the routing to display your form and see the result in your browser. To call your search form, simply create a route to the form as in the example below (mymodule.routing.yml):

MyModule.searchForm:
  path: '/mysearchform'
  defaults:
    _form: '\Drupal\mymodule\Form\SearchProductsForm'
  requirements:
    _access: 'TRUE'

Display can be customized within the css file. In our case, we define  "thumbnail"  properties to display the items images:

/* search display */
.product_thumbnail {
    
    width: 40px;
    height: 40px;
    overflow: hidden;
    vertical-align: middle;
    margin-right: 10px;
    border: solid 1px;
    -webkit-border-radius: 5px;
    -moz-border-radius: 5px;
}

search

 

Feel free to add your comments or own experience.

Thank you.

Add new comment

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.