51

Question says it all really. I want to copy an observable array to another in KnockoutJS.

jaffa
  • 26,770
  • 50
  • 178
  • 289

4 Answers4

63

To clone an observableArray you would want to do:

var viewModel = {
    array1: ko.observableArray(["one", "two"]),
    array2: ko.observableArray()
};

viewModel.clone = function() {
   viewModel.array1(viewModel.array2.slice(0));
};

If you want to just do a copy, then you would do:

viewModel.array1(viewModel.array2());

The problem with the second example is that the underlying array is the same, so pushing to array1 or array2 would result in both having the new value (as they both point to the same array).

netpoetica
  • 3,375
  • 4
  • 27
  • 37
RP Niemeyer
  • 114,592
  • 18
  • 291
  • 211
  • 2
    Yes I only want a value copy of the array rather than a reference copy so the first option should be the best fit for my scenario, thanks. – jaffa Jun 29 '11 at 08:37
  • 9
    In my experience, this seems to copy by reference if your observable arrays are full of observable properties because the observables themselves are objects. Slice copies objects by reference. Do you know how to deeply clone knockout arrays? – PrinceTyke Aug 15 '16 at 18:37
48

I was faced with the same task; to clone an observable array. The only way I could figure out how to do it, is to convert the observable to an JS object, then convert that object to an observable object. The following function requires KnockoutJS mapping plugin: http://knockoutjs.com/documentation/plugins-mapping.html

function cloneObservable(observableObject) {
    return ko.mapping.fromJS(ko.toJS(observableObject));
}

Hope this helps

Garry English
  • 5,070
  • 1
  • 36
  • 23
  • 8
    Be careful, this messes up with `ko.computed()`, stores just value, you have to redefine `computed` after clone. – Anri May 06 '13 at 16:21
  • @Anri - Thank you for posting your comment... it saved me hours!! Garry - please update answer with this! – Kevin Aug 16 '13 at 13:16
  • 5
    If you don't want to use the knockoutJS mapping plugin, another option is to convert your object to JSON an create a new using the parsed json data. Since I didn't want to include the mapping plugin as I only need it here I did: `self.myObservable(new koModel(JSON.parse(ko.toJSON(oldObservable))))` – Gigi2m02 Oct 15 '13 at 15:51
  • 2
    @Anri and it also messes up with `__ko_mappings__` property if you do the mapping back and forth. Don't forget to ignore this property when you remap previously `toJS`-em object. – SOReader Mar 23 '14 at 17:50
  • 1
    use ko.mapping.fromJS(ko.mapping.toJS(observableObject)); to prevent the __ko_mappings__ causing problem on edit of the pushed cloned object later on. Had the same problem and using the mapping.toJS solved it. – user1415567 Jul 11 '14 at 19:41
  • Whats the fix for avoiding _ko_mappings ? – pravin Mar 29 '15 at 08:01
  • @pravin just use delete newItem.__ko_mapping__; – Yiping Jul 18 '15 at 02:53
  • One thing to note is that if you have an observable array of observable objects you're going to have to do that for each object in the array – user3619165 Jun 28 '16 at 22:56
2

Assuming you have something like:

modelA { someValues: observableArray(); }
modelB { iWantYourValues: observableArray(); }

You should be able to:

modelB.iWantYourValues(modelA.someValues())
0

Not exactly what you're asking, but I'd like to add this for posterity...

If you want to clone an observable that stays in sync with the original (most often to create a throttled/debounced clone while maintaining the original), you can do something as such:

const clone = ko.pureComputed(() => original()).extend({ rateLimit: 500 })

caseyWebb
  • 1,997
  • 17
  • 18