avo-hq / avo

Build Ruby on Rails apps 10x faster
https://avohq.io
Other
1.43k stars 223 forks source link

Enabling and disabling fields using Stimulus based on the Type #2894

Open rctneil opened 1 week ago

rctneil commented 1 week ago

Hi,

I have a PersonResource with four fields: Type (Select), Marriage Date (Date), Marriage Date Parts (Select), and Divorced (Boolean).

I want to disable / enable the two marriage fields and the divorced field based on the value of the Type field. They should be enabled if the Type is "Spouse", and disabled if not.

Screenshot 2024-06-26 at 09 37 58

I have set up Stimulus and have created a PersonResource controller.

This is what I have so far:

import { Controller } from '@hotwired/stimulus'

export default class extends Controller {
  static values = {
    view: String,
  }

  static targets = ["typeSelectInput", "marriagedateDateInput", "divorcedBooleanInput", "marriagedatepartsSelectInput"];

  async connect() {
    console.log('view ->', this.viewValue)
  }

  typeChanged() {
    console.log('type was changed');

    let value = this.typeSelectInputTarget.value;

    if (value == "Spouse") {
        console.log('Selected SPOUSE');
        this.divorcedBooleanInputTarget.disabled = false;
        this.marriagedatepartsSelectInputTarget.disabled = false;

        this.marriagedateDateInputTarget.disabled = false;
        this.marriagedateDateInputTarget.nextElementSibling.disabled = false;
        this.marriagedateDateInputTarget.nextElementSibling.nextElementSibling.disabled = false;
    } else {
        console.log("Selected PERSON");
        this.divorcedBooleanInputTarget.disabled = true;
        this.marriagedatepartsSelectInputTarget.disabled = true;

        this.marriagedateDateInputTarget.disabled = true;
        this.marriagedateDateInputTarget.nextElementSibling.disabled = true;
        this.marriagedateDateInputTarget.nextElementSibling.nextElementSibling.disabled = true;
    }
  }
}
            field :type,
                  as: :select,
                  name: 'Type',
                  only_on: [:forms],
                  nullable: true,
                  options: { Person: nil, Spouse: "Spouse" },
                  html: {
                        edit: {
                              input: {
                                    data: {
                                          action: "change->person-resource#typeChanged"
                                          # resource_edit_toggle_target_param: "marriage_person_id", # target to be toggled
                                          # resource_edit_toggle_targets_param: ["countrySelectWrapper"] # add more than one target
                                    }
                              }
                        }
                  }

            field :marriagedate,
                  as: :date,
                  name: 'Marriage Date',
                  hide_on: [:index],
                  nullable: true,
                  disabled: -> { resource.record.type != 'Spouse' || false }

            field :marriagedateparts,
                  as: :select,
                  options: { 'Day, Month & Year': "DMY", 'Month & Year': "MY", 'Year': "Y" },
                  include_blank: 'N / A',
                  nullable: true,
                  name: 'Date of marriage parts',
                  help: "Which parts of the date are confirmed?",
                  hide_on: [:index],
                  disabled: -> { resource.record.type != 'Spouse' || false }

            field :divorced,
                  as: :boolean,
                  name: 'Divorced?',
                  hide_on: [:index],
                  disabled: -> { resource.record.type != 'Spouse' || false }

A few questions:

  1. How do we know which element to disable / enable? Is it the wrapper, the input? Both? Any others i've missed? Which?
  2. In the case of the date input, there are appears to be 3 inputs. The first two have marriagedateDateInput targets assigned, the last one doesn't. Hence my use of nextElementSibling.

When testing this, the 3 fields do enable when changing to "Spouse", if i fill them in and save the record, those values do not save.

If I wish to change a "Spouse" back to a regular "Person", When changing the "Type" back to "Person", the fields do disable, but the values remain and don't get emptied when saving the record.

I have read through the documentation and it was very useful for setting up the Stimulus integration, but I was a bit confused with how to use stimulus to do things like toggling, enabling / disabling, etc, and interacting with different field types. A bit more on the docs would be super useful.

Maybe have an "Integration with Stimulus" section of each field type docs page to explain how to deal with it specifically.

I hope this is a lot clearer than my hectic and quick post in Discord yesterday!

Thanks!

adrianthedev commented 1 week ago

Hey @rctneil.

I haven't had a very good look at the code. It's too much to take in my head. If you create a reproduction repository, I'll happily have a look.

How do we know which element to disable / enable? Is it the wrapper, the input? Both? Any others i've missed? Which?

It's the input. That's what's disabled.

In the case of the date input, there are appears to be 3 inputs. The first two have marriagedateDateInput targets assigned, the last one doesn't. Hence my use of nextElementSibling.

Your observation is correct. Avo uses a few inputs for one field. It's how the flatpickr plugin works. I don't know by heart how to do that with flatpickr, but maybe this thread might help.

Maybe have an "Integration with Stimulus" section of each field type docs page to explain how to deal with it specifically.

I'd love to provide that but that we would need a team at least double in size to be able to keep shipping so much. We will take a PR from good folks who can help. The docs are open source.

Again, I haven't had a very good look over the code as it's too much to load in my memory. I will have a look over your reproduction repo and try to help.