Which method can I use which will be called every time a prop on a component changes - vue.js

I am new to Vue. I am passing props to a child component and when the props change, I am using watch to catch the changes and assign them to this.query. I am able to log this change in watch and it is working. The prop value is changing on the page but mounted is not being called again, it is only being called once when the component is rendered for the first time,. I want to be able to do a fetch each time these props change.
I have tried to play around with updated and beforeUpdate but I am unable to log the props there and so don't know where I can fetch the data every time they change.
<template>
<div class="movie-list">
<h1>{{ query }}</h1>
//At the moment I am just rendering the query which is changing as the prop changes but I want to be able to render movies incrementally from a fetch in mounted() as this.query changes
</div>
</template>
<script>
export default {
props: ['query'],
name: 'SearchedMovies',
data: function () {
return {
movies: []
}
},
watch: {
query: function(newVal){
this.query = newVal
console.log('query in watch', this.query);
//this is logging each time it changes
}
},
updated () {
console.log('query in updated', this.query);
//this is not logging
},
beforeUpdate () {
console.log('query in beforeUpdate', this.query);
//this is not logging
},
mounted () {
console.log('this is the query in SearchedMovies', this.query);
//this is logging once when the component first renders
//I want to be able to do a fetch here each time this.query changes and save the results to this.movies.
}
}
</script>

The watch will be called any time the query prop changes. You are assigning the new value — which is already in the query prop — back to the query prop, which is a bad idea, because you should not modify props.
Just put your fetch inside the watch callback.

Related

vuejs3 updated life cycle hook not being called when state is changed

I'm learning Vue.js coming from React. I'm confused about the updated life cycle hook. no matter what state I change I can't get it to run. I have a console.log inside the updated hook and I've set a setTimeout in the mounted hook. the setTimeout callback runs and changes the state which changes what is displayed on the DOM although I don't ever see the console.log from the updated hook. I'm not sure why the update is not firing
NOTE: I'm using Vue.js 3.
<template>
...other components
</template>
<script>
export default {
name: 'App',
data: () => ({
state: 'initial'
}),
updated () {
console.log('UPDATED!!!')
},
mounted () {
setInterval(() => {
this.state = 'changed'
console.log('changing')
}, 1000)
}
}
</script>
The updated lifecycle hook only runs when the template has been updated (was re-rendered), which would only occur when a property used in the template has changed values.
Since state was modified, and the updated hook did not run, that would imply that the template is not using state. Also note repeatedly setting state to the same value would only trigger the hook once for the new value (assuming the template needed state).
For example, the following snippet would trigger the updated hook periodically, as the timer callback toggles state, which is rendered in the template:
<template>
<div>{{ state }}</div>
</template>
<script>
export default {
name: 'App',
data: () => ({
state: 'initial'
}),
updated () {
console.log('UPDATED!!!')
},
unmounted () {
clearInterval(this._timerId)
},
mounted () {
this._timerId = setInterval(() => {
this.state = this.state === 'changed' ? 'initial' : 'changed'
console.log('changing')
}, 1000)
}
}
</script>
demo
That said, there's usually a better way to do things in Vue instead of the updated hook (e.g., using a watch on a specific prop), but that depends on your actual use case, which isn't clear in the question.

Is there any way to check the status of props when passing data from parent to child component

I have been troubled by a question for a long time. Now I am using Vue.js to develop a web project. What I want to do is to pass data from parent to child component. However, the child component's main program would run only after the props data was received, due to the async data transmission mechanism. So I would like to know whether these are some ways to check the status of props data in the child component. Therefore I can make sure the subsequent task would run after the data was passed.
For example, a feasible solution is axios.requset({..}).then(res => {..}).
You can use the watchers in your child component. Consider the following parent component:
Vue.component('parent-comp', {
props: ['myProp'],
template: `
<div>
<child-comp my-prop={someAsyncProp} />
</div>
`,
data() {
return {
// Declare async value
someAsyncProp: null
};
},
mounted() {
// Some async computation
axios
.requset({ url: '/get-data' })
.then(res => {
// Set value asynchronously
this.someAsyncProp = res;
});
}
});
Your child component would use watchers to check if data is available:
Vue.component('child-comp', {
props: ['myProp'],
template: '<div></div>',
watch: {
// Watch the property myProp
myProp(newValue, oldValue) {
if (newValue !== null) {
// Do something with the props that are set asynchronously by parent
}
}
}
})

Call API automatically to fetch data with prop value when component is displayed in Vue.js

I have a page which displays a list of mutual funds. With each mutual fund, I have a button to display their NAV history. This button calls a component which has an embedded API call to fetch the NAV history. I pass the fund code for which the data is to be fetched as a prop to the component. However, I am not able to trigger the API call automatically when the prop is called.
this is my code as of now:
Parent component (main page):
<template>
<!-- some code -->
<a href="#" #click="fetchNavHistory(fund)">
<v-icon small>history</v-icon>
</a>
<!-- some more code -->
<NAVHistory
:show="showNavHistory"
:amfi_code="amfi_code"
ref="history"
#hide="showNavHistory=false"
/>
</template>
export default {
name: "FundList",
components: {
NAVHistory
},
data() {
return {
showNavHistory: false,
amfi_code: 0
}
},
methods:{
fetchNavHistory(fund){
this.amfi_code = fund.amfi_code
this.showNavHistory = true
var child = this.$refs.history
child.fetchNavHistory()
}
}
}
Child component (where NAV history is displayed):
<template>
<!-- some code -->
</template>
<script>
export default {
props: {
show: Boolean,
amfi_code: Number
},
data(){
return{
apiURL: process.env.VUE_APP_BASEURL,
navHistory: [],
}
},
methods: {
async fetchNavHistory(){
try{
const response = await fetch(this.apiURL + '/navhistory', {
method: 'POST',
body: JSON.stringify({"amfi_code": this.amfi_code}),
headers: {'content-type': 'application/json; charset=UTF-8'},
})
const data = await response.json()
console.log(data)
this.navHistory = data
} catch(error){
console.log(error)
}
}
}
}
</script>
At first I tried calling the fetchNavHistory() method on updated() event. But that kept calling the API non-stop when the component was displayed on the screen.
Then I tried adding a watch for the show prop. But that didn't work at all.
Finally, as a workaround, I called the API from the parent component itself. While that is working, it is calling the component with the previous value of the amfi_code, rather than the updated value. So the first time it gets called, the amfi_code is passed as 0.
Is there a way to safely trigger the API call when the component is displayed, i.e., the show prop is set to true?
You can try watch with deep:true option that way the watch will be triggered when a component will be mounted. Or you can call API on mounted hook and check show prop in it.
deep:true means a watch will look at if changes occur not only for a watched prop but additionally at all nested props.
immediate:true means that a watch will fire after a component is mounted (when a watched prop has initial value).

(VueJS) Update component whenever displayed

I want a way to run a function (which talks to the backend) whenever a component is re-displayed.
I understand that the mounted hook will fire if the component is re-added to the DOM by a v-if directive. But, if the component is hidden and re-shown via a v-show directive, this will not fire. I need to update the component regardless of what directive is in control of it's visibility.
I looked at the updated hook but this seems to not be the indented use case.
How do I run a function whenever a component is displayed (not only for the first time)?
updated fires whenever data passed to your component changes. Therefore it will work if you pass in whatever condition controls your v-show, as a prop.
Generic example:
Vue.config.devtools = false;
Vue.config.productionTip = false;
Vue.component('child', {
props: {
shown: {
type: Boolean,
default: true
}
},
template: '<div>{{shown}}</div>',
mounted() {
console.log('child mounted');
},
updated() {
// runs whenever any prop changes
// (optional condition) only run when component is shown
if (this.shown) {
console.log('child updated');
}
}
});
new Vue({
el: '#app',
data: () => ({
showChild: true
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<label><input type="checkbox" v-model="showChild" /> Show child</label>
<child v-show="showChild" :shown="showChild" />
</div>
Now updated hook works properly, because it fires everytime :shown changes its value, which maps precisely on your show/hide logic.
maybe you can achieve it in two ways
1.use :key
whenever you want to rerender your component whether it is shown, change the value of key can rerender it.
<template>
<h1 :key="key">Text</h1>
</template>
<script>
export default{
data(){
return {
key:this.getRandomString()
}
},
methods(){
getRandomString(length = 32) {
  let chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
  let max_pos = chars.length;
  let random_string = '';
  for (let i = 0; i < length; i++) {
    random_string += chars.charAt(Math.floor(Math.random() * max_pos));
  }
  return random_string;
},
yourMethod(){
// communicate with backend
let data = await axios.get(...);
this.key = this.getRandomString();
}
}
}
</script>
use vm.$forceUpdate()
...
yourMethod(){
// communicate with backend
let data = await axios.get(...);
this.$forceUpdate();
}
...
you could implement this in a couple of ways. However since you would like to got the v-show way, here is how I would suggest you go about it.
v-show (v-show, watcher):
The v-show is definitely dependent on a variable (data, or computed). Create a watcher, to watch that data/computed property change. Depending on the value of the data/computed property, execute whatever function you intend to on the watcher.

How to send updated values from Parent component to child component in Vue JS?

I am passing a variable from parent component to child component through props. But with some operation, the value of that variable is getting changed i.e. on click of some button in parent component but I did not know how to pass that updated value to child? suppose the value of one variable is false initially and there is Edit button in parent component. i am changing the value of this variable on click of Edit button and want to pass the updated value from parent to child component.
Your property's value should be updated dynamically when using props between parent and child components. Based on your example and the initial state of the property being false, it's possible that the value was not properly passed into the child component. Please confirm that your syntax is correct. You can check here for reference.
However, if you want to perform a set of actions anytime the property's value changes, then you can use a watcher.
EDIT:
Here's an example using both props and watchers:
HTML
<div id="app">
<child-component :title="name"></child-component>
</div>
JavaScript
Vue.component('child-component', {
props: ['title'],
watch: {
// This would be called anytime the value of title changes
title(newValue, oldValue) {
// you can do anything here with the new value or old/previous value
}
}
});
var app = new Vue({
el: '#app',
data: {
name: 'Bob'
},
created() {
// changing the value after a period of time would propagate to the child
setTimeout(() => { this.name = 'John' }, 2000);
},
watch: {
// You can also set up a watcher for name here if you like
name() { ... }
}
});
You can watch a (props) variable with the vue watch.
for example:
<script>
export default {
props: ['chatrooms', 'newmessage'],
watch : {
newmessage : function (value) {...}
},
created() {
...
}
}
</script>
I hope this will solve your problem. :)
Properties, where the value is an object, can be especially tricky. If you change an attribute in that object, the state is not changed. Thus, the child component doesn't get updated.
Check this example:
// ParentComponent.vue
<template>
<div>
<child-component :some-prop="anObject" />
<button type="button" #click="setObjectAttribute">Click me</button>
</div>
</template>
<script>
export default {
data() {
return {
anObject: {},
};
},
methods: {
setObjectAttribute() {
this.anObject.attribute = 'someValue';
},
},
};
</script>
// ChildComponent.vue
<template>
<div>
<strong>Attribute value is:</strong>
{{ someProp.attribute ? someProp.attribute : '(empty)' }}
</div>
</template>
<script>
export default {
props: [
'someProp',
],
};
</script>
When the user clicks on the "Click me" button, the local object is updated. However, since the object itself is the same -- only its attribute was changed -- a state change is not dispatched.
To fix that, the setObjectAttribute could be changed this way:
setObjectAttribute() {
// using ES6's spread operator
this.anObject = { ...this.anObject, attribute: 'someValue' };
// -- OR --
// using Object.assign
this.anObject = Object.assign({}, this.anObject, { attribute: 'someValue' });
}
By doing this, the anObject data attribute is receiving a new object reference. Then, the state is changed and the child component will receive that event.
You can use Dynamic Props.
This will pass data dynamically from the parent to the child component as you want.