About Listening to Changes of Autocomplete Fields which don't Fire Any Special Events

Django-Grappelli's autocomplete fields don’t trigger any JavaScript events when a selection is made, making it difficult to detect changes. If you need dynamic functionality based on the selected value, you can override the getter for the read-only field’s value and inject your callback function there. Here's how:

$(document).ready(function () {
  function watchForAutocompleteChanges(fieldSelector, callback) {
    const hiddenFields = document.querySelectorAll(
      `${fieldSelector}:not([data-watcher-installed])`
    );

    hiddenFields.forEach(function (hiddenField) {
      hiddenField.setAttribute('data-watcher-installed', 'true');

      const originalDescriptor = Object.getOwnPropertyDescriptor(
        HTMLInputElement.prototype, 'value'
      );

      let currentValue = hiddenField.value;

      Object.defineProperty(hiddenField, 'value', {
        get: function () {
          return currentValue;
        },
        set: function (newValue) {
          const oldValue = currentValue;
          currentValue = newValue;

          originalDescriptor.set.call(this, newValue);

          if (oldValue !== newValue && newValue) {
            // The autocomplete text input is the previous sibling
            const $autocompleteField = $(this).prev();
            callback($autocompleteField, {
              hiddenField: this,
              oldValue: oldValue,
              newValue: newValue
            });
          }
        },
        enumerable: true,
        configurable: true
      });
    });
  }

  watchForAutocompleteChanges(
    'id_country',
    function($autocompleteField, data) {
      console.log(`New country value: ${data.newValue}`);
    }
  );

});

Tips and Tricks Programming Django 5.2 Django 4.2 Django 3.2 JavaScript Django Grappelli