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

Automatically substitute a term with a clickable link

Is there a way to automatically turn every word into a hyperlink? I have specific words that need to be linked. For example, I want Ronaldo (Only for the First Appearance) to link to a certain page. However, my attempted method did not work. <p> Ro ...

Exploring the power of Angular JS promises through ng-repeat

My current project involves fetching latitude and longitude coordinates from a postcode using an API, then utilizing those coordinates to retrieve data on street level crimes near that location on a specific date through the UK police API. However, I have ...

Ways to invoke a JavaScript function using a JSON string

Suppose I make an AJAX post request using jQuery with the following structure: $.post('MyApp/GetPostResult.json', function(data) { // what should be implemented here? }); When the result is as follows: { "HasCallback": true, "Cal ...

Are elements in React Native PanResponder capable of being clicked?

While I have movable panresponders, I also require the ability to click on one for an onPress event. Is it achievable? At present, they are <View> elements. If I attempt to switch them to <TouchableOpacity> or a similar element, it results in ...

Tips for ensuring a successful POST request using a hyperlink tag

Within my view file, I have the following code snippet: <a href="/logout" role="button" class="btn btn-lg btn-primary left-button">Logout</a> Inside my app.js file, I have implemented the following route for loggi ...

ReactJS component disappearing behind the Navbar

When using my React app, I have a navigation bar at the top. The Navbar component is called in App.js, and the code snippet below shows how it is implemented. export default function App() { return ( <Router> <Fragment> ...

Having trouble with loading JSON due to a Cross-Domain AJAX problem

I am facing a challenge with a URL that I do not have control over, which returns a JSON string. Within this JSON string, there is a URL that I am attempting to load using JavaScript/jQuery AJAX. However, I am encountering a Cross-Domain issue while trying ...

What is the best way to bundle a .js file containing code with the export default syntax?

I have a single .js file with the following code: export default (vueInst, obj, events) => { for (const eventName of events) { ... } } An issue has occurred: Error at Function.missingTransform in /node_modules/buble/dist/buble.cjs.js:376:9 T ...

Creating a specialized Angular directive for handling input of positive numbers

I am working on an application that requires a text field to only accept positive integers (no decimals, no negatives). The user should be restricted to entering values between 1 and 9999. <input type="text" min="0" max="99" number-mask=""> While s ...

Exploring the world of Django: Using model formsets with the power

I am struggling to use Ajax for submitting my zipped formsets. The code functions flawlessly without Ajax, but as soon as I try to incorporate Ajax, I encounter a ValidationError: [u'ManagementForm data is missing or has been tampered with'] Thi ...

Sending data retrieved asynchronously to child components' props

Currently, I am developing an application that fetches an array of news items from a remote source and showcases them on a webpage. After successfully calling the endpoint using $.getJSON(), as indicated by console logs, I integrated this call into the pa ...

Is there a way to incorporate a fade-in effect when I trigger the expand function in this script?

I recently came across a jQuery plugin for expanding and collapsing content. I am interested in adding a fade-in effect to this plugin specifically when the EXPAND button is clicked. How can I accomplish this? $(document).ready(function () { var maxlines ...

Rendering sibling components on Multiple Select Material UI in React

Here is my current challenge: I am trying to implement a multiple select feature with checkboxes in React using Material UI. The desired outcome should resemble the image linked below: https://i.stack.imgur.com/TJl8L.png I have structured my data in an a ...

Having trouble with Axios PUT request not sending complete data to the server using JavaScript

One issue I'm encountering is that when sending an axios request with specific data, not all of the data gets updated in the user model. Here's a look at my code: Here is the Front-End code with axios request: import axios from "axios" ...

Example TypeScript code: Use the following function in Angular 5 to calculate the total by summing up the subtotals. This function multiplies the price by the quantity

I have a table shown in the image. I am looking to create a function that calculates price* quantity = subtotal for each row, and then sum up all the subtotals to get the total amount with Total=Sum(Subtotal). https://i.stack.imgur.com/4JjfL.png This is ...

Tips for Using Threejs Loaders in a React App

Greetings and thank you for taking the time to read my question. ...

Transfer the data-url attribute to the jQuery ajax URL

I am facing an issue with my form which includes a data-attribute holding a URL to an API JSON file: <form class="product" action="#" data-url="dist/scripts/main.js"> [...] </form> My goal is to transfer the URL from the data attribute to ...

Why is applying CSS on an li element using .css() in JQuery not functioning?

Hey there! Could you please review my code? I'm attempting to add a top position to the <li> element using a variable in jQuery, but I'm not sure how to do it. Here's the code: <script> $(document).ready(function(){ ...

the language of regular expressions expressed in strings

As a beginner in Javascript and regular expressions, I found myself stuck on how to create a route that matches all URLs starting with /user/.... Initially, I thought of using app.get(/user/, function(req, res){ /*stuff*/}); However, curiosity led me to ...

Translating PCRE(PHP) regular expressions into ECMAScript(Javascript) syntax

I have this PCRE Regex that I'm using to validate JSON strings, but now I need to convert it to JavaScript so I can use it for validation in a React application. PCRE Regex - /(?(DEFINE) (?<json>(?>\s*(?&object)\s*|\s* ...