Knockout: Add ko.observableArray.fn.pushAll

Created on 2 Apr 2012  路  9Comments  路  Source: knockout/knockout

I'd like to have a pushAll function added to observableArray that appends items to the array. The use case is for when I have a list of items and I want to poll the server for additional records without blowing away my observableArray. Another simpler use case is for when I want to add multiple selected items to another selectedItems array. If there is another way to do this and keep observability, I'm all for it. Currently I am adding this to my code, though:

ko.observableArray.fn.pushAll = function(valuesToPush) {
var items = this;
for (var i = 0, j = valuesToPush.length; i < j; i++) {
items.push(valuesToPush[i]);
}
return items;
};

very few nice-to-have feature

Most helpful comment

IMO, something about this needs to be said in the doco under 'Working with observable arrays' because the question of suddenly adding/replacing a large number of items watched by an observableArray is simply not addressed there.

I was very disappointed that after having read that page, not to know about:

a) whether we can simply invoke myObsArry([]) to replace the entire array being observed;
b) how a large number of items could be added without firing events, if using push()
c) the existence of ko.utils.arrayPushAll()

Certainly, you should document all of that, on that page.

Let's assume that myObsArry([]) does work, and that the use-case of 'add a lot of items at once an observableArray without the bother of replacing the items already there' is indeed something which will not often be used, and it may in fact be preferable not to make every user pay the price of having pushAll() on observableArray. Well, my preferred approach to that would be:

a) for the function suggested by rniemeyer to be already-coded up for me somewhere (eg. in utils)
b) for there to be an easy way to install it (observableArray.fn approach is fine)
c) for it to be documented in the 'Working with observable arrays' page

thanks for hearing me out.
David.

All 9 comments

We do have ko.utils.arrayPushAll(array, valuesToPush) as a utility function that you can use. It is not available directly off of observableArrays though.

If you add your pushAll to observableArrays, you would want to operate on the underlying array (this() in your case) and then call valueHasMutated() on the observableArray at the end. This will ensure that subscribers to the observableArray are only notified once with the end result rather than with each push.

In KO core, it would need to call valueWillMutate() beforehand as well.

Good points Ryan. In my sample valueHasMutated does not need to be called, but depending on how you write it it would be. For example, when I used ko.utils.arrayPushAll(array, valuesToPush) I had to call valueHasMutated afterwards to get it to work. Having the ability of append items to an array and have them updated in the UI through observability is one of those requests I hear a lot.

@johnpapa - my point was that I would not recommend using the code that you posted, as it will notify on every push, which can have a performance impact if you are pushing many items.

In core, we might do something like:

ko.observableArray.fn.pushAll = function(valuesToPush) {
    var underlyingArray = this();
    this.valueWillMutate();
    ko.utils.arrayPushAll(underlyingArray, valuesToPush);
    this.valueHasMutated();
    return this;  //optional
};

Yep. Agreed. I just meant to day that it should update. That's all.

--- John Papa ---
From: Ryan Niemeyer
Sent: 4/2/2012 9:00 AM
To: John Papa
Subject: Re: [knockout] Add ko.observableArray.fn.pushAll (#416)
@johnpapa - my point was that I would not recommend using the code
that you posted, as it will notify on every push, which can have a
performance impact if you are pushing many items.

In core, we might do something like:

ko.observableArray.fn.pushAll = function(valuesToPush) {
    var underlyingArray = this();
    this.valueWillMutate();
    ko.utils.arrayPushAll(underlyingArray, valuesToPush);
    this.valueHasMutated();
    return this;  //optional
};

Reply to this email directly or view it on GitHub:
https://github.com/SteveSanderson/knockout/issues/416#issuecomment-4877853

I would be very nice if this code was merged, so that people don't reopen the same bugs again and again :-)

I wish there were similar functions for:

  • unshiftAll - adds items to the beginning of the observableArray
  • insertAt - inserts an item at a particular index and pushing the rest down
  • replaceAt - replaces an item at a particular index
  • removeAt/deleteAt - removes/deletes an item at a particular index
  • and other array functions..

@freejelly Except for pushAll, all of those functions can be accomplished with splice, which is available on the knockout observableArray directly so observers are updated accordingly.

@freejelly : As an example, given an observableArray called the_list and the list of elements to add items_to_add or an individual item, one can achieve the equivalent of unshiftAll _et al_ like this:

function unshiftAll(the_list, items_to_add) {
    the_list.splice.apply(the_list, [0, 0].concat(items_to_add))
}

function insertAt(the_list, item, at) {
    the_list.splice.apply(the_list, [at, 0].concat([item]))
}

function replaceAt(the_list, item, at) {
    the_list.splice.apply(the_list, [at, 1].concat([item]))
}

removeAt(the_list, at) {
    the_list.splice.apply(the_list, [at, 1])
}

#  EDIT
concat(the_list, items_to_concat) {
  the_list.splice.apply(the_list, [the_list().length, 0].concat(items_to_concat))
}

If anything could be considered a "missing" function on observableArray is concat (which I just added).

I believe it is fairly straightforward to add these to observableArray.fn.

IMO, something about this needs to be said in the doco under 'Working with observable arrays' because the question of suddenly adding/replacing a large number of items watched by an observableArray is simply not addressed there.

I was very disappointed that after having read that page, not to know about:

a) whether we can simply invoke myObsArry([]) to replace the entire array being observed;
b) how a large number of items could be added without firing events, if using push()
c) the existence of ko.utils.arrayPushAll()

Certainly, you should document all of that, on that page.

Let's assume that myObsArry([]) does work, and that the use-case of 'add a lot of items at once an observableArray without the bother of replacing the items already there' is indeed something which will not often be used, and it may in fact be preferable not to make every user pay the price of having pushAll() on observableArray. Well, my preferred approach to that would be:

a) for the function suggested by rniemeyer to be already-coded up for me somewhere (eg. in utils)
b) for there to be an easy way to install it (observableArray.fn approach is fine)
c) for it to be documented in the 'Working with observable arrays' page

thanks for hearing me out.
David.

To help us focus on more highly-prioritised issues, we're tidying up our issue backlog. We're closing the oldest feature requests that don't have pull requests and score very low on our triage ranking system. The low triage score for this feature request suggests we should focus our finite resources on other issues. For more details, see here. Thanks for posting this feature request though. If you still actively want this feature, please reopen this issue, or post a new one (and ideally, be prepared to supply a pull request if we agree the feature would be accepted). Thanks!

Was this page helpful?
0 / 5 - 0 ratings