Delcon
Delcon

Reputation: 2555

Vue Directive data binding like v-model

with v-model it is possible to bind a property of a reactive object to an html component

<input v-model="myObject.property" />

however, when I bind myObject.property to my custom directive:

<input v-customDirective="myObject.property" />

the value is accessible through binding.value - and this value is not reactive

I was trying to find out how they do it in vue - but I was not successful. How would it be possible to achieve a v-model like data binding?

Update:

I get an myObject.property change on the updated(el, binding, vnode, prevVnode) function. But I want to

directives: {
  'custom-input': {
     created(el, binding) {
        el.value = binding.value;
        el.addEventListener('input', () => {
           // ---- this does not work ------
           binding.value = el.value;
        });
     },
     updated(el, binding) {
        console.log('Value has updated', binding.value);
     }
  },

},

Upvotes: 0

Views: 85

Answers (1)

Alexander Nenashev
Alexander Nenashev

Reputation: 23602

You can't achieve what you want with this syntax, since the myObject.property is unwrapped and passed as by value to the directive and you cannot change any variable passed by value in JS.

Moreover myObject.property string isn't available at runtime and you can't guess the source of the directive's value.

The only way is to achieve your syntax is to make a Vue/Vite plugin to compile your directive to whatever you want (I did that to achieve behavior not possible with runtime directives only).

The closest what you want could be done with providing a ref bound to an object's property:

Playground

<script setup>
import { reactive , toRef } from 'vue'

const myObject= reactive({property: 'Hello World!'});

const vCustomDirective = {
  mounted(el, {value}){
    el.value = value.value;
    el.addEventListener('input', e => value.value = el.value);
  },
  updated(el, {value}){
    el.value = value.value;
  }  
}

</script>

<template>
  <h1>{{ myObject }}</h1>
  <input v-customDirective="toRef(myObject, 'property')" />
  <button @click="myObject.property = 'Clicked button'">Change property</button>
</template>

Alternative:

Playground

<script setup>
import { reactive , toRef } from 'vue'

const myObject= reactive({property: 'Hello World!'});

const vCustomDirective = {
  mounted(el, {value: [myObject, property]}){
    el.value = myObject[property];
    el.addEventListener('input', e => myObject[property] = el.value);
  },
  updated(el, {value:[myObject, property]}){
    el.value = myObject[property];
  }  
}

</script>

<template>
  <h1>{{ myObject }}</h1>
  <input v-customDirective="[myObject, 'property']" />
  <button @click="myObject.property = 'Clicked button'">Change property</button>
</template>

Upvotes: 0

Related Questions