Resources

Download Source Code

Summary

# Terminal
rails g scaffold users name age:integer driver_license:boolean extend_profile:boolean twitter linkedin perferred_method_of_contact email phone
rails g stimulus toggle-fields

# settings.json
"inlineFold.regex": "(class=|className=|class:\\s*)(({(`|))|(['\"`]))(.*?)(\\2|(\\4)})",

# app/models/user.rb
class User < ApplicationRecord
  enum :perferred_method_of_contact, { email: 0, phone: 1 }
end

# app/views/users/_form.html.erb
<%= form_with(model: user) do |form| %>
  <% if user.errors.any? %>
    <div style="color: red">
      <h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>

      <ul>
        <% user.errors.each do |error| %>
          <li><%= error.full_message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div class="mb-3">
    <%= form.label :name, class: 'form-label' %>
    <%= form.text_field :name, class: 'form-control' %>
  </div>

  <div data-controller="toggle-fields">
    <div class="mb-3">
      <%= form.label :age, class: 'form-label' %>
      <%= form.number_field :age, "data-toggle-fields-target": :input, class: 'form-control' %>
    </div>

    <div data-toggle-fields-target="hidden" data-greater-than=16 class="mb-3 form-check">
      <%= form.label :driver_license, class: 'form-check-label' %>
      <%= form.check_box :driver_license, class: 'form-check-input' %>
    </div>
  </div>

  <div data-controller="toggle-fields">
    <div class="mb-3 form-check">
      <%= form.label :extend_profile, class: 'form-check-label' %>
      <%= form.check_box :extend_profile, "data-toggle-fields-target": :input, class: 'form-check-input' %>
    </div>

    <div data-toggle-fields-target="hidden" data-value=true class="mb-3">
      <%= form.label :twitter, class: 'form-label' %>
      <%= form.text_field :twitter, class: 'form-control' %>
    </div>

    <div data-toggle-fields-target="hidden" data-value=true class="mb-3">
      <%= form.label :linkedin, class: 'form-label' %>
      <%= form.text_field :linkedin, class: 'form-control' %>
    </div>
  </div>

  <div data-controller="toggle-fields">
    <div class="mb-3">
      <%= form.label :perferred_method_of_contact, class: 'form-label' %>
      <%= form.select :perferred_method_of_contact,
        User.perferred_method_of_contacts.keys,
        { include_blank: true },
        "data-toggle-fields-target": :input,
        class: 'form-control' %>
    </div>

    <div data-toggle-fields-target="hidden" data-value="email" class="mb-3">
      <%= form.label :email, class: 'form-label' %>
      <%= form.text_field :email, class: 'form-control' %>
    </div>

    <div data-toggle-fields-target="hidden" data-value="phone" class="mb-3">
      <%= form.label :phone, class: 'form-label' %>
      <%= form.text_field :phone, class: 'form-control' %>
    </div>
  </div>

  <div class="actions">
    <%= form.submit class: 'btn btn-primary' %>
  </div>
<% end %>

# app/javascript/controllers/toggle_fields_controller.js
import { Controller } from "@hotwired/stimulus"

// Connects to data-controller="toggle-fields"
export default class extends Controller {
  static targets = ["hidden", "input"]

  connect() {
    this.inputTarget.addEventListener("change", this.toggle.bind(this))
    this.inputTarget.addEventListener("keyup", this.toggle.bind(this))
    this.toggle()
  }

  disconnect() {
    this.inputTarget.removeEventListener("change", this.toggle.bind(this))
    this.inputTarget.removeEventListener("keyup", this.toggle.bind(this))
  }

  toggle() {
    this.hiddenTargets.forEach((target) => {
      const targetValue = target.dataset.value ? target.dataset.value : null
      const greaterThanValue = target.dataset.greaterThan ? parseFloat(target.dataset.greaterThan) : null
      const lessThanValue = target.dataset.lessThan ? parseFloat(target.dataset.lessThan) : null
      console.log(this.value, targetValue, greaterThanValue, lessThanValue);

      let shouldShow = true

      if (targetValue !== null && this.value !== targetValue) { shouldShow = false }
      if (greaterThanValue !== null && this.value < greaterThanValue) { shouldShow = false }
      if (lessThanValue !== null && this.value > lessThanValue) { shouldShow = false }

      if (shouldShow) {
        target.classList.remove("d-none")
        // target.classList.remove("hidden", "sm:hidden", "md:hidden", "lg:hidden")
      } else {
        target.classList.add("d-none")
        // target.classList.add("hidden", "sm:hidden", "md:hidden", "lg:hidden")
      }
    })
  }

  get value() {
    switch (this.inputTarget.type) {
      case "checkbox":
        return this.inputTarget.checked ? "true" : "false"
      case "number":
        return this.inputTarget.value ? parseFloat(this.inputTarget.value) : null
      default:
        return this.inputTarget.value
    }
  }
}