我是backbone.js的新手,并且遇到一些问题,我正在设计一个"向导"类型的过程(也就是多步骤形式).该向导应该能够处理不同的屏幕分支逻辑,具体取决于用户对问题的响应,在用户进行时将响应存储到每个屏幕,最后能够将所有表单响应(每个步骤)序列化为一个大对象(可能是JSON).向导问题每年都在变化,我需要能够同时支持多个类似的向导.
我已经掌握了创建屏幕的基础知识(使用骨干形式),但现在我想要保存用户输入,我想不出最好的方法.我见过的大多数示例都有一个特定类型的对象(例如Todo
),你只是创建它们的集合(例如TodoList
),但由于屏幕类型不同,我有一个混合的Backbone.Model定义包,所以它没有看起来很简单.有关如何实例化我的向导及其包含的屏幕以及记录用户响应的任何指示?
如果它有帮助我可以发布一个jsfiddle与我的视图代码,到目前为止只前进和后退屏幕(没有用户输入响应记录或屏幕分支).
var Wizard = Backbone.Model.extend({
initialize: function(options) {
this.set({
pathTaken: [0]
});
},
currentScreenID: function() {
return _(this.get('pathTaken')).last();
},
currentScreen: function() {
return this.screens[this.currentScreenID()];
},
isFirstScreen: function(screen) {
return (_(this.screens).first() == this.currentScreen());
},
// This function should be overridden by wizards that have
// multiple possible "finish" screens (depending on path taken)
isLastScreen: function(screen) {
return (_(this.screens).last() == this.currentScreen());
},
// This function should be overridden by non-trivial wizards
// for complex path handling logic
nextScreen: function() {
// return immediately if on final screen
if (this.isLastScreen(this.currentScreen())) return;
// otherwise return the next screen in the list
this.get('pathTaken').push(this.currentScreenID() + 1);
return this.currentScreen();
},
prevScreen: function() {
// return immediately if on first screen
if (this.isFirstScreen(this.currentScreen())) return;
// otherwise return the previous screen in the list
prevScreenID = _(_(this.get('pathTaken')).pop()).last();
return this.screens[prevScreenID];
}
});
var ChocolateWizard = Wizard.extend({
nextScreen: function() {
//TODO: Implement this (calls super for now)
// Should go from screen 0 to 1 OR 2, depending on user response
return Wizard.prototype.nextScreen.call(this);
},
screens: [
// 0
Backbone.Model.extend({
title : "Chocolate quiz",
schema: {
name: 'Text',
likesChocolate: {
title: 'Do you like chocolate?',
type: 'Radio',
options: ['Yes', 'No']
}
}
}),
// 1
Backbone.Model.extend({
title : "I'm glad you like chocolate!",
schema: {
chocolateTypePreference: {
title: 'Which type do you prefer?',
type: 'Radio',
options: [ 'Milk chocolate', 'Dark chocolate' ]
}
}
}),
//2
Backbone.Model.extend({
title : "So you don't like chocolate.",
schema: {
otherSweet: {
title: 'What type of sweet do you prefer then?',
type: 'Text'
}
}
})
]
});
wizard = new ChocolateWizard();
// I'd like to do something like wizard.screens.fetch here to get
// any saved responses, but wizard.screens is an array of model
// *definitions*, and I need to be working with *instances* in
// order to fetch
Run Code Online (Sandbox Code Playgroud)
编辑:根据要求,我希望看到一个向导的JSON返回值,该值已保存为类似这样的内容(作为最终目标):
wizardResponse = {
userID: 1,
wizardType: "Chocolate",
screenResponses: [
{ likesChocolate: "No"},
{},
{ otherSweet: "Vanilla ice cream" }
]
}
Run Code Online (Sandbox Code Playgroud)
Der*_*ley 14
您需要做的最重要的事情是将工作流与视图本身分开.也就是说,您应该有一个对象来协调视图之间的工作流程,保持输入到视图中的数据,并使用视图的结果(通过事件或其他方式)来确定去哪里下一个.
我在博客中更详细地介绍了这一点,其中有一个非常简单的向导式界面示例,在这里:
和这里:
这是第一篇文章的基本代码,它显示了工作流对象及其如何协调视图:
orgChart = {
addNewEmployee: function(){
var that = this;
var employeeDetail = this.getEmployeeDetail();
employeeDetail.on("complete", function(employee){
var managerSelector = that.selectManager(employee);
managerSelector.on("save", function(employee){
employee.save();
});
});
},
getEmployeeDetail: function(){
var form = new EmployeeDetailForm();
form.render();
$("#wizard").html(form.el);
return form;
},
selectManager: function(employee){
var form = new SelectManagerForm({
model: employee
});
form.render();
$("#wizard").html(form.el);
return form;
}
}
// implementation details for EmployeeDetailForm go here
// implementation details for SelectManagerForm go here
// implementation details for Employee model go here
Run Code Online (Sandbox Code Playgroud)
我认为Derick的答案是被接受的,因为它比我的更清洁,但它不是我可以在我的情况下使用的解决方案,因为我有50多个屏幕来处理我不能闯入漂亮的模型 - 我已被给予他们的内容,只需要复制它们.
这是我提出的处理屏幕切换逻辑的hacky模型代码.我确信在我继续努力的时候,我最终会重构它.
var Wizard = Backbone.Model.extend({
initialize: function(options) {
this.set({
pathTaken: [0],
// instantiate the screen definitions as screenResponses
screenResponses: _(this.screens).map(function(s){ return new s; })
});
},
currentScreenID: function() {
return _(this.get('pathTaken')).last();
},
currentScreen: function() {
return this.screens[this.currentScreenID()];
},
isFirstScreen: function(screen) {
screen = screen || this.currentScreen();
return (_(this.screens).first() === screen);
},
// This function should be overridden by wizards that have
// multiple possible "finish" screens (depending on path taken)
isLastScreen: function(screen) {
screen = screen || this.currentScreen();
return (_(this.screens).last() === screen);
},
// This function should be overridden by non-trivial wizards
// for complex path handling logic
nextScreenID: function(currentScreenID, currentScreen) {
// default behavior is to return the next screen ID in the list
return currentScreenID + 1;
},
nextScreen: function() {
// return immediately if on final screen
if (this.isLastScreen()) return;
// otherwise get next screen id from nextScreenID function
nsid = this.nextScreenID(this.currentScreenID(), this.currentScreen());
if (nsid) {
this.get('pathTaken').push(nsid);
return nsid;
}
},
prevScreen: function() {
// return immediately if on first screen
if (this.isFirstScreen()) return;
// otherwise return the previous screen in the list
prevScreenID = _(_(this.get('pathTaken')).pop()).last();
return this.screens[prevScreenID];
}
});
var ChocolateWizard = Wizard.extend({
initialize: function(options) {
Wizard.prototype.initialize.call(this); // super()
this.set({
wizardType: 'Chocolate',
});
},
nextScreenID: function(csid, cs) {
var resp = this.screenResponses(csid);
this.nextScreenController.setState(csid.toString()); // have to manually change states
if (resp.nextScreenID)
// if screen defines a custom nextScreenID method, use it
return resp.nextScreenID(resp, this.get('screenResponses'));
else
// otherwise return next screen number by default
return csid + 1;
},
// convenience function
screenResponses: function(i) {
return this.get('screenResponses')[i];
},
screens: [
// 0
Backbone.Model.extend({
title : "Chocolate quiz",
schema: {
name: 'Text',
likesChocolate: {
title: 'Do you like chocolate?',
type: 'Radio',
options: ['Yes', 'No']
}
},
nextScreenID: function(thisResp, allResp) {
if (thisResp.get('likesChocolate') === 'Yes')
return 1;
else
return 2;
}
}),
// 1
Backbone.Model.extend({
title : "I'm glad you like chocolate!",
schema: {
chocolateTypePreference: {
title: 'Which type do you prefer?',
type: 'Radio',
options: [ 'Milk chocolate', 'Dark chocolate' ]
}
},
nextScreenID: function(thisResp, allResp) {
return 3; // skip screen 2
}
}),
// 2
Backbone.Model.extend({
title : "So you don't like chocolate.",
schema: {
otherSweet: {
title: 'What type of sweet do you prefer then?',
type: 'Text'
}
}
}),
// 3
Backbone.Model.extend({
title : "Finished - thanks for taking the quiz!"
}
]
});
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
6825 次 |
最近记录: |