How to use v-model in place of v-bind? - vue.js

here is a working custom component:
jsFiddle
<myselect :option="cnt" ></myselect>
above code works, now how to change it into v-model? following code won't work:
<myselect v-model="cnt"></myselect>
how to use v-model in this case? Thanks.

From the documentation:
<input v-model="something">
is just syntactic sugar for:
<input :value="something" #input="something = $event.target.value">
In your fiddle you are still referencing an option property, but the component no longer has one. You need to reference the value property to get the initial value of cnt and then emit an input event to update the cnt var being used as the model. https://jsfiddle.net/4yavj0en/2/

Related

Is is possible to call watch() on an input ref in Vue 3 to watch the input's value attribute?

I know that we can use the v-model directive on an <input> element and use watch() to trigger a function when the state of the <input>'s value attribute changes.
I'm trying, however, to watch an <input> ref. When I do, the watcher's function is executed when the element is mounted to the DOM, but does not trigger when the <input>'s value attribute changes.
Am I doing something wrong?
<script setup>
import { ref, watch, watchEffect } from 'vue'
const refA = ref(null)
watch(refA, () => {
console.log('refA changed')
console.log(refA)
console.log(refA.value)
console.log(refA.value.value)
}, { deep: true })
</script>
<template>
<input ref="refA" type="text" value="test" /> <br />
</template>
From what I know, template refs can be watched, but they only fire when the .value changes - that's the value of the ref, i.e. the HTML element, not the value of a HTML input element. That fits with what you describe, the watcher firing once at mount and never again. Also, it is not possible to deep-watch a HTML element.
It is fascinating to me how different template refs and data felt in Vue 2, when apparently the mechanism on script-side was always the same, which is now made obvious in Vue 3 composition API. But they still do different things, where one gives access to a HTML element or a Vue component instance, and the other stores a value. I guess the big difference comes from using a ref in the ref attribute, not from the way it is declared.
Anyway, if you want to bind to the value of an input, use v-model (or v-bind:modelValue and #update:modelValue), but if you need access to the node for whatever reason, use ref=. If you want to do both, you need to use both, it is not possible to use just the template ref.
I am not sure what you are trying to achieve. if you don't need to use v-model then you should use v-bind and emit the input event, like the below.
<template>
<input :value="refA" type="text" #input="refA = $event.target.value" /> <br />
</template>
This will trigger watch on every update to your input.
A template ref which you have used as part of your example cannot be used to watch the internal value of the component.See below comments thread regarding the same.

Vue seems to be calling all custom directives more often than expected. Why, can this be avoided?

I'm only a few months into vue coming from an angularjs background.
I built my first custom directive and it's acting a little odd to me.
Vue.directive('silly',{
componentUpdated: function (el, binding, vnode) {
console.log("it was called");
}
});
and I place it on my form like this:
<form id="opt-cpmt-form" method="post" class="mri-grid mri-p-none">
<label for="one">name<input id="one" type="text" v-model="local.name" v-silly class="form-control"></label><br/>
<label for="two">phone<input v-isnumeric id="two" type="text" v-model="local.phone" class="form-control "></label><br/>
<label for="two">zip<input id="three" type="text" v-model="local.zip" class="form-control" ></label><br/>
</form>
It kinda works...the part that I didn't understand is that my v-silly directive is called when any of the other fields are updated too. It seems to be related to the model but I only want my directive called when the field changes.
Any ideas??
It's an expected behaviour as the component updates whenever a piece of its data object is updated. To not trigger the logic too many times, you can create an event listener when the directive is bound to its parent and then run the logic when a desired event happens.
Vue.directive('silly', {
bind(el) {
this.updateCallback = function(event) {
// Your logic
};
el.addEventListener('input', this.updateCallback);
},
unbind(el) {
el.removeEventListener('input', this.updateCallback);
}
});
In case you plan to listen to the changes of v-model directive, bear in mind that it uses different events based on what element it's bound to. You can read more about that topic in v-model documentation.
v-model internally uses different properties and emits different events for different input elements:
text and textarea elements use value property and input event;
checkbox and radiobutton inputs use checked property and change event;
select fields use value as a prop and change as an event.
Also, from my experience when it comes to the form validation; I've done it using the directives and regretted it afterwards. I found it best to create reusable functions and create the custom form validation for every form. See custom form validation for more.

v-model concatenate a string with a var?

I've read answers such as this, but I can't get my template to compile.
I need to concatenate a string with a variable in v-model to bind to an array inside an object:
<li v-for="name in names">
<input type="checkbox" v-model="'checked.'+name">
....
I just get a compile error though.
Also when I do this:
:data-test="'checked.'+name"
It compiles fine, so it's something with v-model.
The compile error is:
Syntax Error: SyntaxError: Unexpected token (1:1161)
Just in case a slightly different perspective helps: Whether you use it in a v-model or :data-test directive
'checked.'+name
results in a string. Although it probably isn't what one would normally want to do, that is syntactically legal for an arbitrary v-bind (e.g. :data-test). That is not, however, syntactically legal for a v-model. As other have pointed out, v-model attempts to assign a value on "change" events. It would be equivalent, for example, to
'checked.foo' = true;
when what I think you want is
checked.foo = true;
It's hard to say for sure without seeing more of your code, but it may be the case that
<input type="checkbox" v-model="checked[name]">
is sufficient for you.
The v-model="name" helps you do two things.
:value="name"
#input="name = $event
However, in your case, you're doing v-model="'checked.'+name", which means:
:value="'checked.'+name" // Which is perfectly fine
#input="'checked.'+name = $event" // This would cause an error.
Documentation can be found here.
Below is some solution of mine: JsFiddle
computed: {
checkedName: {
// getter
get: function () {
return `${this.prefix}${this.name}`;
},
// setter
set: function (newValue) {
this.name = newValue.replace(this.prefix, '');
}
}
},
then
<input type="checkbox" v-model="checkedName">
You can't do that.
v-model is used for two-way data binding and is a syntactic sugar for :checked="myField" + #change="evt => myField = evt.target.checked in the case of a checkbox.
As you can see, myField must be a valid left-hand side expression in JS, this is by the way one of the rules precised by Vue to have a valid v-model:
The directive does not have the attribute value which is valid as LHS. E.g. <input v-model="foo() + bar()">
And that is exactly why your template doesn't compile. Vue can understand how to bind the data in one way because it can assign 'checked.'+name to a variable, but it can't assign a variable to 'checked.'+name - that is not a valid left-hand sign expression.

Vuejs keyup event trigger with keyCode and condition

I wanted to bind an event on condition and trigger it only when the user enters a particular key, For example,
I want to bind the event when the value of the label equal to tags
onTagAdd($event, index) : {} }" v-model="value">
onTagAdd: function (oEvent, index){
if(oEvent.key === ";"){
//some code
}
}
It's working as expected.
Further, I wanted to trigger the event only when ; (virtual key code = 186) pressed, so I have tried below code
onTagAdd($event, index) : {} }" v-model="value">
It is not working. I have referred vue.js documentation But, haven't got any workaround.
How to do this?
Note: I have eliminated this approach as it's giving me an error in IE for $event. still looking for the solution if any which solves the problem and support the browser compatibility.
Try this out and tell me if it worked.
<input type="text" v-on:keyup.186="{ (label == 'Tags') ? $event => onTagAdd($event, index) : {} }" v-model="value">
maybe the inline style doesn't support keycode in event type(not sure),try this out:
<input type="text" v-on:keyup.186="onTagAdd($event,2)" v-model="value">
methods: {
onTagAdd(event,index){
// put your logic codes here
console.info('==')
console.info(event)
console.info(index)
}
}
This page was helpful for me in Vue3: https://v3-migration.vuejs.org/breaking-changes/keycode-modifiers.html
3.x Syntax#
Since KeyboardEvent.keyCode has been deprecated, it no longer makes sense for Vue 3 to continue supporting this as well. As a result, it is now recommended to use the kebab-case name for any key you want to use as a modifier.
<!-- Vue 3 Key Modifier on v-on -->
<input v-on:keyup.page-down="nextPage">
<!-- Matches both q and Q -->
<input v-on:keypress.q="quit">
As a result, this means that config.keyCodes is now also deprecated and will no longer be supported.
TLDR: in Vue 3 keycodes appear to be deprecated in favour of using kebab case.
For example, #keyup.186 would be #keyup.;

How create a v-model modifier to a VueJS Component?

In VueJS there is some v-model modifies that pre-parse the binded value, for instance v-model.trim that removes whitespaces from the string.
How can I create my own modifier? for instance v-model.myparse
Today um using something like:
computed: {
name: {
get: function () { return parse(this._name);},
set: function (value) { _name = parse(value);}
}
What is very verbose.
I would it to be reusable to do something like:
<input v-model.myparse="name" name='n1'/>
<input v-model.myparse="name" name='n2'/>
<input v-model.myparse="name" name='n3'/>
<input v-model.myparse="name" name='n4'/>
computed properties with setters seems to do part of the work, but it is really useful with some few variables only, it becomes very verbose with a lot of properties.
First, adding adding a custom modified to v-model is under discussion but not yet implemented.
If it was implemented, you could extend the v-model and add a modifier to it.
Since that is not possible, you have a couple of options left, one of which is to use :value instead of v-model. Because v-model is just a syntactic sugar of following:
<input type="text" :value="message" #input="message = $event.target.value">
The above code is the same as:
<input type="text" v-model="message">
So, I suggest you replace the logic for the #input to something like this:
<input type="text" :value="message" #input="getModel">
Now, you can use a function to return a modified value as:
methods: {
getModel ($event) {
return $event.target.value.trim()
}
}
But all of what I mentioned can still be done with the v-model if you use a function.
Of course it goes without saying, you can create your own custom directive also.