knockout create an empty object from auto-mapped observable
Asked Answered
F

2

9

I am trying to solve a small problem with ko mapping. The scenario is that my ViewModel is basically a collection of objects. Each of the objects is created from a json call, in this way:

var ViewModel = ko.observableArray();

$.getJSON(url, function(data) {
    ViewModel.push(ko.mapping.fromJSON(data));
});

This works perfectly and I can do all sort of magic in my HTML. The question is if for example I want to add something to my collection, let's say to support client-side "Add and Edit" scenario. I would like to do something like:

<input type="button" value="add new" data-bind="click: AddNew" />

And I would like the AddNew function in the ViewModel to be something like:

function AddNew() {
    this.push(// WHAT HERE?);
}

Basically I need to push an object which is identical to the other already existing, but of course with all blanked out properties...

I was thinking of a way of "cloning" an object form the list and setting all the observables to empty but I would not know where to start I'm afraid :/

Friede answered 26/5, 2012 at 2:48 Comment(0)
S
7

If you are going to expand your functionality to client-side edit/add then i would recommend formalizing your objects into a js class and then mapping these objects internally. This would allow you to have your add methods on your main viewmodel and create blank instances client side easily.

One caveat with the mapping plugin is that for updating objects it expects those objects to have been originally mapped by the plugin. Here's a quick example of how it could be done.

var YourObjectClass = function (config) {
    var self = this, data;

    // your default structure goes here
    data = $.extend({
        name: "",
        id : -1
    }, config);

    ko.mapping.fromJS(data, {}, self);
};

var viewModel = function(initialData) {
    var self = this;

    ko.mapping.fromJS(initialData, {
        items: {
            create : function (options) {
                return new YourObjectClass(options.data);
            }
        }
    }, self);

    this.AddNew = function () {
       self.items.push(new YourObjectClass());
    }
};

Hope this helps.

Squilgee answered 26/5, 2012 at 4:19 Comment(1)
"One caveat with the mapping plugin is that for updating objects it expects those objects to have been originally mapped by the plugin. Here's a quick example of how it could be done." - if I'd have known this before it would have saved me a lot of time!Canzone
T
0

Do you know the fields or do you need them copied from what you get from the server? I think you'd have trouble starting a new list if that were the case. You could just create a template and push it:

var myTemplate = {
    name: ko.observable(),
    phone: ko.observable()
};

ViewModel.push(myTemplate); // add empty item

I've never seen the view model be an observable or observable array, why not create it like this?

function ViewModel() {
    self = this;
    self.items = ko.observableArray();
}

var myViewModel = new ViewModel();
ko.applyBindings(myViewModel);

$.getJSON(url, function(data) {
    myViewModel.items.push(ko.mapping.fromJSON(data));
});
Tother answered 26/5, 2012 at 4:28 Comment(1)
The second way is what I am doing. I was not clear sorry but I meant that the VM only contains one collection and I fill it exactly like that. You are right I know the properties of the single items but I am using auto mapping to avoid the hassle of creating a template for them. If that is the only way to go it is fine, but I was wondering if ko or mapping (or JavaScript alone) provided some kind of way of "newing" an object from previously existing ones (maybe exploring the metadata? Cloning?).Friede

© 2022 - 2024 — McMap. All rights reserved.