2013-01-05 3 views
6

Tôi đang thiết kế một bài kiểm tra với Knockoutjs. Tôi chỉ muốn hiển thị một câu hỏi tại một thời điểm. Tôi đã suy nghĩ về một var đếm toàn cầu được thêm vào bởi mỗi lần một câu hỏi được trả lời. Tuy nhiên tôi dường như không thể tìm ra cách để chỉ có một câu hỏi vào lúc đó. Làm cách nào để tôi tiếp cận tốt nhất điều này? Tôi mới đến Knockoutjs. Tôi đã thử các câu hỏi() [số] nhưng điều đó dường như không hiệu quả.Nhận đối tượng từ observableArray theo chỉ mục

Cảm ơn

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} 
     ] 
    } 
] 

}

Trả lời

8

Jsfiddle đang rất hài hước nên tôi không thể đăng mã ở đó, nhưng nó khá đơn giản: bạn đã gần đến rồi! Đây là một cú đẩy nhỏ. Trước tiên, không thực sự tốt để gọi các phương thức trong đánh dấu trừ khi cần thiết, và đưa ra các tham số cho hàm có thể có nghĩa là bạn có thể sử dụng một quan sát cho điều đó. Trong trường hợp này:

<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> 

Như bạn có thể thấy, trong đánh dấu, tôi chỉ đặt biến, không gọi nó ra ở dạng filteredQuestions. Bởi bây giờ bạn có thể hỏi, "Có chuyện gì với điều đó? Tôi chỉ muốn hiển thị một câu hỏi, không phải tất cả chúng. Và điều gì đã xảy ra với điều được lọc?"

Đây là những gì tôi đang thực hiện: thay vì hiển thị mảng câu hỏi ban đầu, tôi hiển thị một mảng được lọc, được cập nhật mỗi lần mọi quan sát bên trong nó thay đổi. Đây là mã cho các câu hỏi được lọc.

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; 
    }); 
}); 

Nếu bạn chú ý đến các mã, tôi chỉ trở về một mảng với chỉ có một câu hỏi, một trong đó Id là giống như một thiết lập bởi các biến currentQuestionId. Đây là một cách tiếp cận, mặc dù có rất nhiều: một cách tốt khác có một quan sát được gọi là selectedQuestion giá trị nào là câu hỏi đầu tiên; sau đó trong đánh dấu, sử dụng ràng buộc dữ liệu with:selectedQuestion. Nhưng chúng ta hãy gắn bó với cái này, bạn có thể đoán $parent.nextQuestion làm gì?

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

tiên, chúng ta sử dụng từ khóa $parent để duyệt phụ huynh phạm vi thực tế, kể từ khi chúng tôi đang trong con question vì tuyên bố foreach. Chức năng này hoạt động vì các yêu cầu của bạn, nhưng có thể có một số cạm bẫy ở đó (chúng ta làm gì trong câu hỏi cuối cùng? Nếu id không tăng số lượng?). Đó là khi cách tiếp cận khác có thể hữu ích hơn, trong trường hợp này bạn sẽ có một cái gì đó như thế này:

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

Vẻ đẹp của cách tiếp cận cuối cùng? Vâng, backQuestion khá dễ dàng!

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

(Bạn rõ ràng là cần kiểm tra lỗi loại IndexOutOfBounds). Trong phương pháp cuối cùng này, đừng quên rằng vì chúng ta đang ở trong một phần tử chuyển đổi ngữ cảnh như foreach, chúng ta nhận được câu hỏi hiện tại như một tham số. Hi vọng điêu nay co ich.

+0

Cảm ơn bạn đã trả lời! Tôi thực sự đánh giá cao bạn đã dành thời gian để giải thích điều này. Tôi đã thử nó nhưng không có gì đáng buồn được hiển thị. Có điều gì khác tôi đang làm sai? – MrJM

+1

Tôi đã thực hiện một vài lỗi trong mã của mình, đó là 'self.questions()', không phải câu hỏi, và trong câu hỏi tiếp theo bạn cần một parseInt như 'self.currentQuestionId (parseInt (currentQuestionId, 10) + 1);'; đây là codepen - http://codepen.io/jjperezaguinaga/full/jIytF – jjperezaguinaga

+0

Awsome nó hoạt động, cảm ơn rất nhiều! – MrJM

1

Here là giải pháp hoạt động.

Mã sử ​​dụng mapping plugin để đơn giản hóa việc tạo mô hình chế độ xem.
các quan sát "questionIndex" và "currentQuestion" theo dõi câu hỏi hiện tại.

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"); 
}; 
} 

Markup:

<!-- 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 --> 

Ở đây tôi sử dụng yếu tố ảo <!-- ko --> <!-- /ko --> để giới thiệu bối cảnh mong muốn.

+0

Cảm ơn, tôi sẽ xem xét nó. – MrJM