Why Vue can't watch the data change? - javascript

i want the function:createComparation work when tableRowName change,but acturely when tableRowName change , it didnt works,vue follows;
createComparation is another function which didnt define by vue but only by javascript
const selectedRight =Vue.createApp({
data(){
return {
tableRow:0,
tableRowName:[],
stockName:[],
rectWidth:40,
rectHeight:5,
}
},
watch: {
tableRowName(newtable,oldtable){
console.log(1)
createComparation()
},
immediate:true,
stockName(){
changeTip()
},
},
methods:{
}
}).mount('#selectedRight')

in case of tableRowName contain objects then you have to use
deep:true
watch: {
tableRowName(newtable,oldtable){
console.log(1)
createComparation()
},
immediate:true,
deep: true,
stockName(){
changeTip()
},
},
but i think you are updating the array without reactive manner, Vue cannot detect the following changes to an array:
When you directly set an item with the index, e.g.
vm.items[indexOfItem] = newValue
When you modify the length of the array, e.g.
vm.items.length = newLength
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // is NOT reactive
vm.items.length = 2 // is NOT reactive

I guess whatching array might be the issue. You can try this:
computed: {
rowNames() {
return this.tableRowName;
// if the line above doesn't work:
return this.tableRowName.join();
}
},
watch: {
rowNames(newtable,oldtable){
createComparation()
},

I think this is what you're looking for. You need to define the handler as an object for the property you're trying to watch and set immediate: true.
Vue.config.productionTip = false
Vue.config.devtools = false
new Vue({
el: "#app",
data() {
return {
tableRow: 0,
tableRowName: [],
stockName: [],
rectWidth: 40,
rectHeight: 5,
}
},
watch: {
tableRowName: {
handler(newtable) {
console.log('Calling createComparation function');
console.info(newtable);
},
immediate: true
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="tableRowName.push(Math.random())">Trigger change manually again</button>
</div>

The watch method definition is wrong. When you need to use immediate, you have to put you function body into handler property.
For example,
watch: {
tableRowName: {
handler: function() {
},
immediate: true
}
},

Related

Vue data variable to methods and then to provide

I have issue to get some values from methods and want to parse to provide.
How I can solve the problem?
methods: {
onClickCategory: (value) => {
return (this.catId = value);
},
},
provide() {
return {
categoryId: this.value,
};
},
I get always categoryId:undefined
I found solution:
methods: {
onClickCategory(value) {
this.categoryId.value = value;
},
},
provide() {
this.catID = this.categoryId;
return {
catId: this.catID,
};
},
As Vue Guide highlights,
Note: the provide and inject bindings are NOT reactive. This is
intentional. However, if you pass down an observed object, properties
on that object do remain reactive.
So one solution is wrap your value into one observed object, like test2.value in below example:
Vue.config.productionTip = false
Vue.component('v-parent', {template: `
<div>
<h4>Example</h4>
<p>Not Working: <input v-model="test1"></p>
<p>Working: <input v-model="test2.value"></p>
<v-child></v-child>
</div>
`,
data () {
return {
test1: 'blabla1',
test2: {value: 'blabla2'}
}
},
provide () {
return {parent1: this.test1, parent2: this.test2}
}
}),
Vue.component('v-child', {
template: `<div><pre>{{parent1}}</pre><pre>{{parent2.value}}</pre></div>`,
inject: ['parent1', 'parent2']
})
new Vue({
el: '#app',
data() {
return {
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<div>
<v-parent/>
</div>
</div>

vue escaping object child as parameter

I want to build a helper function to reduce my redundant code lines.
Instead of doing nearly the same over and over i want to use that function to simple add a parameter and reduce the lines of code.
<template>
<div>
<!-- TODO: ADD ALL PROPS NOW! -->
<UserInfo
:user-name="userData.userName"
:budget="userData.budget"
:leftover="userData.leftover"
/>
<UserIncExp />
</div>
</template>
<script>
import UserInfo from '../User/UserInfo.vue';
import UserIncExp from '../User/UserIncExp/_UserIncExp.vue';
export default {
components: {
UserInfo,
UserIncExp
},
data() {
return {
test: '',
userData: {
userName: '',
budget: '',
leftover: '',
inc: [],
exp: [],
active: false
}
};
},
computed: {},
watch: {
'$route.params.userId': {
handler() {
this.loadUserDataFromState();
}
},
immediate: true
},
created() {
this.loadUserDataFromState();
},
methods: {
loadUserDataFromState() {
// this.userData.userName = this.$store.state.users[this.$attrs.userId].userName;
// this.userData.budget = this.$store.state.users[this.$attrs.userId].salery;
// this.userData.leftover = this.$store.state.users[this.$attrs.userId].leftover;
this.helper(userName);
},
// CHECK HERE <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
helper(data) {
return this.userData.data = this.$store.state.users[this.$attrs.userId].data;
}
}
};
</script>
<style>
</style>
but i dont get why i am not able to use data as an parameter to use it then in the executed function
Use bracket notation like so:
loadUserDataFromState() {
this.helper("userName");
},
helper(key) {
this.userData[key] = this.$store.state.users[this.$attrs.userId][key];
}
helper expects a key (which is a string).
Note: helper doesn't need to return anything.
An alternative way which doesn't require the function helper at all is to use a loop over an array of keys like so:
loadUserDataFromState() {
for(let key of ["userName", "salery", "leftover"]) {
this.userData[key] = this.$store.state.users[this.$attrs.userId][key];
}
}
it just loops over the keys in the array ["userName", "salery", "leftover"] and dynamically copy the values from the source object to this.userData.

Vue v-for on first item containing property X call a method

Let's say I have a component which repeats with a v-for loop like so:
<hotel v-for="(hotel, index) in hotels"></hotel>
And my hotels array would look like so:
[
{
name: false
},
{
name: false
},
{
name: true
},
{
name: true
}
]
How could I perform an action when my v-for loop encounters the property name set to true only on the very first time it encounters this truthy property?
I know I could probably cache a property somewhere and only run something once and not run again if it has been set but this does not feel efficient.
Use a computed to make a copy of hotels where the first one has an isFirst property.
computed: {
hotelsWithFirstMarked() {
var result = this.hotels.slice();
var first = result.find(x => x.name);
if (first) {
first.isFirst = true;
}
return result;
}
}
Just use computed source
HTML
<div v-for="(hotel, index) in renderHotels"></div>
JS
export default {
name: 'message',
data () {
return{
hotels:[
{
name: false
},
{
name: false
},
{
name: true
},
{
name: true
}
] ,
wasFirst : false
}
},
methods:{
},
computed:{
renderHotels(){
return this.hotels.map((hotel)=>{
if(hotel.name && !this.wasFirst){
this.wasFirst = true;
alert('First True');
console.log(hotel);
}
})
}
}
}
Best way is to use filter function.
data() {
return {
firstTime: false,
};
},
filters: {
myFunc: function (hotel) {
if (hotel.name && !this.firstTime) {
//perform some action
this.firstTime = true;
}
}
}
<hotel v-for="(hotel, index) in hotels | myFunc"></hotel>

How to watch only one object in an array?

I have an array:
basicForm.schema = [
{},
{} // I want to watch only this
]
I tried doing this:
‘basicForm.schema[1].value’: {
handler (schema) {
const plan = schema.find(field => {
return field.name === ‘plan’
})
},
deep: true
},
But I got this error:
vue.js?3de6:573 [Vue warn]: Failed watching path:
“basicForm.schema[1]” Watcher only accepts simple dot-delimited paths.
For full control, use a function instead.
What's the correct way of doing this?
You can watch a computed property instead:
new Vue({
el: '#app',
data: {
basicForm: {
schema: [
{a: 1},{b: 2} // I want to watch only this
]
}
},
computed: {
bToWatch: function() {
return this.basicForm.schema[1].b
}
},
methods: {
incB: function() {
this.basicForm.schema[1].b++
}
},
watch: {
bToWatch: function(newVal, oldVal) {
console.log(newVal)
}
}
});
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<button #click="incB()">Inc</button>
</div>
You should use a function as the warning message suggests. You need to do so via vm.$watch.
new Vue({
el: '#app',
data: {
items: [
{ name: 'bob' },
{ name: 'fred' },
{ name: 'sue' },
],
},
created() {
this.$watch(() => this.items[1].name, this.onNameChanged);
},
methods: {
changeValue() {
this.items[1].name = 'rose';
},
onNameChanged(name) {
alert('name changed to ' + name);
},
},
});
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<button #click="changeValue">Click me</button>
</div>
You should probably check that this.items[1] exists before accessing it inside the watch function otherwise you'll get an error.

Update data property / object in vue.js

is there a way I can programmatically update the data object / property in vue.js? For example, when my component loads, my data object is:
data: function () {
return {
cars: true,
}
}
And after an event is triggered, I want the data object to look like:
data: function () {
return {
cars: true,
planes: true
}
}
I tried:
<script>
module.exports = {
data: function () {
return {
cars: true
}
},
methods: {
click_me: function () {
this.set(this.planes, true);
}
},
props: []
}
</script>
But this gives me the error this.set is not a function. Can someone help?
Thanks in advance!
Vue does not allow dynamically adding new root-level reactive properties to an already created instance. However, it’s possible to add reactive properties to a nested object, So you may create an object and add a new property like that:
data: function () {
return {
someObject:{
cars: true,
}
}
and add the property with the vm.$set method:
methods: {
click_me: function () {
this.$set(this.someObject, 'planes', true)
}
}
for vue 1.x use Vue.set(this.someObject, 'planes', true)
reactivity

Categories