2013-01-05 10 views
6

私はKnockoutjsでクイズをデザインしています。私は一度に一つの質問しか表示しません。私は質問に答えられるたびに1ずつ追加されるグローバルカウントvarについて考えていました。しかし、私はその時に一つの質問だけを得る方法を見つけることができないようです。 これに最も接近するにはどうすればよいですか?私はKnockoutjsの新人です。私は質問をしましたが、それはうまくいかないようです。observableArrayからオブジェクトをインデックスで取得する

おかげ

JS

$(document).ready(function(){ 
function Answer(data) { 
    this.answer = ko.observable(data.answer); 
    this.correct = ko.observable(data.correct); 
} 

function Question(data){ 
    this.id = ko.observable(data.id); 
    this.question = ko.observable(data.question); 
    this.answers = ko.observableArray([]); 
    var mappedAnswers = $.map(data.answers, function(item) {return new Answer(item) }); 
    this.answers(mappedAnswers); 
} 


function AnswerViewModel(){ 
    // Data 
    var self = this; 

    self.questions = ko.observableArray([]); 

    self.checkAnswer = function(item, event){ 
      if (item.juist() == true){ 
       $(event.currentTarget).css("background", "green"); 

      }else{ 
       $(event.currentTarget).css("background", "red"); 
      } 
    } 

    $.getJSON("../res/json/quiz.json", function(allData) { 
     var mappedQuestions = $.map(allData.questions, function(item) {return new Question(item) }); 
     self.questions(mappedQuestions); 
    }); 
} 

ko.applyBindings(AnswerViewModel()); 
}); 

HTML

<h3 data-bind="questions()[1].question"></h3> 
<ul class="list-container" data-bind="foreach: questions()[1].answers"> 
    <li class="list-item"> 
     <a class="list-item-content" href="#" data-bind="text: answer, click:checkAnswer"></a> 
    </li> 
</ul> 
<button>Next question</button> 

JSON

{ 
"questions" : 
[ 
    { 
     "id": 1, 
     "question": "This is the first question", 
     "answers" : 
     [ 
      {"answer": "q1a1", "correct": false}, 
      {"answer": "q1a2", "correct": false} , 
      {"answer": "q1a3", "correct": true}, 
      {"answer": "q1a4", "correct": false} 
     ] 
    }, 
    { 
     "id": 2, 
     "question": "This is the 2nd question", 
     "answers" : 
     [ 
      {"answer": "q2a1", "correct": false}, 
      {"answer": "q2a2", "correct": false} , 
      {"answer": "q2a3", "correct": false}, 
      {"answer": "q2a4", "correct": true} 
     ] 
    } 
] 

}

答えて

8

Jsfiddleは面白いので、私はそこにコードを投稿することはできませんが、それは非常にシンプルです:あなたはほとんどそこにいました!ここに少し押します。

まず、必要な場合を除いて、マークアップ内のメソッドを呼び出すのは、実際には良い習慣ではなく、関数にパラメータを渡すことは、おそらくそれにオブザーバブルを使用できることを意味します。この場合:

<div data-bind="foreach: filteredQuestions"> 
<ul class="list-container" data-bind="foreach: answers"> 
    <li class="list-item"> 
     <a class="list-item-content" href="#" data-bind="text: answer"></a> 
    </li> 

</ul> 
<button data-bind="click: $parent.nextQuestion">Next question</button> 
</div> 

あなたが見ることができるように、マークアップで私が唯一のフォームfilteredQuestionsでそれを呼び出していない、変数を入れています。今では、 "foreachで何が起きているのですか?"という質問だけではなく、すべての質問を表示したいと思っているかもしれません。

ここで私は何をしているのですか?オリジナルの質問配列を表示する代わりに、フィルタリングされたものを表示しています。その中の観測値が変更されるたびに更新されます。次に、filteredQuestionsのコードを示します。

self.currentQuestionId = ko.observable("1"); 
self.filteredQuestions = ko.computed(function() { 
    var currentQuestion = self.currentQuestionId(); 
    return ko.utils.arrayFilter(self.question(), function(question) { 
     return question.id() == currentQuestion; 
    }); 
}); 

あなたがコードに注意を払えば、私は一つだけ質問、IdはcurrentQuestionId変数で設定したものと同じであるものと1つの配列を返しますよ。これは1つのアプローチですが、多くはありますが、他の良いものにはselectedQuestionと呼ばれる観察可能なものがあります。その値は最初の質問です。マークアップでは、データバインディングwith:selectedQuestionを使用します。しかし、これにこだわってみましょう。$parent.nextQuestionは何をしていますか?

self.nextQuestion = function() { 
    var currentQuestionId = self.currentQuestionId(); 
    self.currentQuestionId(currentQuestionId + 1); 
} 

まず、我々は理由foreach声明の子供questionにあるため、実際の範囲の親を閲覧するために$parentキーワードを使用します。この関数はあなたの必要条件のために機能しますが、そこにはいくつかの落とし穴があります(最後の質問では何をしますか?IDが数値的に増加していない場合はどうなりますか?)。それでもう1つのアプローチがもっと役に立つかもしれません。この場合、次のようなものがあります:

self.nextQuestion = function(question) { 
    var index = self.questions().indexOf(question); 
    self.selectedQuestion(self.questions()[index + 1]); 
} 

最後のアプローチの美しさは?まあ、バッククエストはとても簡単です!

self.backQuestion = function(question) { 
    var index = self.questions().indexOf(question); 
    self.selectedQuestion(self.questions()[index - 1]); 
} 

(明らかにIndexOutOfBoundsタイプのエラーをチェックする必要があります)。この最後のアプローチでは、foreachのようなコンテクストスイッチング要素の内部にあるので、現在の質問をパラメータとして受け取ることを忘れないでください。お役に立てれば。

+0

返信いただきありがとうございます!私はあなたにこれをよく説明する時間がかかったことに本当に感謝しています。 私はそれを試しましたが、悲しいことに何も表示されません。私が間違っている何か他にありますか? – MrJM

+1

私は自分のコードでいくつかの間違いを犯しました。疑問ではなく 'self.questions()'です。次の質問では、 'self.currentQuestionId(parseInt(currentQuestionId、10)+ 1);'のようなparseIntが必要です。ここにcodepen - http://codepen.io/jjperezaguinaga/full/jIytF – jjperezaguinaga

+0

それはうまくいく、ありがとう! – MrJM

1

Hereが有効な解決策です。

コードでは、mappingプラグインを使用して、ビューモデルの作成を簡略化しています。
"questionIndex"と "currentQuestion"観測値は現在の質問を追跡します。

function AnswerViewModel(){ 
    var self = this; 
    ko.mapping.fromJS(data, {}, self); 

    this.questionIndex = ko.observable(0); 
    this.currentQuestion = ko.computed(function(){ 
    return self.questions()[self.questionIndex()]; 
    }); 

this.nextQuestion = function(){ 
    var questionIndex = self.questionIndex(); 
    if(self.questions().length - 1 > questionIndex){ 
    self.questionIndex(questionIndex + 1); 
    } 
}; 

this.prevQuestion = function(){ 
    var questionIndex = self.questionIndex(); 
    if(questionIndex > 0){ 
    self.questionIndex(questionIndex - 1); 
    } 
}; 

this.checkAnswer = function(item, event){ 
    $(event.currentTarget).css("background", item.correct() ? "green" : "red"); 
}; 
} 

マークアップ:

<!-- ko with: currentQuestion --> 
<h3 data-bind="text: question"></h3> 
<ul class="list-container" data-bind="foreach: answers"> 
    <li class="list-item"> 
    <a class="list-item-content" href="#" data-bind="text: answer, click:$root.checkAnswer"></a> 
</li> 
</ul> 
<!-- /ko --> 

ここで私は、所望のコンテキストを導入する仮想要素<!-- ko --> <!-- /ko -->を使用しています。

+0

ありがとう、私はそれを見てみましょう。 – MrJM

関連する問題