Vue props data not updating in child component - javascript

Hi everyone I just want some explanation about vue props data. So I'm passing value from parent component to child component. The thing is when parent data has data changes/update it's not updating in child component.
Vue.component('child-component', {
template: '<div class="child">{{val}}</div>',
props: ['testData'],
data: function () {
return {
val: this.testData
}
}
});
But using the props name {{testdata}} it's displaying the data from parent properly
Vue.component('child-component', {
template: '<div class="child">{{testData}}</div>',
props: ['testData'],
data: function () {
return {
val: this.testData
}
}
});
Thanks in advance
Fiddle link

This is best explained with a very simple example
let a = 'foo'
let b = a
a = 'bar'
console.info('a', a)
console.info('b', b)
When you assign...
val: this.testData
you're setting the initial value of val once when the component is created. Changes to the prop will not be reflected in val in the same way that changes to a above are not reflected in b.
See https://v2.vuejs.org/v2/guide/components.html#One-Way-Data-Flow

I resolve with! this.$set(this.mesas, i, event);
data() {
return { mesas: [] }
},
components: {
'table-item': table_l,
},
methods: {
deleteMesa: function(event) {
let i = this.mesas.map(item => item.id).indexOf(event.id);
console.log("mesa a borrare", i);
this.mesas.splice(i, 1);
},
updateMesa: function(event) {
let i =this.mesas.map(item => item.id).indexOf(event.id);
console.log("mesa a actualizar", i);
/// With this method Vue say Warn
//this.mesas[i]=event;
/// i Resolve as follow
this.$set(this.mesas, i, event);
},
// Adds Useres online
addMesa: function(me) {
console.log(me);
this.mesas.push(me);
}
}

Related

petite-vue directive where ctx.get() is array is not reactive

I need to create a custom 'chart' directive where the directive expression is an 'array'. When I update the array, no reactivity occurs. Is this possible in petite-vue? I see some caveats in Vue proper and using Vue.set() but with petite-vue I don't have access to that.
Javascript
class KatApp {
state;
constructor() {
this.state = PetiteVue.reactive({ chartData: [] });
const vue = PetiteVue.createApp(this.state);
vue.directive("ka-chart", ctx => {
let cnt = 1;
ctx.effect(() => {
ctx.el.innerHTML = "Rebuild chart: " + cnt;
cnt++;
});
});
vue.mount(".myApp");
}
calculate() {
// This is method that would be called when UI button is clicked...
this.state.chartData = [ { "#id": "1", value: 1 }, { "#id": "2", value: 2 } ];
}
}
Markup
<div v-scope class="myApp">
<div v-ka-chart="{ data: chartData }"></div>
</div>
When application.calculate() is called and the chartData is updated, my ctx.effect code does not fire.

vuetify expansion panel does not update with data change

I have a bunch of expansion panels that are rendered from a list. This list has 60+ items in it, so I have set up a pagination to display 5 results at a time. The problem is that the expansion panels do not correctly update with the new lists. I Know that I am returning a new list.
{{this.viewFilteredTools}}
<success-factors v-for="(x,index) in this.viewFilteredTools" :key="index" :factor="x" :tool="tool" />
{{x}}
</v-expansion-panels>
<v-pagination
v-model="page"
:length="this.paginationLength/5"></v-pagination>
This is just holding the expansion panels which are coming in through <success-factors/>.
<script>
import SuccessFactors from './SuccessFactors.vue';
export default {
components: { SuccessFactors },
props:{
tool:{
type: Object,
required: false,
}
},
data() {
return {
page:1,
paginationLength: 0,
factors: {},
factorsList: [],
filterByItems: [
{text:'A to Z (Complete Reviews First)',value:'ascending'},
{text:'Z to A (Complete Reviews First)',value:'descending'},
{text:'A to Z (all)',value:'allAscending'},
{text:'Z to A (all)',value:'allDescending'}
],
filterValue: {text:'Z to A (all)',value:'allDescending'},
viewFilteredTools:[]
};
},
mounted() {
console.log('something happens here')
this.factorsList = this.tool.factors;
this.paginateResults(1,this.factorsList)
this.paginationLength = this.factorsList.length
},
watch: {
page(oldPage){
this.paginateResults(oldPage,this.factorsList);
}
},
// computed:
async fetch() {
const { $content} = this.$nuxt.context;
this.factors = (await $content(this.tool['factors']).fetch());
this.factorsList = this.tool.factors;
},
methods: {
sortList(lstValue) {
console.log(this.tool.factors);
let sortFactors = this.tool.factors;
sortFactors = sortFactors.sort((a,b) => {
if(a<b){
return -1;
}
if(a>b){
return 1;
}
return 0;
})
this.factors = sortFactors;
},
paginateResults(page,results){
console.log(results)
const startCount = (page-1)*5;
const endCount = startCount + 5;
this.startCount = startCount+1;
this.endCount = endCount;
console.log(results.slice(startCount,endCount))
this.viewFilteredTools = results.slice(startCount,endCount);
}
}
};
</script>
this.viewFilteredTools is created from this.factorsList inside the mount lifecycle. Then any time a new page is chosen it is updated from the method sortList(). I can see the viewFilteredTools does change with every page change due to printing it out above the <success-factors>. However, the <success-factors> does not take the data from the updated lists.
Your best bet is to use a computed property. Vue knows when a computed property is updated & would re-render when that property updates.

Vue.js - watch particular properties of the object and load data on change

I have Vue component with prop named product, it is an object with a bunch of properties. And it changes often.
export default {
props: {
product: {
type: Object,
default: () => {},
},
},
watch: {
'product.p1'() {
this.loadData()
},
'product.p2'() {
this.loadData()
},
},
methods: {
loadData() {
doApiRequest(this.product.p1, this.product.p2)
}
},
}
The component should load new data when only properties p1 and p2 of product are changed.
The one approach is to watch the whole product and load data when it is changed. But it produces unnecessary requests because p1 and p2 may not have changed.
Another idea is to watch product.p1 and product.p2, and call the same function to load data in each watcher.
But it may happen that both p1 and p2 changed in the new version of the product, it would trigger 2 calls.
Will it be a good solution to use a debounced function for data load?
Or rather use single watcher for the whole product and compare new p1 and p2 stringified with their old stringified versions to determine if data loading should be triggered?
There are several approaches to this, each with pros and cons.
One simple approach I do is to use a watch function that accesses each of the properties you want to watch and then returns a new empty object. Vue knows product.p1 and product.p2 were accessed in the watch function, so it will re-execute it any time either of those properties change. Then, by returning a new empty object instance from the watch function, Vue will trigger the watch handler because the watch function returned a new value (and thus what is being watched "changed").
created() {
this.$watch(() => {
// Touch the properties we want to watch
this.product.p1;
this.product.p2;
// Return a new value so Vue calls the handler when
// this function is re-executed
return {};
}, () => {
// p1 or p2 changed
})
}
Pros:
You don't have to stringify anything.
You don't have to debounce the watch handler function.
Cons:
You can't track the previous values of p1 and p2.
Take care if this.product could ever be null/undefined.
It will always trigger when p1 or p2 are changed; even if p1 and p2 are set back to their previous values before the next micro task (i.e. $nextTick()); but this is unlikely to be a problem in most cases.
You need to use this.$watch(). If you want to use the watch option instead then you need to watch a computed property.
Some of these cons apply to other approaches anyway.
A more compact version would be:
this.$watch(
() => (this.product.p1, this.product.p2, {}),
() => {
// changed
}
})
As some of other developers said you can use computed properties to monitor the changing of product.p1 or product.p2 or both of them and then calling loadData() method only once in each case. Here is the code of a hypothetical product.vue component:
<template>
<div>
this is product compo
</div>
</template>
<script>
export default {
name: "product",
watch: {
p1p2: function(newVal, oldVal) {
this.loadData();
}
},
props: {
productProp: {
type: Object,
default: () => {},
},
},
computed: {
p1p2: function() {
return this.productProp.p1 + this.productProp.p2;
}
},
methods: {
loadData() {
console.log("load data method");
}
},
}
</script>
I renamed the prop that it received to productProp and watched for a computed property called p1p2 in that. I supposed that the values of data are in String format (but if they are not you could convert them). Actually p1p2 is the concatenation of productProp.p1 and productProp.p2. So changing one or both of them could fire the loadData() method. Here is the code of a parent component that passes data to product.vue:
<template>
<section>
<product :productProp = "dataObj"></product>
<div class="d-flex justify-space-between mt-4">
<v-btn #click="changeP1()">change p1</v-btn>
<v-btn #click="changeP2()">change p2</v-btn>
<v-btn #click="changeBoth()">change both</v-btn>
<v-btn #click="changeOthers()">change others</v-btn>
</div>
</section>
</template>
<script>
import product from "../components/product";
export default {
name: 'parentCompo',
data () {
return {
dataObj: {
p1: "name1",
p2: "name2",
p3: "name3",
p4: "name4"
}
}
},
components: {
product
},
methods: {
changeP1: function() {
if (this.dataObj.p1 == "name1") {
this.dataObj.p1 = "product1"
} else {
this.dataObj.p1 = "name1"
}
},
changeP2: function() {
if (this.dataObj.p2 == "name2") {
this.dataObj.p2 = "product2"
} else {
this.dataObj.p2 = "name2"
}
},
changeBoth: function() {
if (this.dataObj.p2 == "name2") {
this.dataObj.p2 = "product2"
} else {
this.dataObj.p2 = "name2"
}
if (this.dataObj.p1 == "name1") {
this.dataObj.p1 = "product1"
} else {
this.dataObj.p1 = "name1"
}
},
changeOthers: function() {
if (this.dataObj.p3 == "name3") {
this.dataObj.p3 = "product3"
} else {
this.dataObj.p3 = "name3"
}
}
},
}
</script>
You can test the change buttons to see that by changing dataObj.p1 or dataObj.p2 or both of them the loadData() method only called once and by changing others it is not called.
for you do ontouch event in vuejs while using it with your HTML inline
and you have an object model that house you other variable and you need to validate the any of the variable you will need to put them in watcher and use qoute ('') to tell vuejs that this is from a the model.email
hope this is useful
data() {
return {
model: {},
error_message: [],
}
},
watch: {
'model.EmailAddress'(value) {
// binding this to the data value in the email input
this.model.EmailAddress = value;
this.validateEmail(value);
},
},
methods: {
validateEmail(value) {
if (/^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(value)) {
this.error_message['EmailAddress'] = '';
} else {
this.error_message['EmailAddress'] = 'Invalid Email Address';
}
}
},

Vue computed setter with object prop

The idea is to pass different objects to an input component, have them displayed there as CSV and be able to edit/validate the text and change the style depending on the validation results. Here is the code I currently have:
<div id="vue">
<base-input :cslice="c.workspace"></base-input>
</div>
javascript:
(function () {
Vue.component('base-input', {
props: ['cslice'],
data: function () {
return {
colors: ['red', 'white', 'yellow', 'green', 'orange', 'purple'],
ind: 1
}
},
computed: {
str: {
get: function () {
return Object.values(this.cslice).join(", ");
},
set: function (val) {
if(val.indexOf('0'))
this.ind = Math.floor(this.colors.length * Math.random());
},
},
styleObj: {
get: function () {
return { color: this.colors[this.ind] };
},
set: function () {
},
}
},
template: '<div><input v-model="str" :style="styleObj" type="text"/></div>'
});
let vue = new Vue({
el: '#vue',
data: {
c: {},
},
created: function () {
this.c = Object.assign({}, this.c, {
workspace: { width: 820, height: 440 },
});
},
});
})();
Here is the fiddle:
https://jsfiddle.net/tfoller/sz946qe2/3/
This code allows me to delete the last character only if the new style is the same as the current, otherwise the text is practically uneditable, how do I fix this problem so I'm able to normally edit the input field?
Since the computed in the child uses a prop in its getter, you need to $emit back up a value with the same shape in its setter:
set: function (val) {
const arrValues = val.split(',').map(v => v.trim());
console.log(arrValues);
this.$emit('input', {
width: arrValues[0],
height: arrValues[1]
})
},
That means reversing some of the string formatting stuff you were doing in order to get the right shape.
Listen for that input event in the parent. You can change your prop name to value so that you can use v-model as the listener:
<base-input v-model="c.workspace"></base-input>
Move all of the color changing functionality into a separate method in the child that's triggered by changing the input as well. Here's a demo:
Demo

How to check when data changes in component by itself with vue js?

I have method that changes data in itself, simple example:
Vue.component('component', {
template: '#component',
data: function () {
return {
dataToBeWatched: ''
}
},
methods: {
change: function (e) {
var that = this;
setTimeOut(function() {
that.dataToBeWatched = 'data changed';
}, 2000);
},
makeSmthWhenDataChanged: function () {
// ajax request when dataToBeWatched changed or when dataToBeWatched isn't empty
}
}
});
How to create such watcher using correct methods vue js?
Or I need to use props watching it in component?
Vue components can have a watch property which is an object. The object keys need to be the name of the prop or data that needs to be watched, and the value is a function that is invoked when the data changes.
https://v2.vuejs.org/v2/guide/computed.html#Computed-vs-Watched-Property
Vue.component('component', {
template: '#component',
data: function () {
return {
dataToBeWatched: ''
}
},
methods: {
change: function (e) {
var that = this;
setTimeOut(function() {
that.dataToBeWatched = 'data changed';
}, 2000);
},
makeSmthWhenDataChanged: function () {
// ajax request when dataToBeWatched changed or when dataToBeWatched isn't empty
}
},
watch: {
dataToBeWatched: function(val) {
//do something when the data changes.
if (val) {
this.makeSmthWhenDataChanged();
}
}
}
});

Categories

Resources