How can I create a typed collection class in Knockout mapping?

94 views Asked by At

I'm using knockout with the mapping plugin so that I can write code like this:

function ContactViewModel() {
    //other properties...
    this.emails = ko.observableArray();
}

function ContactEmailViewModel() {
    //other properties...
    this.address = ko.observable();
}

var koContactMap = {
    'emails': {
        create: function (options) {
                    return new ContactEmailViewModel(options.data);
        }
    }
};

var model = new ContactViewModel();
ko.mapping.fromJS([JSON data from web service], koContactMap, model);

In English, I have contacts and emails, and a contact has emails.

This works perfectly, and I can load the contact and have the data and emails populated, and the emails are of type ContactEmailViewModel, as I want.

But the thing I'm confused about is: why does the map create method return a singular email object instead of a collection of email objects. The emails property is a collection, but seems to be populated by returning a single object, and is called multiple times, once for each member.

This correctly populates the emails property. But now I want to change emails from an array to an EmailsList object, so that I can give it methods, and I can't see how to do this, since the create method returns individual emails, not the whole emails property.

1

There are 1 answers

3
Anders On BEST ANSWER

For that behaviour you can add ignore on the emails propery and let the mapping plugin serperate map the emails into a standar array that you feed the EmailsList constructor with.

var emailMapping = {
   create: function(opt) {
      return new Email(opt.data);
   }
};

this.emails = new EmailList(ko.mapping.fromJS(arr, emailMapping));

Hmm, you could also extend a custom class with a observableArray I think (havent tested) that ko.mapping will then just add emails to your EmailList like

this.emails = new EmailList(); //This is is a ko.observableArray the mapping plugin will populate it with emails
ko.mapping.fromJS(data, mappingInfo, this);

https://github.com/knockout/knockout/blob/master/src/subscribables/observableArray.js

update: I just confirmed that solution two worked, so I would say thats a good way of doing it

http://jsfiddle.net/xop2go2z/

//stolen from ko source
function setPrototypeOf(obj, proto) {
        obj.__proto__ = proto;
        return obj;
    }

ko.MyList = function(arr) {
    var result = ko.observableArray(arr);
    return setPrototypeOf(result, ko.MyList.fn);
}

ko.MyList.fn = {
    pushMyThingy: function(obj) {
        this.push(obj);
    }
};