We have this data structure
[[{foo:"12", bar:"34"}, {foo:"33", bar:"55"}], [{foo:"45", bar:"67"}]]
How is it possible to make a computed property to observe a change on foo or bar?
Normally you use .#each.someProperty in a computed property to watch specific object properties in an array but in the case of nested arrays (or nested objects), you can't do .#each.#each.someProperty because .#each only works one level deep.
You need to use intermediary computed properties to raise the inner objects up one (or more) levels and have another computed property watch these in turn. There may be other ways to wire things up, but this is a pretty straightforward way to do it.
Here's a twiddle that demonstrates it. If you open the console you can see the individual computed properties recording a change occurring.
You can also use an alias to pull the inner arrays / objects up a level or two.
Related
This is my code and I am trying to change value of object in packs object. But when I type it, Javascript somehow changes all three different objects, that have nothing in common. And this is the only line that changes packs, rest should stay the same, but it's all changing with this line. How?
console.log(packs[usedPack].levels[level].bestBy) //null
console.log(defaultPack.levels[level].bestBy) //null
console.log(mainPacks[usedPack].levels[level].bestBy) //null
packs[usedPack].levels[level].bestBy = nameTyped; //this changes values in three different objects
//packs[usedPack].levels[level] = nameTyped; //if I type like this, this does change only original object, rest stays the same
console.log(packs) //nameTyped
console.log(defaultPack) //nameTyped
console.log(mainPacks) //nameTyped
Edit: It was indeed problem with referencing. I was using constructor function and this function needed to deep copy complex objects. I was checking values and I used multiple loops to copy all values from objects, but it didn't work properly. I've changed all of this to JSON.parse(JSON.stringify()), and it's working now. Thanks for help.
The issue here seems like you're assigning some object by its reference.
In javascript, if you have two objects and you assign them like
a = b;
Now whenever you will change b, a will also be changed. To avoid this we do deep clone using the spread operator
// this now does not reference to b but clones it
a = {...b}
In your code, you might be assigning some objects like this. a=b
Maybe you're assigning packs, defaultPack, and mainPacks using some same object.
Updated
#David pointed out one thing and that is if you are having some complex structure (like objects within object) and then you clone it using spread operator, the inner objects will still reference the same object.
To resolve this, for easiness you can use lodash deepclone function
const clonedeep = require('lodash/clonedeep');
const deepClonedObject = clonedeep(originalObject);
This will deep clone and even if the objects are nested they won't refer to the same object.
In KnockoutJS, we can go
ko.mapping.toJS(object)
and get our object returned with observables and whatnot converted to vanilla Javascript object. Now, ko.mapping.toJS ignores computed properties. I have a scenario where I basically would like the functionality of ko.mapping.toJS to retain my pureComputed fields with whatever value they had at the time.
I've looked into the documentations page, but it looks like the "mapping options" are for the fromJS method, not toJS.
Any way I can convert my Knockout Object to a JS object but retain the pureComputeds being regular properties in the output?
I switched from ko.mapping.toJS to ko.toJS and the output of ko.toJS does include the computed properties like I want, so that seems to be all I need.
I have array named List and created computed property computedList for him.
When i update value of array it's not showing in html, but in console i see thar array is updated.
`https://jsfiddle.net/apokjqxx/69/`
What is best way to use computed properties for array?
Maybe is exists way to trigger to re-render computed property?
Due to limitations in JavaScript, Vue cannot detect the changes to an array like this: this.list[1] = 'vueman'
You have to use Vue.set or vm.$set as explained here to trigger state updates in the reactivity system, like follwoing:
this.$set(this.list, 1, 'vueman')
see updated fiddler here.
So I understand that Ember can compute on an array and elements within it. There are two options here.
someArray.[] and someArray.#each
If say I changed one of the element in array and there is a computed property that depends on it. Which one should I use? Thanks.
someArray.[] will only be used when the array items are added/removed.
When the particular property in the array object is changed then someArray.#each will be called.
isNameChanged: function() {
console.log('is Name Changed')
}.property('someArray.#each.name')
Can check this Ember.js: Observing array property using #each doesn't work
I have a HTML fragment that iterates over key, value collection. When I create an object and put some value in, then iterate trough that object via HTML fragment, all works perfectly.
However since I need keys in specific order, I'm using a Map instead of plain object. This time when I debug I can see that my insertion order was preserved, but for some reason the HTML fragment which iterates over collection doesn't seem to know how to do so. I see nothing on my screen when I use the map object, opposed to the regular object when I see unordered content
tr ng-repeat="(key, value) in rowTitlesValues"
Is how my HTML fragment looks like, when I switch rowTitlesValues back to object works again, what am I doing wrong, and how does one keep insertion order or how do I sort object so it's keys are in custom order?
From Angular reference on ng-repeat (link):
Iterating over object properties
It is possible to get ngRepeat to iterate over the properties of an object using the following syntax:
<div ng-repeat="(key, value) in myObj"> ... </div>
You need to be aware that the JavaScript specification does not define the order of keys returned for an object. (To mitigate this in Angular 1.3 the ngRepeat directive used to sort the keys alphabetically.)
Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser when running for key in myObj. It seems that browsers generally follow the strategy of providing keys in the order in which they were defined, [...]
If this is not desired, the recommended workaround is to convert your object into an array that is sorted into the order that you prefer before providing it to ngRepeat. You could do this with a filter such as toArrayFilter or implement a $watch on the object yourself.
Additionally, I do not think Angular 1.x knows how to iterate over a Map. I believe this line in the code proves it:
collectionKeys = [];
for (var itemKey in collection) { // iterates your object using `in`, not `of` or `Map.forEach()`
...
}
// ng-repeat then iterates the collectionKeys to create the DOM
So you will probably need to act as Angular docs suggest:
[...] convert your object into an array that is sorted into the order that you prefer before providing it to ngRepeat. You could do this with a filter such as toArrayFilter or implement a $watch on the object yourself.