Vuejs does not update my interface, anyone know why? - javascript

I have an app with vuejs. I have an object that is being updated from an API every 10 seconds, it must also change the Html (interface) when it's updated.
I am getting some difficulties in updating the interface although I can see the object changing in the console, therefore I know its changing.
Here is a piece of my code:
watch: {
myObj() {
for (let [key, tuner] of Object.entries(myObj)) {
---- some code ----
}
}
},
},
created() {
setInterval(this.callMyApi(), 10000);
},
I call my API every 10 seconds and then i use the WATCH property to check when myObj changes and the do some logic with it. Can anyone help me?

There are 2 components to this that are extremely important to ensure that watching an object -- especially with nested properties -- works correctly.
First, you must either A) instantiate the property as an object by declaring myObj: {} or B) use Vue.set(this, 'myObj', yourCustomObject) to ensure that it is reactive.
Second, you must use deep: true, if your object contains nested objects/arrays.
watch: {
myObj: {
deep: true, // this is the important part
handler() {
// your code
}
}
}

Related

How to replace one object for another in Vuejs? Interface not changing

i am doing an app in Vuejs and i am stuck with a problem in replacing one object for another that comes from an API every 10 seconds. I have this at the moment:
watch: {
myObj() {
for (let [key, tuner] of Object.entries(myObj)) {
---- some code ----
}
}
},
},
created() {
setInterval(this.callMyApi(), 10000);
},
I am watching the object and then use it in a for loop that does some logic. I saw the documentation mention using 'this.$set()' but this only adds a property to the object and i want replace one for another. Thanks for the help.
I would use:
this.myObj = JSON.parse(JSON.stringify(newObj))
to make a deep copy of the object inside this.callMyApi().
To watch for changes on this object do the following: create a new bool variable and use it on the component you want to update:
<componenttoupdate :reactto=objupdated/>
data: {
...
objupdated: false
...
}
and switch the objupdated bool state anytime you do the deep copy on your function, and so the component will update.
This is a cheaper solution than keeping watchers over large objects that may get updated very often as yours.

Vue reactivity issue, need some explanations

I've removed most useless parts of my code, so don't worry if this code doesn't really make sense, it's just to show you what's not working.
First, I create an array from a base array called objects:
objects: [
{
text: "I dont trigger stuff",
},
{
"text": "I dont trigger stuff",
},
{
text:"I trigger stuff",
activated: undefined,
},
],
And the create function
created() {
const newArray = [];
this.objects.forEach(anObj => {
anObj.activated = false;
newArray.push(anObj);
});
this.filteredObjects = newArray;
},
I initialize a property activated to false. In my real code I'm not using a forEach, but a find but the result is the same.
Then, I display a few buttons to trigger an "activation"
<button
v-for="(myObj, index) in filteredObjects"
:key="index"
#click="activateObject(myObj, index)">
{{ myObj.text }}
</button>
And the function being triggered is this one:
activateObject(anObj, anObjIndex) {
this.$set(this.filteredObjects[anObjIndex], 'activated', !anObj.activated)
},
My goal here is just to update the activated property.
To check if reactivity is working, I've got a watcher:
watch: {
filteredObjects: {
handler() {
alert('called')
},
deep: true,
}
},
I've got two questions:
1/ Since all activated properties are set to false for all objects, why is there only one working, the one with the property initially set to undefined?
2/ If I update my activation function to:
activateObject(anObj, anObjIndex) {
anObj.activated = !anObj.activated;
this.$set(this.filteredObjects, anObjIndex, anObj);
},
It works well. Can someone explain me why, and what is the difference?
In both cases, VueJS Devtools shows the updated values when clicking on refresh. It's a reactivity issue.
You can find a fiddle here:
https://jsfiddle.net/dv1jgneb/
From the Docs:
Since Vue performs the getter/setter conversion process during instance initialization, a property must be present in the data object in order for Vue to convert it and make it reactive.
This explains why only the third button "trigger stuff".
So, you may either add that attribute in the data(), or as said in the docs, use this.$set:
this.objects.forEach(anObj => {
this.$set(anObj, 'activated', false);
newArray.push(anObj);
});
JS Fiddle
Hope this helps!

Value with parametrized validation with ES6 class inheriance

I'm rewriting some old Chrome extension code while simultaneously trying to learn new ES6 tricks, and I'm running into some design questions.
My goal is to provide a value storage (which is backed by the asynchronous chrome.storage for persistence, but that's outside the scope of the question). What I want is to associate some validation with the values. So, my Storage is a collection of Values, each associated with a validation function.
In my old version, I would just pass a validation function when I instantiate a value, something like this (simplified):
Storage["key1"] = new Value({
validator: ValidatorIsInteger, defaultValue: 0, /* ... */
});
Storage["key2"] = new Value({
validator: ValidatorEnum(["a", "b", "c"]), defaultValue: "a", /* ... */
});
However, I'm trying to rewrite this with Value being a class that can be extended with specific validators, which seemed to be a good idea at the time. Again, simplified:
class Value {
constructor(key, defaultValue) {
this.key = key;
this.defaultValue = defaultValue;
this.set(defaultValue);
}
set(newValue) {
var validationResult = this.validate(newValue);
if (validationResult.pass) {
this.value = newValue;
return newValue;
} else {
throw new RangeError(
`Value ${newValue} for ${this.key} failed validation: ${validationResult.message}`
);
}
}
get() { return this.value; }
// Overload in children
validate(value) {
return {pass: true};
}
}
class IntegerValue extends Value {
validate(value) {
if (Number.isInteger(value)) {
return {pass: true};
} else {
return {pass: false, message: "Value must be an integer"};
}
}
}
So far so good. However, I run into problems when trying to make a parametrized child class:
class EnumValue extends Value {
constructor(key, defaultValue, possibleValues) {
this.possibleValues = possibleValues; // NUH-UH, can't set that before super()
super(key, defaultValue);
}
// Will be called from parent constructor
validate(value) {
if (this.possibleValues.includes(value)) {
return {pass: true};
} else {
return {pass: false, message: `Value must be in [${this.possibleValues}]`};
}
}
}
The problem is in "setting up" the parametrized validator before .set(defaultValue) is called. I see several ways out of this, all of which seems lacking:
Resign, and not use the class-extension-based approach - I want to see if it can be fixed first.
Always trust the default value as a workaround to calling .set(defaultValue) - bad, because I don't want accidentally inconsistent data.
Make .set() asynchronous, giving the constructor a chance to finish before validation is performed - while the persistence backend is asynchronous, the purpose of Storage is, among other things, to provide a synchronous "cache".
Am I failing to see some obvious fix to this approach? If not, and this is simply a wrong tool for the job, how should I re-organize this?
This is the classic problem with calling overrideable methods (validate, via set) from constructors; discussed here (different language, same problem).
Your specific example lends itself to a couple of workarounds, but the general issue remains.
To solve the general problem, I'd set value directly, not via set, and use unit tests to ensure that I didn't create validators that have an invalid default value. That is, after all, a coding error, not a runtime error.
But if you want to keep calling set, a couple of options:
You could have the concept of a Value that has no default value, which might be useful in general anyway. That would solve the problem by letting you have a Value constructor that doesn't expect to receive a default value. You could give a Value a default value after construction via setDefaultValue or similar; that method would validate, but that's fine, because it would be called post-construction in the subclass.
You could give Value a "validating" and "non-validating" state, and have the constructor accept a flag for which state it should start out in. Subclasses would use false if they had special validation behavior, ensure that all their ducks are in a row, and then set the validation state (which would validate).

How can I observe multiple key paths with ractive?

I'd like to observe changes to a key path per docs on ractive observe. However, I'd like to be able to observe multiple paths at the same time, eg, given:
var binding = new Ractive({
el: '.here',
data: {
items: [
{
finished: false
},
{
finished: false
}
]
},
template: someTemplate
})
I'd like to be able to do something like:
binding.observe('items.*.finished')
Or similar to be able to watch the finished property of any item in the array.
What's the best way to do this?
Exactly as you described! http://jsfiddle.net/rich_harris/c3yc848z/
Note that the values of any * placeholders are passed to the callback as additional arguments, beyond the (newValue, oldValue, keypath) that you normally get – so in this case there'd be a fourth index argument.

Hidden properties to console.log or utils.inspect

I'm working with sails.js waterline orm. Now this is not particularly a sails question, but i have to place some context, so when you create a record you get back an object with the data created. If the record has other records (collections) associated, it has keys related to those in the returned object, but those keys are getters/setters, even if no data is present for those related objects.
I've simplified a few things just to expose the main point.
This is my user model:
var User = {
attributes:
status: {type:'string'}
images: {
collection: 'Image'
}
}
Lets assumme, i performed a create query on a User model, that has a images collection associated. The userRecord is what the query returned.
if i console.log this out it shows the properties related to the model itself
but not the associated records, even though the key is actually there, you can
access it but is not visible to console.log or utils.inspec even when setting show hidden to true.
console.log(userRecord)
This is what gets returned
{ name: 'zaggen'}
This is what should get returned
{ name: 'zaggen',
images: [{
path: 'some-path/img.png'
}]
}
And i can access the hidden property like this:
console.log(userRecord.images[0].path)
// outputs some-path/img.png
How is this even possible?, as far as i know there is no way to hide info to the console.log in node, except maybe when the properties are defined in the __proto__ object, but in this case they are not.
After searching for this i haven't found anything and its pretty weird so i thought it could be a good question for SO. It'll help on my work process if i could console.log this info and get all the data, right now i can use lodash and call clone or defaults and i get the object as it should.
as far as i know there is no way to hide info to the console.log in node, except maybe when the properties are defined in the proto object
That's no longer true in ES5. It was true in ES3.
Notice that even in the original javascript, objects and functions have hidden properties like .__proto__ or .constructor or .prototype? It was like some native javascript objects have these magic features (like how setting innerHTML can call the HTML compiler). ES5 exposes all that magic by way of Object.defineproperty.
The specific feature that hides a property from console.log() is enumerable. Setting it to false on a property makes it hidden from for..in (no need for .hasOwnProperty() anymore):
var foo = {a:1}
Object.defineProperty(foo,'b',{
enumerable: false, // hide it from for..in
value: 2
})
console.log(foo); // prints out {a:1}
console.log(foo.b); // prints out 2
There are other useful features such as getters and setters (allowing you to emulate properties like .innerHTML that calls a function when you write to it) and writable (allowing you to make a property read-only). See the full documentation for details: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
Sails uses waterline which is where the model is defined. If you take a look at the source code for it, you see this:
https://github.com/balderdashy/waterline/blob/77fe3a9b9a9b12110a6ba079a84e5cd43a4369db/lib/waterline/model/lib/model.js#L57-L75
/**
* Log output
* #return {String} output when this model is util.inspect()ed
* (usually with console.log())
*/
Object.defineProperty(this, 'inspect', {
enumerable: false,
configurable: false,
writable: false,
value: function() {
var output;
try {
output = self.toObject();
} catch (e) {}
return output ? util.inspect(output) : self;
}
});
So they override the console.log output to self.toObject(). This is one of their internal methods that does all kinds of stuff that could be responsible for the output your seeing. For example:
// Don't run toJSON on records that were not populated
if (!self.proto._properties || !self.proto._properties.joins) return;
Or:
if (!this.proto._properties.showJoins) return;
I noticed in their integration tests, they pass { showJoins: true } as the second argument when creating the model. I couldn't find anything about it in the docs, but maybe you could try that?
https://github.com/balderdashy/waterline/blob/48dc007b69a133169651aeb422fa3a61c3c6802c/test/integration/model/save.js#L150

Categories