Reputation: 27350
I would like to push
a new item onto an observableArray
, but only if the item is not already present. Is there any "find" function or recommended pattern for achieving this in KnockoutJS?
I've noticed that the remove
function on an observableArray
can receive a function for passing in a condition. I almost want the same functionality, but to only push it if the condition passed in is or is not true.
Upvotes: 102
Views: 76994
Reputation: 76
I would add "valueWillMutate()" before changes and "valueHasMutated()" after them.
for (var i = 0; i < data.length; i++) {
var needChange = false;
var itemToAdd = data[i];
var match = ko.utils.arrayFirst(MyArray(), function (item) {
return (itemToAdd.Code === item.Code);
});
if (!match && !needChange) {
MyArray.valueWillMutate();
needChange = true;
}
if (!match) {
MyArray.push(itemToAdd);
}
}
if (needChange) {
MyArray.valueHasMutated();
}
Upvotes: 0
Reputation: 11
search a object in a ko.observableArray
function data(id,val)
{ var self = this;
self.id = ko.observable(id);
self.valuee = ko.observable(val); }
var o1=new data(1,"kamran");
var o2=new data(2,"paul");
var o3=new data(3,"dave");
var mysel=ko.observable();
var combo = ko.observableArray();
combo.push(o1);
combo.push(o2);
combo.push(o3);
function find()
{
var ide=document.getElementById("vid").value;
findandset(Number(ide),mysel);
}
function indx()
{
var x=document.getElementById("kam").selectedIndex;
alert(x);
}
function getsel()
{
alert(mysel().valuee());
}
function findandset(id,selected)
{
var obj = ko.utils.arrayFirst(combo(), function(item) {
return id=== item.id();
});
selected(obj);
}
findandset(1,mysel);
ko.applyBindings(combo);
<select id="kam" data-bind="options: combo,
optionsText: 'valuee',
value: mysel,
optionsCaption: 'select a value'">
</select>
<span data-bind="text: mysel().valuee"></span>
<button onclick="getsel()">Selected</button>
<button onclick="indx">Sel Index</button>
<input id="vid" />
<button onclick="find()">Set by id</button>
http://jsfiddle.net/rathore_gee/Y4wcJ/
Upvotes: 0
Reputation: 1022
Thanks RP. Here's an example of using your suggestion to return the 'name' property via the object's 'id' property from within my view model.
self.jobroles = ko.observableArray([]);
self.jobroleName = function (id)
{
var match = ko.utils.arrayFirst(self.jobroles(), function (item)
{
return item.id() === id(); //note the ()
});
if (!match)
return 'error';
else
return match.name;
};
In HTML, i have the following ($parent is due to this being inside a table row loop):
<select data-bind="visible: editing, hasfocus: editing, options: $parent.jobroles, optionsText: 'name', optionsValue: 'id', value: jobroleId, optionsCaption: '-- Select --'">
</select>
<span data-bind="visible: !editing(), text: $parent.jobroleName(jobroleId), click: edit"></span></td>
Upvotes: 11
Reputation: 114792
An observableArray exposes an indexOf
function (wrapper to ko.utils.arrayIndexOf
). This allows you to do:
if (myObservableArray.indexOf(itemToAdd) < 0) {
myObservableArray.push(itemToAdd);
}
If the two are not actually a reference to the same object and you want to run custom comparison logic, then you can use ko.utils.arrayFirst
like:
var match = ko.utils.arrayFirst(myObservableArray(), function(item) {
return itemToAdd.id === item.id;
});
if (!match) {
myObservableArray.push(itemToAdd);
}
Upvotes: 222