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]: v-model not updating input value in Shadcn-vue Input component inside a Form #771

Closed
2 tasks
SAntoineS opened this issue Sep 18, 2024 · 13 comments
Closed
2 tasks
Labels
question Further information is requested

Comments

@SAntoineS
Copy link

SAntoineS commented Sep 18, 2024

Reproduction

https://stackblitz.com/edit/1d3tzs-zd6u8m?file=src%2FApp.vue

Describe the bug

Description:

I am experiencing an issue with the v-model binding not updating the input value in the Input component from Shadcn-vue inside a Form. Even though the value changes in the parent component, the input field in the child component doesn't reflect the updated value.

Steps to reproduce:

  1. Parent component:
  • I pass a reactive object uniteLocale with an ide property to the Input component using v-model.
  • When I update uniteLocale.ide programmatically, the change is reflected in other parts of the template (e.g., a ), but not in the Input field.
<Input v-model="uniteLocale.ide" />
<span>IDE: {{ uniteLocale.ide }}</span>
  • The span reflects the change correctly, but the input field does not.
  1. Child component (Input.vue):
  • I am using default Input component from Shadcn-vue.

Expected behavior:

The input field should update its value when uniteLocale.ide is modified programmatically.

Actual behavior:

The value in the input field does not update even though the value of uniteLocale.ide changes.

What I’ve tried:

  • I removed the passive: true option from useVModel.
  • I verified that uniteLocale.ide is being updated correctly by logging it and displaying it in a <span>.
  • I added a manual watch to observe changes to modelValue and emit update:modelValue accordingly.

Despite these changes, the input field does not reflect the updated value.

Additional context:

  • The useVModel from @vueuse/core appears to work in other components, but here it doesn't update the input value as expected. The span displaying uniteLocale.ide is updated correctly, but not the input field.

Thanks !

System Info

System:
    OS: Windows 10 10.0.19045
    CPU: (12) x64 13th Gen Intel(R) Core(TM) i5-1345U
    Memory: 20.89 GB / 31.64 GB
  Binaries:
    Node: 20.17.0 - C:\Program Files\nodejs\node.EXE
    npm: 10.8.2 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Edge: Chromium (127.0.2651.98)
    Internet Explorer: 11.0.19041.4355
    @vueuse/core: ^11.0.3 => 11.0.3
    radix-vue: ^1.9.5 => 1.9.5
    vue: ^3.5.4 => 3.5.4

Contributes

  • I am willing to submit a PR to fix this issue
  • I am willing to submit a PR with failing tests
@SAntoineS SAntoineS added the bug Something isn't working label Sep 18, 2024
@SAntoineS SAntoineS changed the title [Bug]: v-model not updating input value in Shadcn-vue Input component [Bug]: v-model not updating input value in Shadcn-vue Input component inside a Form Sep 18, 2024
@SAntoineS
Copy link
Author

It seems to work if I bind the v-model on the FormField and not directly on the Input.

@sadeghbarati
Copy link
Collaborator

Don't create new ref for values, componentField slotProp already have model-value and @update:model-value (v-model).

Instead use initialValues (useForm) option or :initial-values (Form) prop

https://stackblitz.com/edit/1d3tzs-8cmpb1?file=src%2FApp.vue

@sadeghbarati sadeghbarati removed the bug Something isn't working label Sep 19, 2024
@SAntoineS
Copy link
Author

SAntoineS commented Sep 19, 2024

Don't create new ref for values, componentField slotProp already have model-value and @update:model-value (v-model).

Instead use initialValues (useForm) option or :initial-values (Form) prop

https://stackblitz.com/edit/1d3tzs-8cmpb1?file=src%2FApp.vue

Oooh ok cool !

So I can access to the values via values.uniteLocale.idea, like :

watch(() => values.uniteLocale.ide,
  (newValue) => {
    console.log('New Value: ', newValue);
  }
);

And it works!

Thanks a lot !

Another question

How can I pass the values to child component, it's like a ref ?

Because in my context I have a stepper (with two steps) inside the form and each steps is a component.

It's a form application with fields into the steps.

@sadeghbarati sadeghbarati added the question Further information is requested label Sep 19, 2024
@SAntoineS SAntoineS changed the title [Bug]: v-model not updating input value in Shadcn-vue Input component inside a Form [Question]: v-model not updating input value in Shadcn-vue Input component inside a Form Sep 19, 2024
@sadeghbarati
Copy link
Collaborator

In Stepper it's better to use Form component instead of useForm composable just like this link

https://www.shadcn-vue.com/docs/components/stepper.html#form

So you could access the Form context wherever you want with passing props or using inject or using vee-validate utilities

@SAntoineS
Copy link
Author

SAntoineS commented Sep 19, 2024

In Stepper it's better to use Form component instead of useForm composable just like this link

https://www.shadcn-vue.com/docs/components/stepper.html#form

So you could access the Form context wherever you want with passing props or using inject or using vee-validate utilities

I am sorry but I can't understand how to do this...

This is my Form :

<Form v-slot="{ meta, values, validate }" as="" keep-values
              :validation-schema="toTypedSchema(formSchema[stepIndex - 1])">
          <Stepper v-slot="{ isNextDisabled, isPrevDisabled, nextStep, prevStep }" v-model="stepIndex"
                   class="block w-full">
            <form @submit.prevent="HandleSubmit(meta, values, validate)">
              <div class="flex w-full flex-start gap-2">
                <StepperItem
                    v-for="step in steps"
                    :key="step.step"
                    v-slot="{ state }"
                    class="relative flex w-full flex-col items-center justify-center"
                    :step="step.step">
                  <StepperSeparator
                      v-if="step.step !== steps[steps.length - 1].step"
                      class="absolute left-[calc(50%+20px)] right-[calc(-50%+10px)] top-5 block h-0.5 shrink-0 rounded-full bg-muted group-data-[state=completed]:bg-primary"/>

                  <StepperTrigger as-child>
                    <Button
                        :variant="state === 'completed' || state === 'active' ? 'default' : 'outline'"
                        size="icon"
                        class="z-10 rounded-full shrink-0"
                        :class="[state === 'active' && 'ring-2 ring-ring ring-offset-2 ring-offset-background']"
                        :disabled="state !== 'completed' && !meta.valid">
                      <Check v-if="state === 'completed'" class="size-5"/>
                      <component v-if="state === 'active' || state === 'inactive'" :is="step.icon" class="w-4 h-4"/>
                    </Button>
                  </StepperTrigger>

                  <div class="mt-5 flex flex-col items-center text-center">
                    <StepperTitle
                        :class="[state === 'active' && 'text-primary']"
                        class="text-sm font-semibold transition lg:text-base">
                      {{ step.title }}
                    </StepperTitle>
                    <StepperDescription
                        :class="[state === 'active' && 'text-primary']"
                        class="sr-only text-xs text-muted-foreground transition md:not-sr-only lg:text-sm">
                      {{ step.description }}
                    </StepperDescription>
                  </div>
                </StepperItem>
              </div>

              <div class="flex flex-col gap-4 mt-20">
                <step-inscription v-if="stepIndex === 1"/>
                <step-responsable v-if="stepIndex === 2"/>
              </div>

              <!-- Step Actions -->
              <div class="flex items-center justify-between mt-4">
                <Button :disabled="isPrevDisabled" variant="ghost" size="sm" @click="prevStep()">
                  Précédent
                </Button>
                <!-- Next or Submit Actions -->
                <div class="flex items-center gap-3">
                  <Button variant="informationTonal" v-if="stepIndex !== 2" :type="meta.valid ? 'button' : 'submit'"
                          :disabled="isNextDisabled"
                          size="sm" @click="meta.valid && nextStep()">
                    Suivant
                  </Button>
                  <Button variant="successTonal"
                          v-if="stepIndex === 2" size="sm" type="submit">
                    Soumettre
                  </Button>
                </div>
              </div>
            </form>
          </Stepper>
        </Form>

How can I use inject from vue with values (Inside the Form)

I want to modify / access (like watch()) values in my child components.

I can't pass with this :

<step-inscription v-model="values" v-if="stepIndex === 1"/> cause Vue don't want v-slot scope variable in v-model.
Like this error :
Internal server error: v-model cannot be used on v-for or v-slot scope variables because they are not writable.

@sadeghbarati
Copy link
Collaborator

Can you share a minimal demo of your Stepper component

I can't help in this way

@SAntoineS
Copy link
Author

SAntoineS commented Sep 20, 2024

Can you share a minimal demo of your Stepper component

I can't help in this way

Sure !

There is a stackblitz of a minimal demo of my project.

I'm currently using v-model on FormField and provide (then inject in child) my object like this in main.vue :

const uniteLocale = ref<any>({});
provide('uniteLocale', uniteLocale);

I saw in the Vee Validate documentation that we can use v-model . I don't know if there is a cleaner method to access to Form values from outside the Form.

@sadeghbarati
Copy link
Collaborator

Inside <Form> component you can use useFormValues vee-validate composable

Outside <Form> component

  1. you can get values on form submit and do whatever you want to them like emit them to parent
  2. or use ref on the form component and access the values in script section like formRef.value.values or in template like formRef.values

Even though the vee-validate docs says you can use v-model on FormField it is better not to do it

@SAntoineS
Copy link
Author

SAntoineS commented Sep 23, 2024

Thanks for the solution ! But...

I tried with ref like :

const uniteLocale = ref<any>({});
provide('uniteLocale', uniteLocale);

----------------------------------------------

<Form v-slot="{ meta, validate }" :initial-values="formValuesUniteLocale" keep-values
 :validation-schema="toTypedSchema(formSchema[stepIndex - 1])" ref="uniteLocale">

In my child component I have a watch() that works :

const uniteLocale = inject<Ref>("uniteLocale");

----------------------------------------------

watch(() => uniteLocale.value.values?.descriptionActiEconomiques, (newValue) => {
  isDescriptionFilled.value = !!newValue;
});

The only issue I have left is that I can't modify the value directly from the code like this :

function importIDE(uniteLegal) {
  if (uniteLegal) {
    try {
      const { value } = useField('ide');
      value.value = "test1"
      uniteLocale.value.values.ide = "test2"
      toast({
        title: 'Entreprise importée avec succès ! ✅',
        variant: 'default',
      });
    } catch (e) {
      console.error("Erreur lors de l'importation UniteLegal -", e)
      toast({
        title: "Oops ! Erreur lors de l'importation ❌",
        variant: 'default',
      });
    }
  }
  console.log("UniteLocale.ide: ", uniteLocale.value.values.ide);
}

I tried with directly changing the variable and tried with useField method of vee-validate.

No one works...

But I have this vue-warn :
[Vue warn] Set operation on key "ide" failed: target is readonly.

Do you know why ? I have to contact directly vee-validate ?

Sorry for the deep discussion : /

Thanks a lot !

@sadeghbarati
Copy link
Collaborator

The only issue I have left is that I can't modify the value directly from the code like this :

You need to use

uniteLocale.value.setFieldValue('idle or object.idle', 'new value')

//or 

uniteLocale.value.setValues({
  idle: 'new value',
  
  // or
  
  'object.idle': 'new value'
})

@SAntoineS
Copy link
Author

uniteLocale.value.setFieldValue('idle or object.idle', 'new value')

It works fine !

Thanks a lot for all your help !

@bjorne84
Copy link

can somebody explain whats the point of adding these extra step instead of binding a ref to the input field? what benefit is there to not being able to bind to the input field?

@sadeghbarati
Copy link
Collaborator

can somebody explain whats the point of adding these extra step instead of binding a ref to the input field? what benefit is there to not being able to bind to the input field?

VeeValidate FormField slotProps already have model-value and @update:model-value (componentField slotProp), which is why it's better to use initialValues with componentField slotProp so we could also get a better resetForm functionality and get right types

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

3 participants