0

I am working on a prototype questionnaire system. To ask customers questions on the telephone.

Using HTML, JS, KO (and Dynamic CRM as a datasource).

Note that I'm new to KO as this is day 2! - I am impressed, thought it seems to have an EVE-Online learning curve!

The questions are stored in Dynamics CRM and retrieved with OData call (returning JSON).

Some questions have child questions, so I de-normalise the original normalised OData result and setup the hierarchy in a new JSON object. Debugging over this object shows its all represented as expected. Tree like.

The problem I have is, I get an when i try to use the view to a certain depth. "Error: Unable to get property 'Value' of undefined or null reference"

I looked at this article Knockout.js Make every nested object an Observable, which seemed to be related to my problem, but retro fitting that solution into my code didnt work for me, i may have misunderstood my problem!

My view shows the first level of questions correctly, but the child questions only work up to a point. So when i try and databind this data-bind="value: adr_nextquestionyesid().adr_answertype.Value i get the null reference error shown above.

However, if I data bind this data-bind="value: adr_nextquestionyesid().adr_answertype i see "[Object object]" in the view. This to me suggests that it should be able to get the int value of adr_answertype!

The top level adr_answertype.Value bindings work fine, it just seems to be an issue as I get deeper into the object.

Here is My ViewModel & Model;

Note that the Next Questions are meant to be (and are in the JSON) actually Nested questions within the question, which as you can imagine could go arbitrarily deep

function QuestionAnswersVM() {
   var self = this;

   var questionDataRaw = RetrieveQuestionSet("15AF22A6-3E58-E411-B2A6-00155DFD8300", odataPath);

   var questionDataOrganised = OrganiseQuestionHierarchy(questionDataRaw);

   self.questions = ko.mapping.fromJS(questionDataOrganised, mapping);
};

var mapping = {
    create: function (options) {
        return new Question(options.data);
    }
};

function Question(data) {
   ko.mapping.fromJS(data, {}, this);

   // Answers
   this.DateTime = ko.observable(data.DateTime);
   this.FreeText = ko.observable(data.FreeText)
   this.YesNo = ko.observable(data.Yes);

   // Next questions - trying to create nested question here
   this.adr_nextquestionyesid = ko.observable(new Question(data.adr_nextquestionyesid));
}

Here is my View (with unrelated stuff removed);

particularly note the databindings within QuesionBoxIndent div which present the problem

<section data-bind="foreach: questions">
<div class="questionBox">
    <span class="questionTitle"><span data-bind="text: adr_name"></span></span>
    <div class="questionBody">
        <label data-bind="text: adr_questionbody" />
    </div>
    <div data-bind="visible: adr_answertype().Value == 1">
        <input data-bind="checked: YesNo" type="radio" value="true" />Yes
        <input data-bind="checked: YesNo" type="radio" value="false" />No
        <label class="mandatory" data-bind="visible: $data.adr_mandatory()"><b>*</b></label>
    </div>
    <div data-bind="visible: adr_answertype().Value == 2">
        <input data-bind="value: FreeText" type="text" name="fname" />
        <label class="mandatory" data-bind="visible: $data.adr_mandatory()"><b>*</b></label>
    </div>
</div>
<div class="questionBoxIndent" data-bind="visible: adr_nextquestionyesid().adr_name != null">
    <span class="questionTitle"><span data-bind="text: adr_nextquestionyesid().adr_name"></span></span>
    <div class="questionBody">
        <label data-bind="text: adr_nextquestionyesid().adr_questionbody" />
    </div>
    <div>
        <input data-bind="value: adr_nextquestionyesid().FreeText" type="text" name="fname" />
        <input data-bind="value: adr_nextquestionyesid().adr_answertype" type="text" name="fname" />
        <input data-bind="value: adr_nextquestionyesid().adr_answertype.Value" type="text" name="fname" />
        <label class="mandatory" data-bind="visible: adr_nextquestionyesid().adr_mandatory"><b>*</b></label>
    </div>
</div>
<br />

Edit: Having spent another day on it, i thought i had cracked it using the suggestion of Sergiu, to use the mapping plugin. I got that to map eventually, but seemingly it doesnt expose the depth which i thought it would. Basically, when trying to access the value on the adr_answertype of the nested question, i get the same "Error: Unable to get property 'Value' of undefined or null reference". I tried all sorts of variations of view syntax to get access to the Value. Even though its definitely there when looking at the original mapped object with a debugger. Maybe I should look to build the model completely manually, that will be a separate stream of research, my reading only shows me how to declare the top level model!

Many thanks in advance all!

Peace

Community
  • 1
  • 1
Didge
  • 3
  • 2
  • Why are you sometimes using `adr_answertype().Value` and other times `adr_answertype.Value`? You do know how an observable works, right? – Sergiu Paraschiv Oct 22 '14 at 09:45
  • The last block of input controls just had various syntax to see if I had something wrong. My knowledge of knockout is patchy as its only day 2, having ran though a number of tutorials and such. The last div (the one without a databind) would be akin to the ones in the first QuestionBox where they would be databound with data-bind="visible: adr_nextquestionyesid().adr_answertype.Value == 1 for example – Didge Oct 22 '14 at 10:20
  • You access an observable's value like this: `someObservable()`. Next you have a `Value` property. If that's not an observable (you don't seem to be accessing it's value by calling the observable (`Value()`) then how is knockout supposed to know it changed? Looks to me you need some sort of _deep observables_ OR manually map that data structure to an observable one. – Sergiu Paraschiv Oct 22 '14 at 10:51
  • Thanks Sergiu, the deep observables was where my research was leading. Hence looking into http://stackoverflow.com/questions/10555115/knockout-js-make-every-nested-object-an-observable ... Ill keep the question live until I crack it or someone posts a definitive answer. Cheers – Didge Oct 22 '14 at 11:25
  • I'd suggest the mapping plugin. – Sergiu Paraschiv Oct 22 '14 at 11:26
  • Instead of pasting so many code, I'd suggest you to provide a JSFiddle. – Rafael Eyng Oct 23 '14 at 12:07

0 Answers0