Add/remove Knockout observableArray nested elements - javascript

I'm trying to add to/remove from a nested observableArray in KnockoutJS. I have an array several elements, each with an attribute object, type object and an attributeValue array which holds objects. So it's a nested array.
The allAttributes array is observableArray. Then I tried making the attributeValue array observable by making a new ViewModel (attributeValueViewModel) with attributeValues as ko.observableArray([]).
I made two Knockout functions (which don't work) and I'm trying to add/remove values to/from that array. The problem is that the array is nested so I have to access the attributeID through this.attribute.id. self.allAttributes[i].attributeValues[j] should be the object I'm adding/removing... where i=attributeID and j=index of the attribute's value object
Why aren't those functions working?
Here is my fiddle: http://jsfiddle.net/M6Hqj/2/

First off, you're overwriting the observable function in your inner view model, e.g. when you assign obj.attribute = item.attribute;, you're overwriting your initial assignment of self.attribute = ko.observable(data.attribute);. Instead assign the value through the observable, like so:
obj.attribute(item.attribute); //instead of obj.attribute = item.attribute;
This will also make your self.addAttributeValue() function call work since the array is now observable.
Next, in your self.removeAttributeValue() function, the this call actually refers to the specific record inside your attributeValues array, therefore, when you do this.attributeValues.splice(), it can't find your attributeValues object property. So, shift the function into the attributeValueViewModel object and use self instead of this, like so:
//This is inside function attributeValueViewModel(data)
self.removeAttributeValue = function() {
alert(JSON.stringify(this));
self.attributeValues.splice(this.id, 1);
}
To call it, just change your data-bind code to use $parent instead of $root, like so:
<button data-bind="click: $parent.removeAttributeValue">REMOVE</button>
Something like this fiddle here: http://jsfiddle.net/UMB79/
(Note that with these changes you still have to modify your logic to correctly add/remove elements, 'cause it's still buggy)

Related

How to duplicate object in JS where duplicate changes if you change original

I am making a planner/calendar website that is going to have a repeat function.
var chain = _.chain(state.items).filter({'id': 1}).head().value();
console.log(chain);
Here i filter one object, how do i duplicate chain that when i change the original the duplicate also changes and the other way around?
Variables that are assigned a non-primitive value are given a reference to that value. That reference points to the object’s location in memory. The variables don’t actually contain the value. This is why the original value changing when you're changing the duplicate.
This can be solved by using JSON.parse() and JSON.stringify()
Create a new function in the methods section
cloneObject:function(obj){
return JSON.parse(JSON.stringify(obj));
}
Now you can call this method to make a copy of any object, like
var items = this.cloneObject(state.items); // this will create a clone of the object
var chain = _.chain(items).filter({'id': 1}).head().value();
Here the filter won't effect the state.items since we made a clone of this data.
If you're already using lodash JS library, you can use the cloneDeep() method to make the copy
Eg:
var items = _.cloneDeep(state.items);
var chain = _.chain(items).filter({'id': 1}).head().value();
console.log(chain);

What does this code using [].filter.call do?

I’m learning javascript and trying to write code that sorts a list, removing elements if they meet certain criteria.
I found this snippet that seems promising but don't have a clue how it works so I can adapt it to my needs:
list = document.getElementById("raffles-list").children; // Get a list of all open raffles on page
list = [].filter.call(list, function(j) {
if (j.getAttribute("style") === "") {
return true;
} else {
return false;
}
});
Can you guys help me learn by explaining what this code block does?
It's getting all the children of the "raffles-list" element, then returning a filtered list of those that contain an empty "style" attribute.
The first line is pretty self-evident - it just retrieves the children from the element with id "raffles-list".
The second line is a little more complicated; it's taking advantage of two things: that [], which is an empty array, is really just an object with various methods/properties on it, and that the logic on the right hand side of the equals sign needs to be evaluated before "list" gets the new value.
Uses a blank array in order to call the "filter" method
Tells the filter to use list as the array to filter, and uses function(j) to do the filtering, where j is the item in the list being tested
If the item has a style attribute that is empty, i.e. has no style applied, it returns true.
Edit:
As per OP comment, [].filter is a prototype, so essentially an object which has various properties just like everything else. In this case filter is a method - see here. Normally you just specify an anonymous function/method that does the testing, however the author here has used the .call in order to specify an arbitrary object to do the testing on. It appears this is already built into the standard filter method, so I don't know why they did it this way.
Array like objects are some of javascript objects which are similar to arrays but with differences for example they don't implement array prototypes. If you want to achieve benefits of array over them (for example like question filter children of an element )you can do it this way:
Array.prototype.functionName.call(arrayLikeObject, [arg1, [arg2 ...]]);
Here in question array like is html element collection; and it takes items without any styling.
list is assigned a collection of elements that are children of the raffles-list element
list is then reassigned by filtering its elements as follows
an empty array is filtered by calling it with the parameter list and a callback function. The formal parameters for call are this (which is the list) and optionally further objects (in this case a callback function)
The callback function receives a formal parameter j and is called for each element
If the element's value for the style attribute is empty the element is retained in the array. Otherwise it is discarded.
At the end list should contain all elements that don't have a value for its style attribute

strange ng-model behaviour (ng-model mapped to array element)

What should be the right behaviuor when you have a ng-model declaration like this one?
data-ng-model="elements[0]"
The way it works, if elements is already defined in the scope as an array, it works as I'd expected assigning the first element of the array.
But if elements is not declared it assigns this value :
elements = {0:'anyvalue'}
(which makes sense if I'd had written data-ng-model="elements['0']")
In this case :
elements[0]='anyvalue';
elements['0']='anyvalue';
and I cannot read the value of the propery using "dot" notation (elements.0 or elements.'0').
So it looks correct, but a bit weird.
Is this behaviour correct, or it should instantiate an array when the scope variable is not defined?
An array is just a special type of object. If you look at an array in a debugger, all of the values are listed as properties with numeric keys, like the one you show. If you don't initialize the object as an array, it would still accesses the object in the same way, which just means you now have an object with numeric keys and none of the helpful functions from the Array prototype.

Pushing object with observable properties in observable array still track object's propteries.

I have an object addItem with several observable properties. Once the properties are populated I push this into an observable array allItems that tracks all the object created during a session.
The problem is that when I push an addItem into the allItems and then make changes to the addItem; allItems also changes. I assumed that once addItem was pushed into allItems the binding between the two would be no more, but it seem like a push just adds a reference.
How can I add addItem to allItems without the reference?
By default, ko.observable items will still be observable after they are added to an array. If you want to remove the observability, you'll need to create an object with non-observable properties.
One way is to copy the properties by hand, using ko.utils.unwrapObservable(obj.prop).
A more "automatic" way is to use ko.toJS() - this will convert an object with observable properties into a plain JS object.
self.allItems.push(ko.toJS(self.item2));
or if you want to maintain the observability separately for items inside the array, you can readd it using ko.mapping.fromJS:
allItems.push(ko.mapping.fromJS(ko.toJS(self.item2)));
See the Fiddle

KnockoutJS observableArray to update when inner observable is changed

I need to know how to trigger the update for an observableArray when an observable is changed inside the observableArray.
I have an observableArray that represents a binary tree. I'm using a storage mapping function to get and set the values in the array so it has to be balance even thought it might only contain an empty observable. E.g. nodes()[9] maybe null but when that node is updated I would call nodes()[9](set new value) and need to trigger the observableArray to update
Ended up using .replace() on the observableArray
Managed to look through the code and find observableArray.replace ()
RELATED: How to replace a given index element in knockoutjs

Categories