javascriptknockout.jsknockout-mapping-pluginknockout-sortable

knockout draggable sortable mapping clone alteration


I am trying to use knockout-sortable, with the mapping plugin, to drag a list of products to another list.

Here is a very stripped-back fiddle to try and show what I'm getting at.

What I want is for the products in the 'cart' to reference, not clone, the products on the 'shelf'.

I have managed to calculate the name of the items in the cart by referencing those on the shelf, but I am having trouble in getting the products in the cart to only have the data they need to make this reference, without all the other cruft.

Having looked around, I found this very tantalising solution by Ryan Niemeyer to a similar problem a couple of years ago, but the fiddles throw a 404 error!

I have created a 'clone' function, but because all my data is mapped it has not used a constructor such as:

var product = function(ID, name, other){
    this.productID = ko.observable(ID);
    //etc.
};

Which means that I cannot do what I want to do, which is:

product.prototype.clone = function(){
    var x = ko.mapping.toJS(this);
    delete x.productName; 
    delete x.someOtherData;
    ko.mapping.fromJS(x);
};        

Which I would hope would drop a new object into the cart with just a reference to the original productID key. I have commented out the code for this in the fiddle. The console.log does not get called after dragging, so the function is not even getting called.

I also looked on the Google forum for knockout-sortable and found this thread, which is where I got the clone bit from - but I can't make it fit my use case!

The result of a successful answer to this question will, when there is just a bike in the cart, leave productArray as it is and have listArray set to [{'productID':1}].

This is the first time that I've posted a question on SO, so please go easy on me if I have not made myself clear! I am already indebted to the community, as it has been so helpful in solving my problems for years and years :)


Solution

  • You would want your mapping options to look something like:

    var options = {
        productArray: {
            create: function(mappingData) {
                var result = ko.mapping.fromJS(mappingData.data);
    
                result.clone = function() {
                    return { productID: result.productID() };
                };
    
                return result;
            }
        }
    };
    

    Alternatively, you could create a constructor for your product with a clone function on it and just return that like return new Product(mappingData.data);

    I had the clone above just return the productID as a non-observable, which should be fine unless you need to react to that value changing.

    The first line of your calculatedName function would then need to be something like:

    var x = ko.unwrap(arg.productID);

    This will retrieve the value whether it is observable or not, so the function can work against the productArray or the listArray.

    http://jsfiddle.net/rniemeyer/hLqctg4L/