Why does Vue's v-model update the data much more later after the click event was fired? - vue.js

I have a very small checkbox component which I was trying to listen to via the #click event. But the v-model data updates 7ms later than the event callback. So I had to remove the #click listener and to add a computed property/method of the checked value and add a watch method for it to $emit the updated checked value. It works fine this way. But I just want to understand what's going behind the scenes. Can someone explain? Or maybe reference to a good resource on the net?
I also couldn't find the v-model methods in the Vue's source code. Where can I find v-model related code in the node_modules?

You should try using #change instead and then print it for testing. For Example:
<input
type="checkbox"
v-model="item"
#change="sendEvent"
/>
sendEvent () {
this.$emit('click', this.item);
},

Related

Why does Vue update variable in parent - no event used

According to Vue.js docs (Components: One-Way data Flow) I assumed that I'm not able to update parent's variable from child component.
How is it possible, that when I bind a variable to child component, it is updated without emitting any event?
Here is Vue SFC playground example.
How can I use a copy of variable in child component without propagating changes to parent?
Edit: I'm curious why the property userAccount in AccountPage.vue gets updated.
There's no v-model, no events submitted, yet it still gets updated.
Edit:
In AccountForm.Vue an reactive and mutable ref object is created when writing
const userAccount = ref ({..})
Changing the reactive object will affect the parent. If you remove the ref() you'll see that the parent isn't updated.
https://v3.vuejs.org/api/refs-api.html#ref
Old answer:
When you use v-model you create a two-way data binding. V-model is a shorthand for v-bind and input, that is you bind data to child via v-bind and receive data via input event that is emitted from child.
So,
<input v-model="searchText">
is the same as writing
<input
v-bind:value="searchText"
v-on:input="searchText = $event.target.value"
>
https://v2.vuejs.org/v2/guide/components.html#Using-v-model-on-Components
The example demonstrates the usage of v-model and as stated in the docs
Custom events can also be used to create custom inputs that work with
v-model. Remember that:
<input v-model="searchText">
does the same thing as:
<input v-bind:value="searchText" v-on:input="searchText = $event.target.value">
meaning it does fire an event

Vue: watching a v-model's 'value' in a component

I have this component which has a v-model directive:
<my-component v-model="someData.someProp"></my-component>
Now, I'd like to be able to watch this piece of data inside the component and make changes based on this model changing via outside influences. So here's what I tried and it's not working:
watch : {
value (newVal, oldVal) {
// ...
}
}
It seems like it should work, or something comparable should be out there, but I just can't find it right now.
EDIT:
The most common answers I found and provided center on using the watcher to watch the data as if it's inside the parent component - but I'd like to watch it inside the child component without concern of what's going on in the parent.
I'm able to work around this by not using v-model and using simple named properties such as :my-data="someData.someProp" at which point I can successfully watch a myData variable inside the child component. I can also use #input to set the data back in the parent component if it's changed from within. But v-model is shorter and if there's a way to use that instead of a workaround that would be preferable.
From what I understand you are trying to pass a prop to your child component from your parent one and watch it in your child.
Assuming you have something like this in your parent:
<div>
<my-component v-model="someData.someProp"></my-component>
</div>
I understand you are getting undefined for that prop when you watch it, which is normal because you should pass it like this:
<my-component :myValue="someData.someProp"></my-component>
Then you should have access to the prop trought this.myValue.
If someData.someProp changes on the parent component it will automatically reflect on the child one.
Again this is what I could understand from your explanation and the amount of code you provided.

Access dom element with vuejs and vuetify in v-tabs

I have a div tag with an 'id="meet"' into v-tabs.
I need to access the node of this tag after I click a tab. I am using
let node=document.querySelector("#meet").
My problem is that it always returns "null". Here is the codepen: https://codepen.io/luizalves/pen/WNrepxz
What is wrong here?
There is no guarantee that after $nextTick you will see DOM element immediately.
<div id="meet"></div>
<button #click="onTest">test</button>
...
methods: {
onTest () {
let node=document.querySelector("#meet");
console.log('meet here',node)
},
After you clicked test you'll see in a console:
"meet here" "<div id='meet'></div>"
You can try to extract this div with an unique id into a separate component and inside it you can use mounted hook to know that it exists in DOM
Of course, according to vuetify docs, you may add eager prop to v-tab-item component, and that's it, but...possibly you are doing something wrong.
It's not a good idea to always add this prop because you are losing one of the vuetify 2.X advantages - lazy loading. This may lead to big performance problems if there are many elements on the tab.
Maybe it would be better if you will work directly with reactive variables (like variables in v-model directive), not DOM objects.

Default-expand-all doesn't work for q-tree? Vue.Js

I have to code a web application and the most important element is the q-tree. I'm already able to load and show data (passing an array called list), but I want that all nodes are expanded.
The vue.js examples of the official documentation show that you're be able to do this with the 'default-expand-all' attribute but this isn't working for me.
It only shows me the root node with an arrow, where I have to expand the children nodes manually.
<q-tree
:nodes="list"
:selected.sync="selected"
#update:selected="onSelectionChangedNode"
node-key="NodeNr"
label-key="NodeTxt"
default-expand-all
></q-tree>
Taking a cue from the accepted answer, I realised that the dom has already been created with the tree component on first render.
In my use case, I want to update the Tree when data comes back from the server.
So, I had to force it to re-render with the expanded functionality using:
this.$nextTick(function () {
this.$refs.nodes.expandAll();
})
The nextTick function will update the dom in the next window of execution, by which time the nodes will get expanded by calling the expandAll function.
And NB: For those confused by the astericks on the ref attribute or how to add it to the component, here goes:
<q-tree :nodes="list"
:selected.sync="selected"
#update:selected="onSelectionChangedNode"
node-key="NodeNr"
label-key="NodeTxt"
ref="nodes"
>
Solved my problem as following:
I have added a ref attribute to the QTree DOM Element which makes it possible to access predefined methods of QTree API.
<q-tree
:nodes="list"
:selected.sync="selected"
#update:selected="onSelectionChangedNode"
node-key="NodeNr"
label-key="NodeTxt"
**ref="nodes"**
>
The function I have been using is expandAll().
updated() {
this.$refs.nodes.expandAll();
}
The most important thing for me was, I had to find out which lifecycle hook was the right one for me. The update() hook was the one I was looking for.
The reason:
Called after a data change causes the virtual DOM to be re-rendered and
patched.
The component’s DOM will have been updated when this hook is called, so you
can perform DOM-dependent operations here.
The default-expand-all is only applied on the first rendering of that Component.
So if your Component renders when the nodes aren't assigned they wont expand if assigned afterwards.
https://v1.quasar-framework.org/vue-components/tree
You have to work with scoped slots and an expanded attribute if you dont have the nodes on first rendering.

Emitting data from a child component?

I have a child component, when data changes on this component I emit the data, I do this like so:
<input type="checkbox" value="john" v-model="users" #click="updateUsers">
When the user clicks on the checkbox, it runs the updateUsers method, which emits the this.users value.
updateUsers() {
this.$emit('users', this.users)
},
The issue is that the #click method runs before the v-model users updates. So nothing is emitted.
Am I approaching this in the wrong way? Basically I want it so that when the data changes on the child component, the parent is made aware of it and is passed the data.
I think you want to be using #change instead so you can catch the instance of the value being updated then send the emit.
When you want an updated variable to be emitted, watch the variable. Only look at the DOM when there's no viewmodel state to tell you what you need to know.
By using a change event to emit a data item, you're mingling concepts and depending on order of execution. The change event does not guarantee that the data item is updated by the time the handler fires. If you are going to emit from an event, you should emit event.target.value, not the data item which is updated separately.
If you want to emit the data item, you should be watching it for changes.
Since you're emitting changes, your component likely shouldn't own the data item, it should receive it as a prop.