1

I am prototyping a project and have a bunch of messy code. The particular part of this prototype I'm stuck on is being unable to iterate and render nested array from a JSON file using knockout. Here are some facts:

  • The value of a specific key that is the issue is a list.
  • Each item I'm referring to in the JSON "response" is "questions" and "answers"
    • Questions are just a string
    • Answers are the list
  • Im breaking each item in "answer" down and insert into a radio button in html
  • When knockout does its thing, each of the items is being returned all together through each iter of the radio button rendering.
  • qna obj is from a function that is filtering an input by calling the json file based on "sub", removing the top node an leaving only

Here is what this case is looking like [Sorry, not enough rep points to embed images, tinypic will have to do!]

http://i64.tinypic.com/25i26xl.png

I'm brand new to using knockout js, I have read the official docs but admittedly I'm having a bit of trouble understanding it

Heres the code lines question, any suggestions would be appreciated.

//questions.json
[
      {
        "question": "Are you a buyer, seller or renter?",
        "answers": ["Buyer", "Seller", "Renter"]
      },
      {
        "question": "Second question",
        "answers": ["yes", "no"]
      }
   ]

1

// functions.js
function questionDisplay() {
  var self = this;
  self.data = ko.observableArray(qna);
}

function initQuestionDisplay() {
  var qnaDataModel = new questionDisplay();
  $("#questionsDisplay").modal("show");
  ko.applyBindings(qnaDataModel);
}

2

<!-- test.html --!>
<div
            class="modal-body"
            data-bind="template: {name: 'template', data: $data}"
          ></div>
          <script type="text/html" id="template">
            <div class="diplay-frame" data-bind="foreach: {data: data, as: '_data'}">
              <div class="question-box">
                  <h2 class="question" data-bind="text: _data['question']"/>
                  <p class="answers" data-bind="text: _data['answers']"/>
                  <div data-bind="foreach: _data['answers']">
                      <input type="radio" name="optionsGroup" data-bind="checked: _data['answers']" />
                      <span data-bind="text: _data['answers']"></span>
                  </div>

1 Answer 1

1

You were so close. A couple of things to note though.

  • Use a closing tag where possible like <p data-bind="text: item"></p> over self closing tags like this <p data-bind="text: item" /> when you want to bind things with knockoutjs. The exception to the rule are those tags that are self closing like <input data-bind="value: somevalue" />. This will eliminate a whole series of bugs. I have been caught out with this many times.
  • Radio buttons need to be grouped by the name so that the selected value only applies to the named group of radio buttons. this will limit the selected answer to just the question the answers relate to and not all answers rendered on the page.

var vm = {arrayOfItems : [ 'item1','item2']}
<ul data-bind="foreach: arrayOfItems"> <li data-bind="text: $data"></li> </ul>

  • When you are inside the foreach bound tags you can use the current iteration data-context called $data to reference the current item.
    A better explanation can be found here. - Knockout Binding Context

I have included a working example using what you have provided.

There is a "learn" section where it takes you through a pretty comprehensive tutorial on using Knockoutjs.

var qna = [
      {
        "questionId": "1",
        "question": "Are you a buyer, seller or renter?",
        "answers": ["Buyer", "Seller", "Renter"]
      },
      {
        "questionId": "2",
        "question": "Second question",
        "answers": ["yes", "no"]
      }
   ];
   
// functions.js
function questionDisplay() {
  var self = this;
  var mappedData = qna.map(function(item){
    item.optionGroupName = "optionGroup_" + item.questionId;
    item.selectedAnswer = ko.observable();
    return item;
  });
  self.data = ko.observableArray(mappedData);
}

function initQuestionDisplay() {
  var qnaDataModel = new questionDisplay();
  //$("#questionsDisplay").modal("show");
  ko.applyBindings(qnaDataModel);
}   

initQuestionDisplay();
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div class="modal-body" data-bind="template: {name: 'template', data: $data}"></div>
<script type="text/html" id="template">
<div class="diplay-frame" data-bind="foreach: {data: data, as: '_data'}">
  <div class="question-box">
    <h2 class="question" data-bind="text: _data['question']"></h2>
    <p class="answers" data-bind="text: _data['answers']"></p>
    <div data-bind="foreach: {data: _data['answers'], as: 'answer'}">
      <input type="radio"  data-bind="checked: $parent.selectedAnswer, attr:{name: $parent.optionGroupName, value: $data}" />
      <span data-bind="text: answer"></span>
    </div>
  </div>
</div>
</script>

<h2>Results</h2>
<ul data-bind="foreach: data">
  <li>
    Question: <span data-bind="text: question"></span>
    Answer: <span data-bind="text: selectedAnswer"></span>
  </li>
</ul>

Sign up to request clarification or add additional context in comments.

1 Comment

I appreciate your notes! Very concise! I will upvote the second my reputation goes up to 15 lol Thanks again.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.