How to initialize a Knockout viewmodel when initial viewmodel load is empty
Asked Answered
C

2

7

I am using Knockout to implement a course list selection tool. I am using the approach below to populate the data (MVC3/Razor), so that when the viewmodel is initially populated, I have no issues working with each KO array (i.e. CourseList, ScheduleList). However, when the initial load from the server returns zero rows, meaning that the viewmodel 'ScheduleList' property is empty, then it's not possible to call any methods such as .push() or .removeAll(). Presumably this means that the observable array was never created since there was nothing to fill it with. When the model is filled, the ScheduleList property is populated with a List. What is the best way to instantiate it when the MVC action returns it as empty? There is a jsFiddle that seems to address it, but when I try to use the 'create' option, it renders my entire model blank. I am not sure what the syntax is of the 'create' option. The jsFiddle is here: http://jsfiddle.net/rniemeyer/WQGVC/

// Get the data from the server
var DataFromServer = @Html.Raw(Json.Encode(Model));     

// Data property in viewmodel
var data = {
    "CourseList": DataFromServer.CourseList ,
    "ScheduleList": DataFromServer.ScheduleList
    };

$(function() {
    // Populate Data property
    viewModel.Data = ko.mapping.fromJS(data);   

    // ko.applyBindings(viewModel, mappingOptions);             
    ko.applyBindings(viewModel);
});

When the initial page load does not populate ScheduleList, then the following code throws an error. If the initial page load contained data, then you could call .removeAll() and .push() etc.

var oneA= 'abc';

// push not working                
this.Data.ScheduleList.push( oneA );
Cleasta answered 19/10, 2012 at 20:58 Comment(0)
C
9

Set up your mapping parameters to make it so on creation, you give it a certain structure. Then it will do the updates for you.

What is most likely happening is that your DataFromServer doesn't actually contain a ScheduleList property at all. So when it is mapped, a corresponding property is never made. The mapper will only map existing properties to observables.

You need to set in your create options for the view model to add empty arrays when either array is not set. That way, your view model will end up with corresponding observable arrays in place.

By ensuring that CourseList or ScheduleList is an array, the mapped view model will map them as observableArray objects so your code will work as you expected.

var DataFromServer = {
    'CourseList': [1,2,3]
    //, 'ScheduleList': []
};

var dataMappingOptions = {
    'create': function (options) {
        var data = options.data;
        data.CourseList = data.CourseList || [];
        data.ScheduleList = data.ScheduleList || [];
        return ko.mapping.fromJS(data);
    }
};

viewModel.Data = ko.mapping.fromJS(DataFromServer, dataMappingOptions);
Cotopaxi answered 20/10, 2012 at 1:54 Comment(1)
Thanks! This is exactly what is happening and this solves the problem. Interesting that most of the jsFiddles I looked at were acting on the property (array itself), so I had not tried this create callback. The other approach suggested by Ray works as well.Cleasta
J
1
var data = {
    CourseList: DataFromServer.CourseList || ko.observableArray([]) ,
    ScheduleList: DataFromServer.ScheduleList  || ko.observableArray([])
    };
Jaimiejain answered 19/10, 2012 at 23:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.