这个问题真的没有一个正确的答案。肯定有不止一种方法可以让它工作,这是 Backbonejs 的优点之一:它非常灵活,不会强加给你太多的设计选择。
如果我要开始构建您所描述的内容,我肯定会:
- 一个
Question
模型
- 一个
Questions
集合
- a
QuestionView
用于呈现单个问题
- a
QuestionsIndexView
用于显示问题列表
在那之后事情变得有点模糊,这取决于您对应用程序的要求。如果您希望像传统网站一样存储状态,您可以使用路由器并执行如下所示的操作:
ApplicationRouter = Backbone.Router.extend({
routes: {
"": "index",
"question/:id": "show"
},
index: function() {
// render a QuestionsIndexView...
},
show: function(id) {
// find the q and render a QuestionView...
}
})
这很好,因为在 URL 中维护了状态,因此用户可以使用浏览器的前进和后退按钮,并且事情可能会按照他们的预期工作。问题是,我们应该如何让这些nextQuestion
和previousQuestion
按钮工作?
如果您将它们作为QuestionView
问题的一部分,则必须知道其下一个和上一个问题的 id 是什么。您可能会想出一个方案来完成这项工作,但更优雅和更常用的模式是创建另一个模型,该模型存在于我们已经提到的所有数据模型之上App
,然后创建该模型的QuestionsCollection
和current_question_id
属性。然后,我们将current_question_id
在路由器方法中更新此属性。
现在我们真的在做饭,我们的应用程序状态不仅持久化在浏览器的 URL 中,而且还作为可观察对象存在于应用程序层。我们可以轻松创建一个ButtonsPanelView
通过此App
模型并在单击其按钮时触发正确路由的方法。当用户无法返回或前进时,在模型中实现hasNextQuestion
和使用它或禁用相应的按钮也很简单。hasPreviousQuestion
App
toggle
按要求编辑:
制作一个App
高于一切的模型非常简单。您可能已经在某处有如下代码:
window._qs = new Questions();
window._qs.fetch();
只需这样做:
var qs = new Questions();
window.app = new App({questions: qs});
qs.fetch();
现在Questions
集合是应用模型的一个属性,正如我们想要的那样。那么App
看起来像的定义是什么?同样,有很多方法可以解决这个问题,但我喜欢使用Backbone.Models 的验证来确保我不会陷入糟糕的状态。这是我可能会做的:
App = Backbone.Model.extend({
defaults: {
current_question_index: 0
},
validate: function(attrs) {
if(typeof attrs.current_question_index !== "undefined") {
// we are trying the change the question index,
// so make sure it is valid
var len = this.get("questions").length
if(attrs.current_question_index < 0 || attrs.current_question_index >= len) {
// returning anything prevents invalid set
return "index is out of bounds";
}
}
},
hasNextQ: function() {
var next_ind = this.get("current_question_index") + 1;
// use the logic we already created, in validate() but
// convert it to a bool. NOTE in javascript:
// !"stuff" === false
// !undefined === true
return !this.validate({current_question_index: next_ind});
},
nextQ: function() {
var next_ind = this.get("current_question_index") + 1;
// validate() will prevent us from setting an invalid index
return this.set("current_question_index", next_ind);
}
});