0

I have a set of Products that are displayed in a select as Product ID => Product Name. For example:

<option value="44">Cambium ePMP Force 180</option>

Problem is, in addition to searching by Product Name, I also want to scan in barcodes to match the Product Code against the Product. I have a mapping that I retrieve via JSON which has a structure of Product ID => Product Code:

mappings = {
    44: 'C050900C171A'
     ...
};

How do I write a custom matcher that also searches the mappings object?

(Bonus points if it automatically selects the matching option.)

Damien
  • 1,140
  • 15
  • 22

2 Answers2

0

I wrote a custom matcher based on the default matcher here: https://github.com/select2/select2/blob/c2326209c21862ed606e98213485a679e8ca0caf/src/js/select2/defaults.js#L272-L315

First, get the mappings via JSON from the remote data source:

$.getJSON('api/stock-code-mappings.json.php', function(mappings) {

This sets mappings to contain:

mappings = {
  44: 'C050900C171A'
  ...
};

The default matcher can be accessed via:

$.fn.select2.defaults.defaults.matcher

In the custom matcher, iterate through the mappings to check if the term matches a Product Code mapping, and if it does, check that the current element's id matches the Product ID:

$.each(mappings, function(id, code) {
  if (code.toUpperCase() == term && id == data.id) foundStockCode = true;
});

Putting it all together:

$(function() {

  // Barcode Mappings:
  // Product ID => Product Code
  var mappings = {
    44: 'C050900C171A',
    100: 'AJSLCL-100XCL',
    200: 'L92FOO-1100M-200'
  };

  // Custom matcher
  function mappingsMatcher(params, data, firstLevel = true) {
    // Check the default matcher on the first level of recursion only.
    if (firstLevel) {
      var result = $.fn.select2.defaults.defaults.matcher(params, data);
      if (result) return result;
    }

    // This is the same recursion pattern of the default matcher.
    if (data.children && data.children.length > 0) {
      var match = $.extend(true, {}, data);
      for (var c = data.children.length - 1; c >= 0; c--) {
        var child = data.children[c];
        var matches = stockTypeMatcher(params, child, false);
        if (matches == null) {
          match.children.splice(c, 1);
        }
      }

      // If any children matched, return the new object
      if (match.children.length > 0) {
        return match;
      }

      // If there were no matching children, check just the plain object
      return stockTypeMatcher(params, match, false);
    }

    // Check against mappings
    var term = params.term.toUpperCase();
    var foundStockCode = false;
    $.each(mappings, function(id, code) {
      if (code.toUpperCase() == term && id == data.id) foundStockCode = true;
    });
    if (foundStockCode) return data;
    return null;
  }

  $('select').select2({
    placeholder: 'Product',
    allowClear: true,
    matcher: mappingsMatcher
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.full.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" rel="stylesheet"/>

<p>Search for the following mappings by copying a code into the search box:</p>
<ul>
  <li>C050900C171A</li>
  <li>AJSLCL-100XCL</li>
  <li>L92FOO-1100M-200</li>
</ul>

<select style="width: 20em">
  <option value=""></option>
  <option value="44">Cambium ePMP Force 180</option>
  <option value="100">Product One</option>
  <option value="200">Product Two</option>
</select>
Damien
  • 1,140
  • 15
  • 22
0

This is an alternative, which changes the value and closes the search box immediately if a Product Code is found via the barcode mappings.

The line that selects the matched barcode and closes the box is:

$(data.element).closest('select')
  .val(data.id)
  .trigger('change')
  .select2('close');

$(function() {

  // Barcode Mappings:
  // Product ID => Product Code
  var mappings = {
    44: 'C050900C171A',
    100: 'AJSLCL-100XCL',
    200: 'L92FOO-1100M-200'
  };

  // Custom matcher
  function mappingsMatcher(params, data, firstLevel = true) {
    // Check the default matcher on the first level of recursion only.
    if (firstLevel) {
      var result = $.fn.select2.defaults.defaults.matcher(params, data);
      if (result) return result;
    }

    // This is the same recursion pattern of the default matcher.
    if (data.children && data.children.length > 0) {
      var match = $.extend(true, {}, data);
      for (var c = data.children.length - 1; c >= 0; c--) {
        var child = data.children[c];
        var matches = stockTypeMatcher(params, child, false);
        if (matches == null) {
          match.children.splice(c, 1);
        }
      }

      // If any children matched, return the new object
      if (match.children.length > 0) {
        return match;
      }

      // If there were no matching children, check just the plain object
      return stockTypeMatcher(params, match, false);
    }

    // Check against mappings
    var term = params.term.toUpperCase();
    var foundStockCode = false;
    $.each(mappings, function(id, code) {
      if (code.toUpperCase() == term && id == data.id) foundStockCode = true;
    });
    if (foundStockCode) {
      $(data.element).closest('select')
        .val(data.id)
        .trigger('change')
        .select2('close');
    }
    return null;
  }

  $('select').select2({
    placeholder: 'Product',
    allowClear: true,
    matcher: mappingsMatcher
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.full.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" rel="stylesheet"/>

<p>Search for the following mappings by copying a code into the search box:</p>
<ul>
  <li>C050900C171A</li>
  <li>AJSLCL-100XCL</li>
  <li>L92FOO-1100M-200</li>
</ul>

<select style="width: 20em">
  <option value=""></option>
  <option value="44">Cambium ePMP Force 180</option>
  <option value="100">Product One</option>
  <option value="200">Product Two</option>
</select>
Damien
  • 1,140
  • 15
  • 22