In the VueJs 2.0 docs I can't find any hooks that would listen on props
changes.
Does VueJs have such hooks like onPropsUpdated()
or similar?
Update
As @wostex suggested, I tried to watch
my property but nothing changed. Then I realized that I've got a special case:
<template>
<child :my-prop="myProp"></child>
</template>
<script>
export default {
props: ['myProp']
}
</script>
I am passing myProp
that the parent component receives to the child
component. Then the watch: {myProp: ...}
is not working.
This question is related to
javascript
vue.js
vuejs2
vue-component
for two way binding you have to use .sync modifier
<child :myprop.sync="text"></child>
and you have to use watch property in child component to listen and update any changes
props: ['myprop'],
watch: {
myprop: function(newVal, oldVal) { // watch it
console.log('Prop changed: ', newVal, ' | was: ', oldVal)
}
}
@JoeSchr has an answer. Here is another way to do if you don't want deep: true
mounted() {
this.yourMethod();
// re-render any time a prop changes
Object.keys(this.$options.props).forEach(key => {
this.$watch(key, this.yourMethod);
});
},
You can use the watch mode to detect changes:
Do everything at atomic level. So first check if watch method itself is getting called or not by consoling something inside. Once it has been established that watch is getting called, smash it out with your business logic.
watch: {
myProp: function() {
console.log('Prop changed')
}
}
The watch function should place in Child component. Not parent.
if myProp is an object, it may not be changed in usual. so, watch will never be triggered. the reason of why myProp not be changed is that you just set some keys of myProp in most cases. the myProp itself is still the one. try to watch props of myProp, like "myProp.a",it should work.
I use props
and variables computed
properties if I need create logic after to receive the changes
export default {
name: 'getObjectDetail',
filters: {},
components: {},
props: {
objectDetail: {
type: Object,
required: true
}
},
computed: {
_objectDetail: {
let value = false
...
if (someValidation)
...
}
}
Have you tried this ?
watch: {
myProp: {
// the callback will be called immediately after the start of the observation
immediate: true,
handler (val, oldVal) {
// do your stuff
}
}
}
For me this is a polite solution to get one specific prop(s) changes and create logic with it
I would use props
and variables computed
properties to create logic after to receive the changes
export default {
name: 'getObjectDetail',
filters: {},
components: {},
props: {
objectDetail: { // <--- we could access to this value with this.objectDetail
type: Object,
required: true
}
},
computed: {
_objectDetail: {
let value = false
// ...
// if || do || while -- whatever logic
// insert validation logic with this.objectDetail (prop value)
value = true
// ...
return value
}
}
So, we could use _objectDetail on html render
<span>
{{ _objectDetail }}
</span>
or in some method:
literallySomeMethod: function() {
if (this._objectDetail) {
....
}
}
He,
in my case I needed a solution where anytime any props would change, I needed to parse my data again. I was tired of making seperated watcher for all my props, so I used this:
watch: {
$props: {
handler() {
this.parseData();
},
deep: true,
immediate: true,
},
Key point to take away from this example is to use deep: true
so it not only watches $props but also it's nested values like e.g. props.myProp
You can learn more about this extended watch options here: https://vuejs.org/v2/api/#vm-watch
Not sure if you have resolved it (and if I understand correctly), but here's my idea:
If parent receives myProp, and you want it to pass to child and watch it in child, then parent has to have copy of myProp (not reference).
Try this:
new Vue({
el: '#app',
data: {
text: 'Hello'
},
components: {
'parent': {
props: ['myProp'],
computed: {
myInnerProp() { return myProp.clone(); } //eg. myProp.slice() for array
}
},
'child': {
props: ['myProp'],
watch: {
myProp(val, oldval) { now val will differ from oldval }
}
}
}
}
and in html:
<child :my-prop="myInnerProp"></child>
actually you have to be very careful when working on complex collections in such situations (passing down few times)
You need to understand, the component hierarchy you are having and how you are passing props, definitely your case is special and not usually encountered by the devs.
Parent Component -myProp-> Child Component -myProp-> Grandchild Component
If myProp is changed in parent component it will be reflected in the child component too.
And if myProp is changed in child component it will be reflected in grandchild component too.
So if myProp is changed in parent component then it will be reflected in grandchild component. (so far so good).
Therefore down the hierarchy you don't have to do anything props will be inherently reactive.
Now talking about going up in hierarchy
If myProp is changed in grandChild component it won't be reflected in the child component. You have to use .sync modifier in child and emit event from the grandChild component.
If myProp is changed in child component it won't be reflected in the parent component. You have to use .sync modifier in parent and emit event from the child component.
If myProp is changed in grandChild component it won't be reflected in the parent component (obviously). You have to use .sync modifier child and emit event from the grandchild component, then watch the prop in child component and emit an event on change which is being listened by parent component using .sync modifier.
Let's see some code to avoid confusion
Parent.vue
<template>
<div>
<child :myProp.sync="myProp"></child>
<input v-model="myProp"/>
<p>{{myProp}}</p>
</div>
</template>
<script>
import child from './Child.vue'
export default{
data(){
return{
myProp:"hello"
}
},
components:{
child
}
}
</script>
<style scoped>
</style>
Child.vue
<template>
<div> <grand-child :myProp.sync="myProp"></grand-child>
<p>{{myProp}}</p>
</div>
</template>
<script>
import grandChild from './Grandchild.vue'
export default{
components:{
grandChild
},
props:['myProp'],
watch:{
'myProp'(){
this.$emit('update:myProp',this.myProp)
}
}
}
</script>
<style>
</style>
Grandchild.vue
<template>
<div><p>{{myProp}}</p>
<input v-model="myProp" @input="changed"/>
</div>
</template>
<script>
export default{
props:['myProp'],
methods:{
changed(event){
this.$emit('update:myProp',this.myProp)
}
}
}
</script>
<style>
</style>
But after this you wont help notice the screaming warnings of vue saying
'Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders.'
Again as I mentioned earlier most of the devs don't encounter this issue, because it's an anti pattern. That's why you get this warning.
But in order to solve your issue (according to your design). I believe you have to do the above work around(hack to be honest). I still recommend you should rethink your design and make is less prone to bugs.
I hope it helps.
I work with a computed property like:
items:{
get(){
return this.resources;
},
set(v){
this.$emit("update:resources", v)
}
},
Resources is in this case a property:
props: [ 'resources' ]
Source: Stackoverflow.com