Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Question] A way to include a field that is not dirty and Kobalte Radio woes #266

Open
Nyloth9 opened this issue Nov 22, 2024 · 6 comments
Assignees
Labels
question Further information is requested

Comments

@Nyloth9
Copy link

Nyloth9 commented Nov 22, 2024

I have a radio component with three options; A, B and C. A is the default value. A feature I would like, or maybe I'm missing something that exists, is; I would like to include the radio in the submitted values even if the user did not change the radio value and it remained the default "A". I don't want do this with a global shouldDirty flag on the form, as I very much the behavior of only including the dirty values - except in the case of the radio input. So basically is there something like a shouldDirty flag just for the specific field or some workaround i could use for it to behave as i described?

@fabian-hiller
Copy link
Owner

I am not sure if I understand you correctly. Do you want your form to return an initial value of "A" when submitted?

@fabian-hiller fabian-hiller self-assigned this Nov 22, 2024
@fabian-hiller fabian-hiller added the question Further information is requested label Nov 22, 2024
@Nyloth9
Copy link
Author

Nyloth9 commented Nov 22, 2024

Yes exactly. Even if the user did not interact with the radio I want it to be submitted

@fabian-hiller
Copy link
Owner

This should work. Do you use controlled fields? https://modularforms.dev/solid/guides/controlled-fields

@Nyloth9
Copy link
Author

Nyloth9 commented Nov 26, 2024

I use Kobalte. Sorry I should have said that before. Even when i copy paste the kobalte radio example from the guide on the website, it doesn't submit the default value. Here is my code:

interface TRadioGroup {
  widget: "element";
  element: "radio";
  variant: "A" | "B" | "C";
  id: string;
  name: string;
  label: string;
  description: string;
  defaultValue?: string;
  options: {
    id: string;
    label: string;
    value: string;
    description: string;
    icon?: string;
  }[];
}

function Radio_A(props: Props) {
  const Field = props.Field;
  const element: TRadioGroup = props.element;

  return (
    <div class="-mb-3 max-w-2xl">
      <Field name={element.name}>
        {(field: any, keys: any) => {
          const [rootProps, inputProps] = splitProps(
            keys,
            ["name"],
            ["ref", "onInput", "onChange", "onBlur"],
          );
          return (
            <RadioGroup
              {...rootProps}
              defaultValue={element.defaultValue}
              value={field.value}
              validationState={field.error ? "invalid" : "valid"}
            >
              <RadioGroup.Label>
                {element.label}
              </RadioGroup.Label>

           
                <For each={element.options}>
                  {(option) => (
                    <RadioGroup.Item value={option.value} class="flex w-fit">
                      <RadioGroup.ItemInput
                        {...inputProps}
                        tabIndex={-1}/>
           
                        <RadioGroup.ItemControl>
                          <RadioGroup.ItemIndicator />
                        </RadioGroup.ItemControl>
       
                      <RadioGroup.ItemLabel
                        tabIndex={0}
                        onKeyDown={(e) => {
                          if (e.key === "Enter") {
                            e.preventDefault();
                            e.target.click();
                          }
                        }}
                  
                      >
                        {option.label}
                        <RadioGroup.ItemDescription>
                          {option.description}
                        </RadioGroup.ItemDescription>
                      </RadioGroup.ItemLabel>
                    </RadioGroup.Item>
                  )}
                </For>
            </RadioGroup>
          );
        }}
      </Field>
    </div>
  );
}

@Nyloth9
Copy link
Author

Nyloth9 commented Nov 28, 2024

Upon further research, it looks like there are a couple of problems (in my use case) with Kobalte Radio, so that the defaultValue prop will not do anything more than set the radio input to "checked" and not update the relevant field value (that's the current way it works for me anyway, but I believe I wired things correctly and it's the same even with the official example).

My radio button controls which fields will be visible, so if its radio A, input "name" will be visible, and if it's radio B, input "designation" will be visible. I'm subscribing to the value of radio field with createEffect and then reactively changing the visibility of the input fields when the value of the radio changes. Because Kobalte Radio doesn't set the relevant field value of the form even if you provide it with a defaultValue, both inputs will remain invisible, because the radio field "didn't mount" so to say.

So that's problem no. 1, and it has two solutions; either providing an initial value when creating the form, or setting the value with "setValue" to the radio field once it has mounted.

Problem no. 2 for me was; I wanted a further behavior ; I also wanted to include the radio value when the form is submitted. However, if the user didn't interact with the radio, the value of the radio field will be set (as explained above - to the default provided value). but the field will not be "dirty" and thus will not be included in the submitted values (that is, if form prop "shoudDirty" is set to true, which I use).

So a workaround for that is to set the field manually to dirty with:

form.internal.fields[your field name]?.dirty.set(true);

And also, if you don't want to set the radio value in the initial values of the form (as I don't), you can set:

<Field name={element.name}>
        {(field: any, keys: any) => {
          const [rootProps, inputProps] = splitProps(
            keys,
            ["name"],
            ["ref", "onInput", "onChange", "onBlur"],
          );

          if (element.defaultValue && !field.value)
            setValue(props.form, element.name, element.defaultValue);
            
            .....

because doing it the Kobalte way;

         <RadioGroup
              {...rootProps}
              defaultValue={element.defaultValue}
              value={field.value || element.defaultValue}
            >

doesnt do anything except set the input element to checked (doesn't update the form field value).

With this hacky solution I managed to get it to work how I wanted, inelegantly, so maybe this solution can help out someone with similar use case.

@Nyloth9 Nyloth9 changed the title [Question] A way to include a field that is are not dirty [Question] A way to include a field that is are not dirty and Kobalte Radio woes Nov 28, 2024
@Nyloth9 Nyloth9 changed the title [Question] A way to include a field that is are not dirty and Kobalte Radio woes [Question] A way to include a field that is not dirty and Kobalte Radio woes Nov 29, 2024
@fabian-hiller
Copy link
Owner

I suspect that the Kobalte code in our docs is out of date, and this is not a problem in Modular Forms source code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants