core icon indicating copy to clipboard operation
core copied to clipboard

when use custom v-model (with <script setup> style),it goes well in dev env,but may occur error:modelValue is undefined in production env

Open CherishTheYouth opened this issue 3 years ago • 47 comments

Version

3.2.31

Reproduction link

gitee.com

Steps to reproduce

(1)create a vue3 project with vite; (2)write a parent component and a child component in

    index.8d23215e.js:1 Uncaught ReferenceError: modelValue is not defined
    at HTMLInputElement.u.onUpdate:modelValue.l.<computed>.l.<computed> [as _assign] (index.8d23215e.js:1)
    at HTMLInputElement.<anonymous> (vendor.52e08146.js:1)

What is expected?

I expect that my vue3 project which structured by vite.js will go well when use v-model write in

What is actually happening?

Actually, it goes well in dev env but get the follow error in prod dev:

  index.8d23215e.js:1 Uncaught ReferenceError: modelValue is not defined
    at HTMLInputElement.u.onUpdate:modelValue.l.<computed>.l.<computed> [as _assign] (index.8d23215e.js:1)
    at HTMLInputElement.<anonymous> (vendor.52e08146.js:1)

NOTE:when I write it not in

  • vite
  • v-model (custom component)
  • in
  • production env

CherishTheYouth avatar Mar 15 '22 01:03 CherishTheYouth

Is there a more realistic scenario in which this happens?

In your example, you use v-model="modelValue", which is not allowed - you can't mutate props.

This is likely the root cause of the error: The compiler so far doesn't take this possibility into account as your can't v-model a prop directly..

LinusBorg avatar Mar 15 '22 11:03 LinusBorg

    <input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)"/>

lidlanca avatar Mar 15 '22 20:03 lidlanca

that doesn't create any problems.

LinusBorg avatar Mar 15 '22 21:03 LinusBorg

that is the working alternative.

a prop should technically not be used in v-model since its read-only, but the compiled output can still be more correct. by referencing via __props in the vmodel logic.

also <input v-model="someProp.x" /> will actually compile correctly with __props which make sense, because only the top level is readonly.

someProp in template is referenced correctly __props.someProp but in v-model it is referenced as a declared variable (someProp).

<script setup>
import {toRef} from "vue"

let props = defineProps(['someProp'])
// uncomment line will prevent error.
//const someProp = toRef(props,'someProp')
</script>

<template>

<input v-model="someProp" />
</template>

compiled output

return (_ctx, _cache) => {
  return _withDirectives((_openBlock(), _createElementBlock("input", {
    "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => (_isRef(someProp) ? (someProp).value = $event : null))
  }, null, 512 /* NEED_PATCH */)), [
    [_vModelText, __props.someProp]
  ])
}
}

lidlanca avatar Mar 15 '22 22:03 lidlanca

thank you all, I will modify my codes, best wishes for you two,god kiss you

CherishTheYouth avatar Mar 16 '22 08:03 CherishTheYouth

@lidlanca @LinusBorg

CherishTheYouth avatar Mar 16 '22 08:03 CherishTheYouth

I run into this issue when deploying in production yesterday here is some example : Default version Its working without error, dev & prod

Setup version Working in dev without warning or error, break in production : Uncaught ReferenceError: modelValue is not defined at _createElementBlock.onUpdate:modelValue._cache.<computed>._cache.<computed> (about:srcdoc:50:74) at HTMLInputElement.<anonymous> (vue.runtime.esm-browser.js:10302:21)

@LinusBorg I understand v-model="modelValue" is not something you want, but it should throw a warning in dev at least, having an inconsistent behavior between dev and production is dangerous.

For this kind of component having <BaseCheckbox v-model="" /> instead of <BaseCheckbox v-model:value="" /> is nicer to me.

Shhu avatar Apr 11 '22 08:04 Shhu

I didn't actually close this, the author did and j missed that.

LinusBorg avatar Apr 11 '22 08:04 LinusBorg

I feel it's a bug.

    } else {
      // v-model used on a potentially ref binding in <script setup> inline mode.
      // the assignment needs to check whether the binding is actually a ref.
      const altAssignment =
        bindingType === BindingTypes.SETUP_LET ? `${rawExp} = $event` : `null`
      assignmentExp = createCompoundExpression([
        `${eventArg} => (${context.helperString(IS_REF)}(${rawExp}) ? (`,
        createSimpleExpression(rawExp, false, exp.loc),
        `).value = $event : ${altAssignment})`
      ])
    }

edison1105 avatar Apr 11 '22 08:04 edison1105

I also meet this problem, I think this error should be output in dev env.

zkwolf avatar Apr 26 '22 11:04 zkwolf

app.js?id=a6222ed598160499e804f155d81a5a68:2 Uncaught ReferenceError: modelValue is not defined at HTMLSelectElement.onUpdate:modelValue.n.<computed>.n.<computed> [as _assign] (app.js?id=a6222ed598160499e804f155d81a5a68:2:81720) at HTMLSelectElement.<anonymous> (vendor.js?id=5ee158ef74247e1f887d62c380ab41c5:2:566971)

also getting this in prod

jackkitley avatar Jun 06 '22 07:06 jackkitley

Is this a big issue? i see it was issued 28 days ago now @yyx990803 . Thanks!

jackkitley avatar Jun 07 '22 14:06 jackkitley

Hi @yyx990803 @LinusBorg

I have created a playground for this problem: Link

If click on the PROD button in menu, you will see this error message:

modelValue is not defined

Whereas in the DEV mode, everything works perfectly fine.

Expecting a fix to this problem, since its critical for making reusable components. Any suggestions for alternate approach is also appreciated 🙏


Attaching screenshots:

PROD Mode

sfc vuejs org_ (2)

DEV Mode

sfc vuejs org_ (3)

preeteshjain avatar Jul 04 '22 13:07 preeteshjain

This needs to be fixed as soon as possible. I wasted more than 2 hours because of this no-sense problem. Working dev but not in production without explanation is frustrating. And there is no official solution.

ArkoxHub avatar Jul 04 '22 22:07 ArkoxHub

This bug is very annoying but there's a workaround:

if (import.meta.env.PROD) { var modelValue = toRef(props, 'modelValue') }

it's a ugly but it works, the only thing you need to adapt is the production check (import.meta.env.PROD is a Vite variable).

franklx avatar Jul 05 '22 05:07 franklx

This approach is even documented in docs, but it doesn't work in production:

image

preeteshjain avatar Jul 05 '22 05:07 preeteshjain

@preeteshjain Your approach is not mutating an object or array prop, and you approach is still mutating a prop, so that's not what the docs recommend.

LinusBorg avatar Jul 05 '22 05:07 LinusBorg

I had a similar issue recently, the workaround I went with was creating a computed with a get and set. Passing the get to model and having the set emit the update.

const value = computed({
  get() {
    return props.modelValue;
  },
  set(value) {
    emit("update:modelValue", value);
  },
});
<input v-model="value"  />

NielsJorck avatar Jul 05 '22 07:07 NielsJorck

To be honest, I still do not understand this problem. I have form components which are working as expected using

This is my code, let's see if you could see what's happening here, at least to understand the issue.

Partent component

<section class="categories"> <BaseSelect label="Selecciona la categoría" :options="getCategoryNames" v-model="categorySelected" /> </section>

export default { components: { BaseSelect }, data() { return { categorySelected: 'todos',
...

Child component

<script setup>
import UniqueID from '../../features/UniqueID';
const uuid = UniqueID().getID();

const props = defineProps({
    label: String,
    placeholder: String,
    options: [Array, Object],
    modelValue: [String, Number]
})

const emit = defineEmits(['update:modelValue'])
function updateValue(value) {
    emit('update:modelValue', value)
}

function getUID() {
    console.log(UniqueID);
}
</script>

<template>
    <div class="form-control">
        <label :for="uuid" v-if="label">{{ label }}</label>
        <select :id="uuid" :value="modelValue" class="input-field" v-bind="$attrs"
            @change="updateValue($event.target.value)">
            <option v-for="option in options" :value="option" :key="option" :selected="option === modelValue">
                <span>{{ option }}</span>
            </option>
        </select>
    </div>
</template>

As I said before, I have other form components created the exactly same way and they are all working fine. If you need an example of them, please let me know.

If you will, you can have a look in the console where the error happens in production: https://www.amigoinvisible.net/regalos-amigo-invisible

Thanks.

ArkoxHub avatar Jul 05 '22 07:07 ArkoxHub

To everyone facing this issue, the only way to fix this problem is to make sure (double check carefully) that you are not modifying your props from child component.

We are now modifying the data from the parent only and passing it down as a prop to the child component and it is working in production environment.

Here's how I have fixed the problem in the example I shared above:

Note that each scenario is different and the solution above is just an example. Hope the solution gives some insipiration for you guys to fix your problem. You guys will have to figure out your own approach making sure you don't change Parent's props from Child component.

That being said, I think the only problem with Vue 3 currently is that it isn't notifying about this problem in DEV environment.

preeteshjain avatar Jul 05 '22 08:07 preeteshjain

Isn't it the main purpose of 2 way data binding? The availability to change the value of a property either in parent or child component?

ArkoxHub avatar Jul 05 '22 09:07 ArkoxHub

@preeteshjain There is two issues :

  • Different behavior between dev and prod (and thats a huge problem)
  • Different behavior between legacy component and setup component

Shhu avatar Jul 05 '22 10:07 Shhu

This issue flared up recently. Am i doing something wrong in vue? i dont think im modifying props as its bad practise and throws error.

We are still unable to release in prod because of this issue.

jackkitley avatar Jul 05 '22 12:07 jackkitley

Hi guys, I have found a solution for my case and I really wish if someone can explain me why is working like this instead of what I've doing until now.

This is the parent Component calling BaseSelect child component. v-model now is linked with object attribute of parent. Like this is working.

<BaseSelect 
            label="Selecciona la categoría" 
            :options="categories.names" 
            v-model="categories.categorySelected"
/>
<script>
import BaseSelect from '../components/form/BaseSelect.vue';

export default {
    components: { BaseSelect },
    data() {
        return {
            categories: {
                names: [],
                categorySelected: 'todos'
            },
...

The only thing I have changed, is that above I am using a JavaScript object, and now a normal attribute of the component. So, categorySelected lonely is not working. But if we put categorySelected as a property of an object like categories.categorySelected yes...

<BaseSelect 
            label="Selecciona la categoría" 
            :options="categories.names" 
            v-model="categorySelected"
/>
<script>
import BaseSelect from '../components/form/BaseSelect.vue';

export default {
    components: { BaseSelect },
    data() {
        return {
            categorySelected: 'todos',
            catalog: [

Does it makes sense? At least, now it's working in production.

ArkoxHub avatar Jul 05 '22 22:07 ArkoxHub

FWIW. I changed the child component so that the v-model in the child was not v-model="modelValue" but rather implemented a ref variable. ie.

const childValue = ref(props.modelValue)

<input v-model="childValue">

By no means am I suggesting this is the right way to do it, but it worked for me. As LinusBorg said, mutating the props seems wrong. However, this should still show up as an error in Dev environment as stated by others.

tyronksmith avatar Jul 06 '22 04:07 tyronksmith

What am i doing thats wrong here? Im setting form data. not modifying a prop

const form = useForm({ contact: { title: null, first_name: null, last_name: null, email: null, cell_number: null, national_id_number: null, company: null }, lead: { drivers_license: 0, source: null, stock_id: null } })

              <div class="form-wrapper-block mb-4">
                                <Label>{{ __('Active Driving License') }}</Label>
                                <SelectInput class="form-select" v-model="form.lead.drivers_license" data-step-index="0" id="lead.drivers_license">
                                    <option :value="null">{{ __('Please select') }}</option>
                                    <option :value="1">{{ __('Yes') }}</option>
                                    <option :value="0">{{ __('No') }}</option>
                                </SelectInput>
                                <InputError :message="form.errors['lead.drivers_license']" />
                            </div>

app.js?id=95d4d35cb093fd65bfc91de1740438d4:2 Uncaught ReferenceError: modelValue is not defined at onUpdate:modelValue.n..n. [as _assign] (app.js?id=95d4d35cb093fd65bfc91de1740438d4:2:87902) at HTMLSelectElement. (vendor.js?id=59d311c9d4e7b11ec500e12a26885332:2:1181914) onUpdate:modelValue.n..n. @ app.js?id=95d4d35cb093fd65bfc91de1740438d4:2 (anonymous) @ vendor.js?id=59d311c9d4e7b11ec500e12a26885332:2

jackkitley avatar Jul 12 '22 07:07 jackkitley

@jackkitley What I was suggesting is; Is your SelectInput a custom component? If so, have a look at the v-model inside that. My example code above is within my custom component. I could not have v-model="modelValue" within the custom / child component. Apologies. I didn't mean to hijack an issue thread.

tyronksmith avatar Jul 12 '22 22:07 tyronksmith

ArkoxHub I was having the same error, i don't know why this method works. But it has really helped for now

ETIM-PAUL avatar Jul 23 '22 01:07 ETIM-PAUL

v-model="modelValue" works with DEV

v-model="props.modelValue" works with PROD

any solution yet ??

I was able to make this work with the following approach with ref

  const props = defineProps({
      modelValue: {
          type: String,
          required: true
  }
  
  const emit = defineEmits(['update:modelValue'])

  const content = ref(props.modelValue)
  
  const handleInput = () => {
      emit('update:modelValue', content.value);
  }


dulshan300 avatar Aug 02 '22 14:08 dulshan300

I have exactly this problem. I'm new to vue.js and spent nearly 20 hours to found the issue mentioned. I still cant fix the problem. SPent 2 hours reading here, over and over again, but to luck. Anyone that can explain a bit more I will greatly apprechiate. I'm a programmer and coding for many years. Just not a JAVA /TS coder.

My code

`

let props = defineProps(['accessKey','secretKey'])

const accessKey = toRef(props,'accessKey') const secretKey = toRef(props,'secretKey')

`

evanerwee01 avatar Aug 05 '22 04:08 evanerwee01