Question

I'm trying to bind a complex object on a view using KnockOut.js. Without using overservable() and observableArray() I was able to get the object bind with the view. However when I implemented the observable(), the returned result added up objects of observable in my javascript viewmodel and my view wasn't able to bind the viewmodel.

Here's the code implementation on the server side:

    Model:

    public class SurveyQuestion
    {
        public string QuestionNumber { get; set; }
        public string Question { get; set; }
        public QuestionType QuestionTypeStructure { get; set; }
        public IList OptionsList { get; set; }
    }

    public enum QuestionType
    {
        CheckBox,
        RadioButton,
        TextBox,
        AdvancedCheckBox,
        AdvancedRadioButton
    }

    public class Options
    {
        public string OptionValue { get; set; }
    }

    ViewModel:

    public class SurveyCollection
    {
        public IList SurveyList { get; set; }
    }

The resultant of the above generic results in the json below

    {"SurveyList":[

    {"QuestionNumber":"1","Question":"Write down the second consonant after the second vowel?","QuestionTypeStructure":2,"OptionsList":[{"OptionValue":"F"},{"OptionValue":"G"},{"OptionValue":"C"},{"OptionValue":"B"}]},

    {"QuestionNumber":"2","Question":"Complete the following letter series: ZYX/ VW/ UTS/ QR/ PON/ LM/ KJI?","QuestionTypeStructure":1,"OptionsList":[{"OptionValue":"GB"},{"OptionValue":"IJ"},{"OptionValue":"GH"},{"OptionValue":"LM"}]},

    {"QuestionNumber":"3","Question":"What would be the numrical digits to represent the word VOWEL when the letters of the alphabets were numbered as A 26, B 25, C 24 and o on till Z 1.","QuestionTypeStructure":0,"OptionsList":[{"OptionValue":"5 ,12 ,4, 22, 15"},{"OptionValue":"6 ,12 ,5, 22, 15"},{"OptionValue":"5 ,14 ,4, 22, 16"},{"OptionValue":"4 ,13,4, 22, 12"}]}

    ]}

    @model Test.UI.ViewModel.SurveyCollection
    @using System.Web.Script.Serialization

    [h2]Survey[/h2]

    [div data-bind="template: { name: 'surveyTemplate', foreach: SurveyList }"][/div]

    [script type="text/html" id="surveyTemplate"]
        [div style = "margin-bottom:20px"]
            [div style = "margin-bottom:10px"]
                [strong data-bind="text: Question"][/strong]
            [/div]
            [div]
[ul data-bind="template: { name: function() { return QuestionTypeStructure.toString(); }, foreach: OptionsList }"]
                [ul]
            [/div]
        [/div]
    [/script]

    [script type="text/html" id="0"]
        [div]
            [input type = "radio" style = "margin-right:10px; width: auto"/][span data-bind="text: $data.OptionValue" /]
        [/div]
    [/script]

    [script type="text/html" id="1"]
        [div]
            [input type = "checkbox" style = "margin-right:10px"/][span data-bind="text: $data.OptionValue" /]
        [/div]
    [/script]

    [script type="text/html" id="2"]
        [div]
            [input type = "text" style = "margin-right:10px"/]
            [span data-bind="value: $data.OptionValue" /]
        [/div]
    [/script]


        $(document).ready(function () {
            var surveyViewModel = {
                SurveyList: ko.observableArray([])
            };

            var data = $('').html("@(new JavaScriptSerializer().Serialize(Model))").text();
            var jsonData = $.parseJSON(data);
            if (jsonData != undefined) {
                //$.each(jsonData.SurveyList, function (baseIndex, T) {
                ko.utils.arrayForEach(jsonData.SurveyList, function (T) {
                    var surveyModel = new SurveyModel(T);
                    surveyViewModel.SurveyList.push(surveyModel);

                    ko.utils.arrayForEach(T.OptionsList, function (Q) {
                        var optionModel = new OptionModel(Q);
                        surveyModel.OptionsList.push(optionModel);
                        //surveyViewModel.SurveyList[baseIndex].OptionsList.push(optionModel);
                    });
                });
                ko.applyBindings(surveyViewModel);
           }
    });

    function SurveyModel(T)
    {
        this.QuestionNumber = ko.observable(T.QuestionNumber);
        this.Question = ko.observable(T.Question);
        this.QuestionTypeStructure = ko.observable(T.QuestionTypeStructure);
        this.OptionsList = ko.observableArray([]);
    }

    function OptionModel(Q)
    {
        this.OptionValue = ko.observable(Q.OptionValue);
    }

I apologize for turning html to something else. The problem is the above result turns out in mozilla's error field as:

    Error: Cannot find template with ID function observable() {
            if (arguments.length > 0) {
                // Write

                // Ignore writes if the value hasn't changed
                if ((!observable['equalityComparer']) || !observable['equalityComparer'](_latestValue, arguments[0])) {
                    observable.valueWillMutate();
                    _latestValue = arguments[0];
                    if (DEBUG) observable._latestValue = _latestValue;
                    observable.valueHasMutated();
                }
                return this; // Permits chained assignments
            }
            else {
                // Read
                ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read" operation
                return _latestValue;
            }
        }
    http://localhost:3391/Scripts/knockout-2.1.0.debug.js
    Line 2653

Was it helpful?

Solution

The problem is with [ul data-bind="template: { foreach: OptionsList }"] you're not giving a template name there. Judging from you other templates you want something like this I think:

[ul data-bind="foreach: OptionsList "]
    [li data-bind="template: $data.OptionValue]
    [/li]
[/ul]
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top