I am trying to pass down a initial value to a select input bound by v-model. I cannot figure out why this doesn't work:
props: ['team'],
data() {
form: {
data: {
country: this.team.country
}
}
}
The form.data.country is undefined. Although, the props data is actually passed down. I can access it with Vue Devtools like $vm0.team.country and I can print other data from the props. However, it is not registred in the data().
Also, when trying to debug using mounted(), the property, team, is not defined.
mounted() {
console.log(this.team);
}
But, as I stated earlier, it is defined when the template is rendered, and can be used like this.
<input class="input" type="text" name="name" :value="team.name" disabled>
Why is the properties I am passing not beinged recognized in data()?
The asynchronous loading is not the problem. Even if team is hardcoded above, it's still undefined when data is created in the component. (The component needs to be created before the root Vue that passes the prop.)
There is, perhaps someone will correct me, never a good reason to reference props in your data. Something is either data (the current component knows where to find what it wants) or it's a prop (the component will let it's context supply the info).
Then, as you've discovered, your data is created once. Vue watches everything for changes, but you're vulnerable to changes of the root value. If the thing referenced as a value when data is created, changes, the reactive pipe is broken. The answer to this is the store pattern. You watch a variable in global scope, that never gets replaced, even though it's contents may change.
code
The props data was asynced loaded by the parent. This was suggested in a comment by #Bert
Related
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.
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.
I am using Inertiajs
with Laravel and also trying to use Element UI components but as i use Menu component i am having following error in console, I just used example as given in Element Ui Components as i was testing.
I see 2 different errors in there, both of them are with props.
I assume your component is taking the route as a prop, and you are also using the route as a method, which you might have put inside methods: {} which is not allowed. Make sure you rename your method route to something else.
Note: As a matter of fact you can't have any data coinciding with each other. your props, data, computed props and methods all should have unique names.
You are trying to use v-model on the props directly which won't work in Vue. if the prop is a primitive (Number, String, Boolean etc). but you can pass Object or an Array which can hold a reference to the data. This is because reactivity in Vue can't keep track of props when passed as primitives.
More on prop mutations here: https://v2.vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow
Assuming that I have this component below:
<c-attachs v-for="item in attachs" v-bind:path="item.path"></c-attachs>
And try to edit some property directly from some method, such as:
methods: {
changeProp: function ()
{
this.path = 'myNewString';
}
}
Vuejs warns on the console with the message:
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders
But... if I set "v-bind:allprops="item" and edit property directly through "allprops" object (such as code below), it works fine without error. My doubt is... Is this the correct way to edit property on events without using v-model?
this.allprops.path = 'myNewString';
There is no correct way to edit props, because you are not supposed to edit them.
Every component should have complete control over its own data. That keeps behavior easy to reason about. Items that are passed to children as props should be considered read-only, so that the owner of those items retains control. That is why Vue has events.
When something happens in a component that should affect data that the component doesn't own, the component should issue an event so that the owner of the data item can handle it. If the owner of the data item changes its value, that change will flow down through the props.
I have run into a baffling issue with reactive properties in a Vue.js app, which probably results from a misunderstanding on my part and I hope someone can help. In my App.vue, I have import auth from 'src/auth.js', and in my data properties I have this:
data() {
return {
authenticated: auth.user.authenticated,
role: auth.user.role
}
},
This authenticated property is used to show or hide various menu items, e.g:
<li v-if="!authenticated">
<router-link to="/login">Log In</router-link>
</li>
<li v-if="authenticated">Log Out</li>
What I'm finding is that changes to 'auth.user.authenticated' (e.g., switching from false to true after a successful login) are not being reflected in the rendering of the menu items - that is, the authenticated data property is not being updated. To do so, I have to explicitly update it in 'beforeUpdate' and the logout method, with this.authenticated=auth.user.authenticated. At first I thought it was simply that this was assigned on first creation and not subsequently updated and that if I used a computed property instead, this would be dynamic, but that doesn't do the job either. Clearly my App component is unaware of changes to the 'auth' data. How can I make things so that updating is automatic? I have it working at present but the use of 'beforeUpdate' feels like a kludge.
Vue data items are reactive, but non-Vue data items are not reactive. Your code initializes a reactive data item with the value from a non-reactive one.
There is no way for Vue to watch external (to Vue) variables for changes. You will need to notice when your variable changes and tell Vue about it, or just use the external variable (instead of an internal copy) when you need it.