vue.js data not updating when incremented - javascript

I have an object containing an array, which gets incremented after some amount of logic has completed.
Vue.js doesn't seem to be capturing this increment and displaying it to the view.
HTML:
<div id="demo">
<p>{{points}}</p>
</div>
JS:
function Board()
{
this.team1 = {pointsMade:[0]};
}
var newBoard = new Board();
newBoard.team1.pointsMade[0]++
new Vue({
el: '#demo',
data: {
points: newBoard.team1.pointsMade
}
})
setTimeout(newBoard.team1.pointsMade[0]++,1000)
I have a JSFiddle that outlines the problem.
You can see that after setTimeout(newBoard.team1.pointsMade[0]++,1000) runs, the value should be '2', but is only displayed at '1'. What am I missing here?

When you define
data: {
points: newBoard.team1.pointsMade[0]
}
the points variable is just assigned current value of newBoard.team1.pointsMade[0], which is 1 at this moment. There is no magic here. JS primitives works by value, not by references.
So, after updating the newBoard.team1.pointsMade[0] variable, the point variable is not updating of course.
To make this work, use object instead of primitive value. Objects work by reference in JS.
From Properties and Methods example:
var data = { a: 1 }
var vm = new Vue({
data: data
})
vm.a === data.a // -> true
// setting the property also affects original data
vm.a = 2
data.a // -> 2
// ... and vice-versa
data.a = 3
vm.a // -> 3
edit
There is another caveat here:
Due to limitations in JavaScript, Vue cannot detect when you directly set an item with the index, e.g. vm.items[indexOfItem] = newValue. So, use Vue.set(newBoard.team1.pointsMade, 0, newBoard.team1.pointsMade[0] + 1);.
I updated your fiddle.

Related

vue v2 value inside object inside class property changed although not altered

I'm using vue v2 to create an SPA.
I declared an Offer class like this in the script section of my vue component:
class Offer {
constructor(Tariff, TariffCopy, billing, at, fa, backs) {
this.Tariff = Tariff;
this.BaseTariff = TariffCopy;
}
changeSAC(p1, p2) {
...
this.Tariff.provAufwand.SO = this.BaseTariff.provAufwand.SO + Number(p1);
}
}
Tariff and TariffCopy are objects initialized from a custom class called Tariff also declared in the script section of the same component. Basically they contain the same values, but Tariff is open to value changes, while BaseTariff's values shall remain unchanged.
class Tariff{
constructor(provAufwand) {
this.provAufwand = provAufwand;
}
}
where
provAufwand= {
SO: 1,
HO5: 2,
HO10: 3,
HO20: 4
}
When calling Offer.changeSAC(p1,p2) (p1 and p2 are bound to number inputs via v-model="p1" and v-model="p2") the value of this.BaseTariff.provAufwand.SO is changed, too, although I never alter it in the code.
Why is that? Where in my code are they connected?
Assuming that you created your Tariff instances like these:
const provAufwand= {
SO: 1,
HO5: 2,
HO10: 3,
HO20: 4
};
const tariff = new Tariff(provAufwand);
const tariffCopy = new Tariff(provAufwand);
Then the reason why BaseTariff or tariffCopy is also changing when you changed tariff is that they are referencing the same provAufwand object.
When you passed provAufwand to the Tariff constructor, the parameter that was passed was not the actual value of the object but the reference to the object.
When you passed an object or an array to a function, then the argument
is passed by reference.
This means that changing the value of the object/array inside the function affect the value of the same object/array outside the function.
To solve it, you can use the spread syntax when passing provAufwand when creating a new Tariff object.
const provAufwand= {
SO: 1,
HO5: 2,
HO10: 3,
HO20: 4
};
const tarrif = new Tariff({...provAufwand});
const tarrifCopy = new Tariff({...provAufwand});
This ensures that you are passing completely different objects when creating your class instances.

Why isn't a bound variable modified?

I have a problem changing the value of a bound variable in Vue (I am not sure whether this is due to my lack of knowledge of Vue, or of JavaScript in general, probably both).
The initialization of the Vue instance is done via the code below
var vm = new Vue({
delimiters: ['<<', '>>'],
el: "#index-container",
data: {
posts: [],
tags: {},
},
methods: {
switchAll: function(what) {
console.log(what)
console.log(JSON.stringify(this.tags))
_.forEach(this.tags, function (v, k) {
console.log(k)
this.tags[k] = false
})
console.log(JSON.stringify(this.tags))
}
},
(...)
Further down the script, values of tags are set with a constuction like Vue.set(vm.tags, tag, true) to ensure reactivity. They are then used in constrictions such as
<div class="tag on" v-for="(content, name) in tags" v-show="tags[name]" v-on:click="tags[name] = false">
Clicking on this div does change the value of the relevant element of the Object tags (initialized previously).
So far everything works as expected.
The problem is in the method switchAll. When I use it with a similar v-on:click() element (switchAll(false) for instance), it gets fired up but the console output is the following:
false
{"hello":true,"world":true}
hello
world
{"hello":true,"world":true}
My problem is that the last line should be {"hello":false,"world":false}.
I tried to use this.tags[k] = false and this.tags.k = false (though both are supposed to be equivalent) and I believe that the issue I have is that (apologies for the wording, I am new to JS) the keys in this.tags are "bare" in my case, and not "quoted". What my operation does is to add a new key, this time "quoted" (but then this.tags.k = false should be fine, as k is either hello or world (without quotes).
I am lost - what is wrong in my code, and why the inline code in the first (working) example of the div is fine?
Not an expert in lodash, but it seems to be a problem with callback function creating new this. There are several ways around this problem, like:
If you're OK with ES6, use lambda functions - they won't create new this:
_.forEach(this.tags, (v, k)=>{
console.log(k)
this.tags[k] = false
})
It can be useful to hold your this inside some method-wide variable:
var vm = this;
_.forEach(vm.tags, (v, k)=>{
console.log(k)
vm.tags[k] = false
})
Use vanillaJS object loop:
for (var k in this.tags) {
vm.tags[k] = false
}

Javascript ERROR, undefined, not an object

I am new in JavaScript, And I am trying to map my controller's buttons and leds for mixxx application. Is that an object, an array? var is missing.
BehringerCMDMM1.leds = [
// Master
{ "shiftButton" : 0x12 },
// Deck 1
{ "sync" : 0x30 },
// Deck 2
{ "sync" : 0x33 }
];
I have an error here,
BehringerCMDMM1.shiftButton = function (channel, control, value, status, group) {
// Note that there is no 'if (value)' here so this executes both when the shift button is pressed and when it is released.
// Therefore, BehringerCMDMM1.shift will only be true while the shift button is held down
var deck = BehringerCMDMM1.groupToDeck(group);
BehringerCMDMM1.shift = !BehringerCMDMM1.shift // '!' inverts the value of a boolean (true/false) variable
BehringerCMDMM1.setLED(BehringerCMDMM1.leds[deck]["shiftButton"], BehringerCMDMM1.shift);
}
about "shiftButton" as undefined.
also I have this function
BehringerCMDMM1.setLED = function(value, status) {
status = status ? 0x7F : 0x00;
midi.sendShortMsg(0x94, value, status);
}
This is from a javascript file I found on the internet created for a different controller. So, I am trying things to understand how can I configure mine.
BehringerCMDMM1.leds is an array of objects. Within that array, the element at index 0 is an object that has a shiftButton property. Thus, the only way to get the 0x12 value in your example is to do this:
BehringerCMDMM1.leds[0]['shiftButton']
So when this code executes...
var deck = BehringerCMDMM1.groupToDeck(group);
...the value of deck is probably something other than 0, and you're accessing one of the sync objects in the BehringerCMDMM1.leds array. For example, if the value of deck was 1, then this...
BehringerCMDMM1.leds[deck]['shiftButton']
...will be undefined because you're effectively doing this:
BehringerCMDMM1.leds[1]['shiftButton']
Ok,
I am new in JavaScript, And I am trying to map my controller's buttons and leds for mixxx application. Is that an object, an array?
You have a array of objects.
var is missing.
You should test what is inside yout deck variable. Try this:
console.log(deck);
if (deck in BehringerCMDMM1.leds) {
BehringerCMDMM1.setLED(BehringerCMDMM1.leds[deck]["shiftButton"], BehringerCMDMM1.shift);
} else {
console.log("Index: "+deck+" doesn't exist");
}

Make koGrid respect observable properties

I've been writing an app with the kogrid, recently I changed my datasource from an array of objects to an array of knockout objects. However, to my surprise when I update the observable properties within my objects the grid is not updated.
Here is my data array:
self.gridData = ko.observableArray([
{ name: ko.observable("joe"), age: ko.observable(5) }
]);
when I update the age property nothing happens on the grid:
self.gridData()[0].age(6);
does anyone have a good answer for why this is?
Update
I've answered the question below, but does anyone know why the kogrid would be caching the unwrapped values?
I looked into the kogrid source and found this line in src/classes/row.js
self.getProperty = function (path) {
return self.propertyCache[path] || (self.propertyCache[path] = window.kg.utils.evalProperty(self.entity, path));
};
it looks like the property cache is caching the unwrapped value of the property we're accessing in the default cell template:
<div data-bind="attr: { 'class': 'kgCellText colt' + $index()}, html: $data.getProperty($parent)"></div>
(Note: $data in the template is the column, which has a getProperty wrapper for row.getProperty)
I simply removed the line to cache property values like this:
self.getProperty = function (path) {
return window.kg.utils.evalProperty(self.entity, path);
};

How bind change event with view when in-array object's property changed?

The following are some of my coffeescript code
class Floor extends Backbone.Model
defaults:
"array":[]
initialize: ->
a = []
for i in [0..10] by 1
tmp = {
x: i*10,
y: i*10
}
a.push(tmp)
this.set('array', a)
class FloorView extends Backbone.View
initialize: ->
this.model.on('change:array', this.renderArray, this)
renderArray: ->
console.log 'Do something'
return this
floor1 = new Floor
floorView = new floorView({ model:floor1})
The following three lines are not fire the change event calling renderArray
method
array = floor1.get('array')
array[0].x = 1000;
floor1.set('array',array)
but the following code actually call the renderArray method
floor1.set('array',{});
is there any method to detect property changed of an object in an array?
or what I have done wrong?
You can think about it this way: "array" is a variable name that points to an array. When you do this:
floor1.set('array', []);
You are pointing "array" to a completely different array. However, when you do this:
array = floor1.get('array')
array[0].x = 1000;
floor1.set('array', array)
You are updating the array that "array" already points to. In other words, you are not changing the array that "array" points to. Instead, you are modifying a value within the existing array.
This Q&A has more backround: Backbone.js : change not firing on model.change()
And points out this work around:
array = floor1.get('array')
array[0].x = 1000;
floor1.set('array', array)
floor1.trigger('change:array')

Categories