1

When creating a viewmodel for Knockout, what's the difference here? I see these different implementations and I've tried a few and some don't work the same.

//the most common example.
function ViewModel(data){
   data = data || {};  var self = this;      //note self = this
   self.property1 = ko.observable(data.property1 || null);
}

//this one uses a var
var ViewModel = function(data) {
   data = data || {};  var self = this;      //note self = this
   self.property1 = ko.observable(data.property1 || null);
}

//this one uses a var, returns itself, and self isn't "this"
var ViewModel = function(data){
   data = data || {}; var self = {};        //note self is empty object
   self.property1 = ko.observable();

   if (data.property1){self.update(data);};
   self.update = function(){self.property1 = data.property1 || null);};
 return self;
}

I've noticed that with the second model, I've had troubles trying to reference $parent in the data-binding part of HTML. The only one that seems to work reliably for it is the last model (and I understand the .update bit, allows you to refresh the viewmodel as well).

Is there a real difference here? I've only noticed that with the last example, if I replace self = {} with self=this, I get issues with $parent and such in the data-binding in HTML (admittedly, my viewmodel was much more complex, built from multiple observablearrays of observables). I also remember having problems with computed functions not showing up based on the type I used. It was all very confusing (and hard to debug a ko'd object's structure, functions everywhere!).

Here's what I ended up doing:

//this is the "main" viewmodel, what gets .applyBindings()'d
var FulfillRequistionRequestModel = function (data) {
    data = data || {}; var self = {};

    self.RequisitionId = ko.observable(data.RequisitionId || null);
    self.FulfillItemList    = ko.observableArray(
        ko.utils.arrayMap(data.FulfillItemList, function (item_list) { return new FulfillItemModel(item_list); })
    );
    self.NumberOfItems = ko.computed(function () { return self.FulfillItemList().length; });

    //add-remove FullfillItem in FulfillItemList (this is per line number)
    self.removeItemFromList = function (fulfill_item) { self.FulfillItemList.remove(fulfill_item); };
    self.addItemToList = function () { self.FulfillItemList.push(new FulfillItemModel()); };
   //allow self updates
    self.update = function (data) {
        data = data || {};
        self.FulfillItemList.removeAll(); self.RequisitionId(data.RequisitionId || null);
        if (data.FulfillItemList) {
            $.each(data.FulfillItemList, function (i, item) {
                self.FulfillItemList.push(new FulfillItemModel(item));
            });
        }
    }; if (data) { self.update(data); } //this handles first-construction (.applyBinding)

    return self;
};

//an observable (array'd in main vm)
var FulfillItemModel = function (data) {
    data = data || {}; var self = {};

    self.BoxNumber      = ko.observable(data.BoxNumber                  || null);
    self.LineNumber     = ko.observable(data.LineNumber                 || null);
    self.SourceBin      = ko.observable(new BinModel(data.SourceBin)    || null);
    self.ItemDetailList = ko.observableArray(
        ko.utils.arrayMap(data.ItemDetailList, function (item_list) { return new ItemDetailModel(item_list); })
    );
    self.NumberOfItems = ko.computed(function () {return self.ItemDetailList().length;});

    //add-remove something from ItemDetailList (ItemDetail)
    self.removeItemFromList = function (item) { self.ItemDetailList.remove(item); };
    self.addItemToList = function () { self.ItemDetailList.push(new ItemDetailModel()); };

    return self;
};

//an observable two deep
var ItemDetailModel = function(data) {
    data = data || {}; var self = {};

    self.Part           = ko.observable(data.Part           || null);
    self.Owner          = ko.observable(data.Owner          || null);
    self.Condition      = ko.observable(data.Condition      || null);
    self.Bcn            = ko.observable(data.Bcn            || null);
    self.SerialNumber   = ko.observable(data.SerialNumber   || null);
    self.Quantity       = ko.observable(data.Quantity       || null);

    return self;
};

TLDR: So I guess what I'm wondering, without really knowing enough about how JS works (I know enough to do damage, plus 10 more damage - but not how it operates internally, or prototyping, etc). What's the best choice for this?

The reason I use .update, is because I like being able to refresh the whole model (think looking at a customer and all their orders and their clients, etc) - and choosing customers on the fly from a list. ko.mapping makes this easy, not so much when you build it out. This way, I can just var myModel = new ViewModel(data); or later do a myModel.update(new_data);. (i know there's a ko.extend feature -- but I never got that to work right, even trying RP's example).

  • 1
    Look into the difference between function declarations and function expressions, and the difference between invoking a function with new and not invoking it with new. – Hey Jan 22 '15 at 17:01
  • @Hey: You might as well put this in an answer, with some guidance about the differences that's specific to the OP's particular scenario. – Robert Harvey Jan 23 '15 at 17:02
  • @RobertHarvey I don't really have much experience with KO, so I can't really give any guidance regarding that. I think KO viewmodels are just plain old objects at the end of the day, though, so this is pretty much a matter of preference as far as I can tell. – Hey Jan 23 '15 at 22:59
  • You're right. They end up being some fairly complex objects, but still just objects. – Jason Jan 23 '15 at 23:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.