How do I convert my Jquery code to a StimulusJS controller Rails 7?

Viewed 176

I'm building out a feature to show if the discount code in a text field is valid. I just managed to build it out using Jquery, but now I wanna convert it into a StimulusJS controller. But I'm not really sure how to.

My current view with jquery:

<div class="mt-2 mx-1">
 <label class="block text-sm font-medium text-gray-700">Discount Code</label>
 <div class="mt-1">
  <input type="text" name="discount_code" id="validateCode" class="appearance-none block w- 
  full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 
  focus:outline-none focus:ring-teal-500 focus:border-teal-500 sm:text-sm">
 </div>
</div>

<script>
  $('#validateCode').on("keyup", function(){
    let code = $('#validateCode').val()
    fetch('/choose_plan/validate_discount_code?code=' + code)
    .then(response => response.json())
    .then(data => { if(data.valid)
      { $('#validateCode').css("background-color", "aliceblue") }
    else
      { $('#validateCode').css("background-color", "white") }
    });
  });
</script>

My validate_discount_controller.js I've started on:

import { Controller } from "@hotwired/stimulus"

// Connects to data-controller="validate-discount"
export default class extends Controller {
  connect() {
    this.element.addEventListener('change', (e) => {
      // execute a fetch request or something here
    })
  }
}
1 Answers

This can be done in Stimulus, but if you have it working you may just want to move on to other things and refactor later if needed.

Nonetheless, here is a breakdown of how to convert your jQuery to vanilla (i.e. no library) JS and also Stimulus.

A great resource is this website - https://youmightnotneedjquery.com/ or the Mozilla docs https://developer.mozilla.org/

jQuery breakdown

Example

<form>
  <div class="mt-2 mx-1">
    <label class="block text-sm font-medium text-gray-700" for="validateCode">Discount Code</label>
    <div class="mt-1">
      <input type="text" name="discount_code" id="validateCode" class="all-the-100-classes"
        data-controller="validate-discount" data-action="validate-discount#validate keyup->validate-discount#validate"
        data-validate-discount-valid-class="bg-error" data-validate-discount-invalid-class="bg-success">
    </div>
  </div>
</form>

Note: I have included a debounce script as you probably do not want to be calling the API every single keystroke - this is very likely going to cause frustration for your users and heavy load on your API. You may want to consider a throttle approach instead.

import { Controller } from '@hotwired/stimulus';

// https://www.joshwcomeau.com/snippets/javascript/debounce/
const debounce = (callback, wait) => {
  let timeoutId = null;
  return (...args) => {
    window.clearTimeout(timeoutId);
    timeoutId = window.setTimeout(() => {
      callback.apply(null, args);
    }, wait);
  };
};

export default class extends Controller {
  // https://stimulus.hotwired.dev/reference/css-classes
  static classes = ['invalid', 'valid'];

  // https://stimulus.hotwired.dev/reference/values
  static values = {
    endpoint: {
      default: '/choose_plan/validate_discount_code?code=',
      type: String,
    },
  };

  initialize() {
    // this will ensure that the API does not get called too much
    // the wait time (300) is in milliseconds so adjust as needed
    this.validate = debounce(this.validate.bind(this), 300);
  }

  validate() {
    // read the value from the input
    // https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement
    const code = this.element.value;

    fetch('/choose_plan/validate_discount_code?code=' + code)
      .then((response) => response.json())
      .then(({ valid }) => {
        if (valid) {
          this.element.classList.remove(...this.invalidClasses);
          this.element.classList.add(...this.validClasses);
        } else {
          this.element.classList.add(...this.invalidClasses);
          this.element.classList.remove(...this.validClasses);
          this.element.setCustomValidity('Thats incorrect!');
        }

        // https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setCustomValidity
        this.element.reportValidity();
      });
  }
}
Related