简体   繁体   中英

How to Update ViewModel without Using Mapping

I have a knockout ViewModel that has a few observables, some observable arrays that have observables.

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()); };

    return self;
};

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;
};
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;
};

var myModel = new FulfillRequistionRequestModel(thing);
ko.applyBindings(myModel);

You get the idea. I have a ViewModel that has a few observables, and then an observablearray made up of a collection of observables (ItemModel).

Here's a sample of the JSON it's consuming:

var thing = {
    RequisitionId: 55,
    FulfillItemList: [
        {
            BoxNumber: 555,
            LineNumber: 001,
            SourceBin: { Location: "Jason", Warehouse: "AMERICA" },
            ItemDetailList: [
                { Part: "999-JASONTEST-111", Condition: "OLD" },
                { Part: "999-JASONTEST-115", Quantity: 55 }
            ]

        },
        {
            BoxNumber: 444,
            LineNumber: 002,
            SourceBin: { Location: "Jason", Warehouse: "CANADA" },
            ItemDetailList: [
                { Part: "999-JASONTEST-221", Condition: "NEW" },
                { Part: "999-JASONTEST-225", Bcn: "ABC123", Condition:"BUSTED", Quantity: 23 },
                { Part: "999-JASONTEST-228", Quantity: 55 }
            ]

        }
    ]
};

Imagine this as an "order". Someone clicks an order ID, and it loads the relevant information. I want it, so that if they click another order ID -- the view model is updated with that information (a new JSON from an AJAX call).

EVERYWHERE I see, says use the ko.mapping.fromJS(newJSON, mapping, viewModel); format -- but I bit the bullet, and am not mapping my ViewModel with ko.mapping this time, I spent the time building it out so I could really flex its muscles, and ko.mapping won't seem to work on my hand-built MVVM.

So - lets say I get a new top-level data - how can I refresh myModel so that it's all working?

I've tried even just setting myModel = {} or myModel = new FulfillRequisitionRequestModel({}); just to see if I could zero out the elements/page - but that seems to break the .applyBindings , as the page stops responding - is no longer connected.

So -- i have myModel - and it is working great. It's an "order" with a list of things in that order (products, say) - and customer info. How do I load a new order into the model?

I really don't want to tear down and "reset" every sub-observable. I'd like to just refresh it, like ko.mapping does -- and frankly, if I have to walk through and clean house - what's the point? Head back to ko.mapping and let it handle all that.

So - with my carefully constructed viewmodel - how can I update it with a whole set of new data coming from AJAX/REST (it's JSON) -- like ko.mapping.fromJS does?

This was really frustrating, and RP Niemeyer led me on the right path with using ko.utils.extend to extend refreshing externally. that didn't work for me so well, and it's probably due to my references of self , this , and return self inside each object.

Essentially, I changed my FulfillRequistionRequestModel (the top dog) to :

var FulfillRequistionRequestModel = function (data) {
    data = data || {}; var self = {};

    self.RequisitionId      = ko.observable();      //neutered
    self.FulfillItemList    = ko.observableArray(); //neutered

    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;
};

You'll note with this, I've set up the two main observable properties as just empties (the RequisitionId and FulfillItemList ) -- and then created a self.update function that will reset those fields to content or null, depending on what's passed in. I'm sure there's probably something more elegant than using $.each to push. Maybe ko.utils.arrayMap or some such, but this works.

TLDR: I created an update routine inside the viewmodel. This isn't perfect, because it now has that when I ko.mapping.toJS it, to send it back to C#/REST-API, but that's also just fine because the way it consumes it, it ignores any extras.

public IHttpActionResult testloadpart(FulfillRequisitionRequest incoming) {
//do stuff
//incoming.update does not exist.
//incoming.NumberOfItems does not exist.
//incoming.RequisitionId DOES exist

}


public partial class FulfillRequisitionRequest {

    [XmlElementAttribute(Order = 0)]
    public long RequisitionId { get; set; }
    [XmlArrayAttribute(Order = 1)]
    [XmlArrayItemAttribute("FulfillItem", IsNullable = false)]
    public List<fulfillItem> FulfillItemList { get; set; }

    public FulfillRequisitionRequest() {
        FulfillItemList = new List<fulfillItem>();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM