Detecting changes in parent ref with Vue's v-modelIs this

I am struggling to implement two-way binding because I can't determine if the model ref is being changed by the parent or child component.

Using watch captures all changes without any indication of the source of the change.

<script setup>
// Parent.vue

const doc = ref('');

setTimeout(() => {
    // Update this ref AND trigger watch in Child.vue
    doc.value = 'new value from parent';
}, 2000);
</script>

<template>
    <Child v-model:doc="doc" />
</template>
<script setup>
// Child.vue

const doc = defineModel('doc');

setTimeout(() => {
    // DO NOT TRIGGER WATCH ON THIS ONE!
    // But still update parent `doc` ref
    doc.value = 'new inner value';
}, 3000);

watch(doc, newValue => {
    // Catch `new value from parent` but not `new inner value`
});
</script>

Answer №1

Employ a flag within the child component:

View on Vue SFC Playground

<script setup>
import {watch} from 'vue';
const doc = defineModel('doc');
let meChangingDoc = false;

setTimeout(() => {
    // DO NOT TRIGGER WATCH ON THIS ONE!
    // But still update parent `doc` ref
    meChangingDoc = true;
    doc.value = 'new inner value';
}, 3000);

watch(doc, newValue => {
  if(meChangingDoc) {
    meChangingDoc = false; 
    console.log('child changed doc');
    return;
  }
  console.log('parent changed doc')
  
    // Capture `new value from parent` but not `new inner value`
});
</script>

You can also utilize a generic function:

View on Vue SFC Playground

SyncRef.js

import {watch} from 'vue';
export default function syncRef(source){
  let syncing = false;
  return new Proxy(source, {
    get(_, prop){
      if(prop === 'watchSource') return function(cb){
        return watch(source, (...args) => {
          if(syncing) {syncing = false; return; }
          cb(...args);
        });
      };
      return Reflect.get(...arguments);
    }, 
    set(_, prop, val){
      if(prop === 'value'){
        syncing = true;
        source.value = val;
        return true;
      }
      return Reflect.set(...arguments);
    }
  });
}

Usage:

<script setup>
import syncRef from './SyncRef';
const model = defineModel('doc');
const doc = syncRef(model);

doc.watchSource(newValue => {
    // Catch `new value from parent` but not `new inner value`
});
</script>

Answer №2

Utilizing v-model simplifies the process of elevating state to the parent component and enabling two-way binding. Unless there is a specific requirement, like distinguishing between updates to child and parent state, there is no need to introduce local state unnecessarily.

// Child.vue
const parentState = defineModel();
const childState = ref();

watch(
  [parentState, childState],
  ([newParentState, newChildState], [oldParentState, oldChildState]) => {
    const isInitial = newChildState == undefined;
    const isParent = 
      newChildState === oldChildState &&
      (newParentState !== newChildState);

    if (isParent || isInitial) {
      childState.value = newParentState;
      // parent-only update logic
    } else {
      parentState.value = newChildState;
      // child-only update logic
    }
  },
  { immediate: true }
);

To prevent recursive changes in such watchers, additional conditions can be implemented to avoid them when necessary.

Answer №3

It is advisable to implement a flag to identify the source of the modification

<template>
  <Child v-model:doc="doc" />
</template>

<script setup>
import { ref } from 'vue';

const doc = ref('');

setTimeout(() => {
  doc.value = 'new value from parent';
}, 2000);
</script>

Additionally, here is the child.vue file for reference:

<script setup>
import { defineModel, watch, ref } from 'vue';

const doc = defineModel('doc');
const changeSource = ref('');

watch(doc, (newValue, oldValue) => {
  if (newValue !== oldValue && changeSource.value === 'parent') {
    console.log('Change from parent:', newValue);
  } else if (newValue !== oldValue && changeSource.value === 'child') {
    console.log('Change from child:', newValue);
  }
});
</script>

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

Issues with Nuxt 3's component transitions in Vue.js 3

I'm trying to implement a transition effect for a dynamic component in Nuxt v3 that moves from right to left when the content changes. The component is functioning correctly, but I'm having trouble getting the transition to work smoothly. <tem ...

What could be causing Next.js to throw an error upon completion of the MSAL OAuth process?

I encountered an error while building a website using next.js. The site is set up for production, and after the authentication process with MSAL for Azure AD integration, I am facing the below error during the OAuth loop. As a beginner in next.js coming fr ...

Unable to retrieve the value of a key from a node object

I'm baffled by how this is even possible For example, when I execute this code: console.error(order.Items[i]); The output is: { ItemURL: '', IsBundle: false, GiftTaxPrice: '0', GiftPrice: '0', GiftNotes: null ...

Browser encountering HTTP response that has been shorted extensively

When making an HTTP post request using axios, I am encountering an issue where the body of the response is a large 4MB string. axios({ method: 'POST', url: url, data: data, headers : headers, }) .then(function (response) { co ...

The primary view seamlessly integrates with the page following the invocation of the partial view

Whenever the button is clicked, a different partial view is returned based on the selected value from the drop-down list. Controller: [HttpPost] public ActionResult Foo(SomeViewModel VM) { var model = VM; if (Request.IsAjaxRequest()) { ...

How to change elements within an HTML string using jQuery

I am facing an issue where I have an HTML string (not DOM) that needs to be manipulated using jQuery. However, the code I am trying does not seem to work as expected: var html = '<div><h4><a class="preview-target" href="content.html"&g ...

I am having trouble getting Form.Select to work in my basic react-bootstrap application, despite following the guidelines provided in the react-bootstrap documentation. Can

I am new to utilizing "react-bootstrap with hooks" and currently in the process of creating a basic form. I have been following the guidance provided by react-bootstrap documentation, but I encountered an issue specifically with select/option form elements ...

Can anyone explain to me how to render attributes of a tag in Vue? I'm curious about how v-for interacts with HTML

<ul> <span class="tabs" :class="{ activeTab: selectedTab === tab }" v-for="(tab, index) in tabs" @click="selectedTab = tab" :key="tab"> {{ in ...

Verify the MAC address as the user types

I need to verify a form field for MAC Addresses and have implemented the following code that does the job. $('body').on('keyup', '#macAddess', function(e){ var e = $(this).val(); var r = /([a-f0-9]{2})([a-f0-9]{2})/i, ...

Is it common practice to include a variable in a function with the same name as the function itself?

Is it common practice to use a variable with the same name as the function within the function itself? const sum = function(arr) { let sum = 0; for(let i = 0; i < arr.length; i++) sum += arr[i]; return sum; }; Although this code runs s ...

Is it possible to modify a dependency import based on the specific request?

How can I switch between test mode and live mode using Stripe's SDK based on a query parameter in my internal form service for collecting payments? For example, consider this route: router.post("/:formId", function(req, res, next) { let isTest ...

Step by step guide on inserting a message (memo/sticky note) onto an HTML page

Wondering how to accomplish this: I've designed an HTML5 homepage and I'm looking to display a message to visitors, such as "Dear visitor, I am currently on vacation from....", in the form of a memo or sticky note positioned in the top right cor ...

Is there a way to simulate a shift+click event on Vue.js elements using the browser console?

My project utilizes the laravel-mix-vue framework for development. As a result, I have implemented several UI functionalities on the Vue side. For example: <template> //... <div @click.prevent="onClick($event)">click element ...

How can I extract the initial values from PHP after receiving the JSON + serialized data in the Ajax response following the first submission?

I am currently utilizing ajax to store data for multi part forms. My goal is to save each form's data upon clicking the next button. I have utilized form data to serialize the information, however, the format of the data is not aligning with my expect ...

How do I customize the appearance of an Element-UI data table in Vue?

I'm struggling to customize the color of an Element UI datatable in my project. I've tried several approaches, but so far, nothing has worked. Here is the code I am currently using: <template> <el-table :data="tableData.filter ...

The yarn installation process is not utilizing the latest available version

Working with a custom React component library my-ui hosted on a personal GitLab instance. In the package.json, I include the library like this: "my-ui": "git+ssh://<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="6 ...

Find the element that is being scrolled in order to delete its attributes

Issue with the sidebar causing whitespace on mobile devices, and scroll properties need to be removed. When expanding the sidebar, white space appears below it. Various display modes have been tried, but they all push elements below instead of keeping th ...

Tips for improving the scrolling function in Java with Selenium for optimal performance

I'm currently working on a project using Java in MAVEN. My task involves retrieving a URL, scrolling down the page, and extracting all the links to other items on that website. So far, I have been able to achieve this dynamically using Selenium, but ...

Issue with an external library in Angular 2

After generating my Angular 2 library using the yeoman generator and adding it to my main Angular project, I encountered errors when running the app in production mode. The specific errors include: WARNING in ./src/$$_gendir/app/services/main/main.compone ...

Does Vuejs have a counterpart to LINQ?

As a newcomer to javascript, I am wondering if Vue has an equivalent to LinQ. My objective is to perform the following operation: this.selection = this.clientsComplete.Where( c => c.id == eventArgs.sender.id); This action would be on a collect ...