Refreshing the v-model in a child component

Within my parent component, the code structure is similar to this:

<template>
    <ProductCounter v-model="formData.productCount" label="product count" />
</template>

<script setup>

const initialFormData = {
    productCount: null,
    firstname: '',
    surname: '',
    phone: '',
    email: '',
    postcode: '',
    submittedOnce: false,
    errors: []
}

let formData = reactive({ ...initialFormData });

const clearUI = () => {
    formData = reactive({ ...initialFormData });
    triggerInlineForm.value = false;
}

</script>

The child component is structured like this:

<template>
    <div class="form__row" @reset-counts="resetCount">
        <div class="counter__row">
            <label>{{ label }}</label>
            <div class="form__counter">
                <button class="form__button--decrease form__button--circle form__button--animate-scale" :disabled="value == 0 || props.disabled" @click.prevent="decreaseCount()">
                    <i>
                        <FontAwesomeIcon :icon="['fal', 'minus']" />
                    </i>
                </button>
                <input type="text" v-model="value" :disabled="props.disabled" @input="updateQty" placeholder="0"/>
                <button class="form__button--increase form__button--circle form__button--animate-scale" :disabled="props.disabled" @click.prevent="increaseCount()">
                    <i>
                        <FontAwesomeIcon :icon="['fal', 'plus']" />
                    </i>
                </button>
            </div>
        </div>
    </div>
</template>

<script setup>
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';

const emits = defineEmits(['update:modelValue', 'resetCounts']);

const props = defineProps({
    label: {
        type: String,
        required: true
    },
    modelValue: {
        type: String,
        required: true,
        default: 0
    },
    disabled: {
        type: Boolean,
        required: false
    }
});

const value = ref(props.modelValue);

const updateQty = () => {
    emits('update:modelValue', value.value)
}

const increaseCount = () => {
    value.value++
    emits('update:modelValue', value.value)
}

const decreaseCount = () => {
    value.value--;
    emits('update:modelValue', value.value)
}

</script>

When triggering clearUI from the parent, I anticipated that when formData is reset, the v-model of ProductCounter would return to 0. However, it does not. Where might I be making an error?

Answer №1

Link to the live solution

Next time, please provide a minimum reproducible example on https://play.vuejs.org/. Regarding your question:

In Vue, do not overwrite reactive variables...

Mutate them instead

(Object.assign(formData, initialFormData))
:

Also, avoid dereferencing component properties like this:

(const value = ref(props.modelValue))
. This can cause the properties to lose their reactivity due to copying a primitive value.

The optimal way to create a v-model pattern is by using computed, which allows direct manipulation in the template.

const value = computed({
  get(){
    return props.modelValue;
  },
  set(val){
    emits('update:modelValue', val);
  }
});

Additionally, ensure that your count property is defined as a number, not a string (to avoid Vue warnings):

modelValue: {
    type: Number,
    required: true,
    default: 0
},

Furthermore, there's no need to update the prop on the input event since you're already utilizing v-model. Also, make sure to convert your input's model to a number:

<input type="text" v-model.number="value" :disabled="props.disabled" placeholder="0"/>

Here's what you have:

App.vue

<template>
    <p>
      <ProductCounter v-model="formData.productCount" label="product count" />
    </p>
    <button @click="clearUI">
      Clear
    </button>
    <div>
      {{ JSON.stringify(formData) }}
  </div>
</template>

<script setup>
import ProductCounter from './ProductCounter.vue'
import {reactive} from 'vue'
  
const initialFormData = {
    productCount: 0,
    firstname: '',
    surname: '',
    phone: '',
    email: '',
    postcode: '',
    submittedOnce: false,
    errors: []
}

let formData = reactive({ ...initialFormData });

const clearUI = () => {
    Object.assign(formData, initialFormData);
}

</script>

ProductCounter.vue:

<template>
    <div class="form__row">
        <div class="counter__row">
            <label>{{ label }}</label>
            <div class="form__counter">
                <button :disabled="value == 0 || props.disabled" @click.prevent="value--">
                -
                </button>
                <input type="text" v-model.number="value" :disabled="props.disabled" placeholder="0"/>
                <button :disabled="props.disabled" @click.prevent="value++">
                 +
                </button>
            </div>
        </div>
    </div>
</template>

<script setup>
import {computed} from 'vue';
const emits = defineEmits(['update:modelValue']);

const props = defineProps({
    label: {
        type: String,
        required: true
    },
    modelValue: {
        type: Number,
        required: true,
        default: 0
    },
    disabled: {
        type: Boolean,
        required: false
    }
});

const value = computed({
  get(){
    return props.modelValue;
  },
  set(val){
    emits('update:modelValue', val);
  }
});


</script>

Answer №2

When you modify the formData in the clearUI() function, you are updating the variable's content:

let formData = reactive({ ...initialFormData });

const clearUI = () => {
    formData = reactive({ ...initialFormData });
}

However, this change does not affect the object that was originally linked to the template during component setup. To address this issue, you can utilize ref and assign a new value to it:

const formData = ref({ ...initialFormData });

const clearUI = () => {
    formData.value = { ...initialFormData };
}

Alternatively, you can update the properties individually:

const formData = reactive({ ...initialFormData });

const clearUI = () => {
    Object.assign(formData, initialFormData);
}

Another concern is that when setting the value within ProductCounter to the initial value of props.modelValue, it remains static since it is not a reactive property. To resolve this, you can add a watcher:

const value = ref(props.modelValue);
watch(
  () => props.modelValue,
  () => value.value = props.modelValue
)

This way, value will update accordingly when props.modelValue changes.

You can see this in action in a playground

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Setting up a web server with a cyclical challenge

I've encountered an issue while hosting my server.js file with the configured API on Cyclic. The deployment was successful, but every endpoint call is returning a status 500 error. Additionally, I have hosted the React front-end on github pages. I&apo ...

Utilizing Node and Electron to dynamically adjust CSS style properties

Having a dilemma here: I need to access the CSS properties from styles.css within Electron. Trying to use document.getElementsByClassName() won't work because Node doesn't have document. The goal is to change the color of a specific div when the ...

Using async/await with Middleware in Express

I'm struggling to grasp the concept of writing middleware in Express that uses async/await without leaving a floating Promise after execution. Despite reading numerous blogs and StackOverflow posts, it appears that there is a common pattern for utiliz ...

Create a dynamic process that automatically generates a variety of div elements by using attributes from JSON data

Is there a way to organize the fixtures from this data into separate divs based on the matchday attribute? I've tried using Underscore's groupBy function but I'm unsure how to dynamically distribute the data into individual divs for each re ...

Do not fetch data again after a re-render

My code is facing an issue where every time I click toggle, the Child component re-renders and triggers another API request. My goal is to fetch the data once and then keep using it even after subsequent re-renders. Check out the CodeSandbox here! functio ...

Transitioning from one bootstrap modal to another in quick succession may lead to unexpected scrolling problems

I'm facing a challenge with two modals where scrolling behavior becomes problematic when transitioning from one to the other. Instead of scrolling within the modal itself, the content behind it is scrolled instead. In order to address this issue, I im ...

Having trouble getting Vue to dynamically change images on mouseover?

Within my Vue application, I have successfully displayed an image using the following code: <img src="@/assets/images/icon-filter-up.png"> However, when I attempt to dynamically pass the image link like this: <img :src="imgLink"> And within ...

Effectively implementing an event observer for dynamically loaded content

I want to set up an event listener that triggers when any of the Bootstrap 3 accordions within or potentially within "#myDiv" are activated. This snippet works: $('#myDiv').on('shown.bs.collapse', function () { //code here }); Howeve ...

The error message "Then is not defined in Element UI Plus" is throwing a ReferenceError

I am currently utilizing the vue3 framework along with element-ui-plus for a project I am working on. However, when attempting to use the MessageBox feature within element-ui-plus, an error Uncaught ReferenceError: then is not defined occurred. All other ...

Struggling to implement dynamic templates in an Angular directive

In the process of developing a small angular application that consists of 3 modes - QA, Production, and Sandbox. In the QA mode, there are multiple buttons for users to select values which then populate "myModel". The other two modes, Production and Sandbo ...

How can I code a script to import JSON data into MongoDB database?

I have a JSON file named "data.json" that contains an array of people's names as shown below: "data": [ { { "name":"John", "age":30, } { "name":"Mark", "age":45, } } ] I am ...

What is the issue with retrieving HTML from an iframe in Internet Explorer when the contents are

Here is the script I used to generate an iframe: Ifrm = document.createElement("IFRAME"); document.body.appendChild(Ifrm); IfrmBod = $(Ifrm).contents().find('body'); IfrmBod.append('<p>Test</p>'); The jQuery function for a ...

How can I utilize the mapv tool created by Baidu in a Node project?

I need assistance converting the following code to a node environment. The code can be found at Here is the code snippet: var map = new BMap.Map(slice.selector, { enableMapClick: false }); // Create Map instance map.centerAndZoom( ...

Numerous options available in a dropdown menu

I have a Dropdown menu that offers various functions. "Update" option redirects to a page for updating information "Export Form" option redirects to a page for exporting the form Although the current code allows these actions, I am facing an issue with ...

URL Construction with RxJS

How can I efficiently create a urlStream using RxJS that incorporates multiple parameters? var searchStream = new Rx.ReplaySubject(1); var pageStream = new Rx.ReplaySubject(1); var urlStream = new Rx.Observable.create((observer) => { //Looking to ge ...

Activate the button solely when the text field has been populated without any spaces

Searching for a solution to my query, but all the suggestions I've encountered don't consider spaces as valid input. In the join function I have, the button should be disabled if the user enters only spaces. A requirement is for actual text inpu ...

`Javascript framework suggests ajax navigation as the preferred method`

What is the best way to handle ajax navigation using jQuery? I have recently experimented with a simple jQuery ajax code to implement ajax-based navigation for all the links on a webpage. $('a').click(function(e){ e.preventDefault(); ...

I encountered an error message stating "Unexpected token {" while trying to import the module "express-fileupload"

Struggling to implement file uploading with NodeJS on Ubuntu, encountering errors. Upon adding const fileUpload = require('express-fileupload'); the app fails to compile, throwing this syntax error: 2|theproje | /home/asgeir/nodejs/first_test ...

Using JSON data to render images onto a canvas

I am encountering an issue with a JSON array that I'm receiving from PHP. The array is indexed and has the following format (larger in real scenario) [ [ [17, 28, 1, "z"], [28, 31, 6, "b"], [8, 29, 6, "b"] ...

Ways to minimize renders while toggling a checkbox

I'm currently working on developing a high-performance checkbox tree component. To manage the checked checkboxes, I am utilizing a parent level state that contains an array of selected checkbox IDs => const [selected, setSelected] = useState([]); ...