Skip to content

Instantly share code, notes, and snippets.

@mxriverlynn
Created November 26, 2012 21:45
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save mxriverlynn/4150820 to your computer and use it in GitHub Desktop.
Save mxriverlynn/4150820 to your computer and use it in GitHub Desktop.
A backbone / marionette state machine for wizard / workflow
MyApp.module('MyApp.SomeBuilder', function(SomeBuilder, App, Backbone, Marionette, $, _){
'use strict';
// Controller
// ----------
SomeBuilder.Controller = Marionette.Controller.extend({
initialize: function(options){
this.navbarRegion = options.navbarRegion;
this.mainRegion = options.mainRegion;
var workflow = this._initWorkflow();
this._setupNavigation(workflow, this.navbarRegion, this.footerRegion){
},
showPool: function(pool){
// create view, show in a region
},
selectShape: function(pool){
// create view, show in a region
},
selectCorners: function(pool){
// create view, show in a region
},
selectHopper: function(pool){
// create view, show in a region
},
selectDimensions: function(pool){
// create view, show in a region
},
selectSteps: function(pool){
// create view, show in a region
},
selectDetail: function(pool){
// create view, show in a region
},
_initWorkflow: function(pool){
this.layout = new SomeLayout();
App.someRegion.show(this.layout);
this.workflow = this._buildWorkflow(pool);
},
_buildWorkflow: function(pool){
var workflow = new App.MyWorkflow.Controller();
workflow.addStep('', this.showPool, this);
workflow.addStep('shape', this.selectShape, this);
workflow.addStep('hopper', this.selectHopper, this);
workflow.addStep('corners', this.selectCorners, this);
workflow.addStep('dimensions', this.selectDimensions, this);
workflow.addStep('steps', this.selectSteps, this);
workflow.addStep('detail', this.selectDetail, this);
// always do this before moving to the next step
workflow.beforeMove(function(done){
this._saveIt(done);
}, this);
return workflow;
},
_saveIt: function(done){
this.myModel.save(null, {
success: function(){
done();
}
});
},
_setupNavigation: function(pool, workflow, navbarRegion, footerRegion){
var nav = new SomeBuilder.Navigation.Controller({
workflow: workflow,
navbarRegion: navbarRegion,
footernavRegion: footerRegion
});
return nav;
}
});
});
MyApp.module("MyWorkflow", function(Workflow, App, Backbone, Marionette, $, _){
// Workflow Steps
// ----------------
Workflow.WorkflowStep = Backbone.Model.extend({
initialize: function(){
var selectable = new Backbone.Picky.Selectable(this);
_.extend(this, selectable);
}
});
Workflow.WorkflowStepCollection = Backbone.Collection.extend({
model: Workflow.WorkflowStep,
initialize: function(){
this.emptyStep = new this.model({isEmpty: true});
var selectable = new Backbone.Picky.SingleSelect(this);
_.extend(this, selectable);
},
getNext: function(){
var index, nextIndex;
if (!this.selected){
index = 0;
} else {
index = this.indexOf(this.selected);
}
if (index < this.length){
nextIndex = index + 1;
} else {
nextIndex = -1;
}
return this.at(nextIndex) || this.emptyStep;
},
getPrevious: function(){
var index, nextIndex;
if (!this.selected){
index = 0;
} else {
index = this.indexOf(this.selected);
}
if (index > 0){
nextIndex = index - 1;
} else {
nextIndex = -1;
}
return this.at(nextIndex) || this.emptyStep;
}
});
// Raw step data
// -------------
var workflowStepData = [
{
id: 1,
key: "",
name: "Info"
},
{
id: 2,
key: "shape",
name: "Shape"
},
{
id: 3,
key: "corners",
name: "Corners"
},
{
id: 4,
key: "hopper",
name: "Hopper"
},
{
id: 5,
key: "dimensions",
name: "Dimensions"
},
{
id: 6,
key: "steps",
name: "Steps"
},
{
id: 7,
key: "detail",
name: "Detail"
}
];
// Workflow Controller
// -------------------
Workflow.Controller = Marionette.Controller.extend({
initialize: function(options){
this.steps = new Workflow.WorkflowStepCollection(workflowStepData);
this.filters = {};
},
getSteps: function(){
return this.steps;
},
nextStep: function(){
var nextStep = this.steps.getNext();
this.moveTo(nextStep);
},
previousStep: function(){
var previousStep = this.steps.getPrevious();
this.moveTo(previousStep);
},
moveTo: function(step){
this._triggerStep(step);
},
addStep: function(stepKey, handler, context){
this.bindTo(this, "step:" + stepKey, handler, context);
},
beforeMove: function(fn, context){
this.filters.beforeMove = {fn: fn, context: context};
},
setStepByKey: function(stepKey){
stepKey = stepKey || "";
var step = this.steps.where({key: stepKey})[0];
if (step){
step.select();
}
},
_triggerStep: function(step){
var key = step.get("key");
var done = _.bind(function(){
step.select();
this.trigger("step:" + key);
}, this);
var beforeMove = this.filters.beforeMove;
if (_.isObject(beforeMove)){
beforeMove.fn.call(beforeMove.context, done);
} else {
done();
}
}
});
});
@scancel
Copy link

scancel commented May 13, 2013

Hi Derick, first off all, thanks for the gist, it's very helpful :)
I think you have a mistake in "app_steps.js" on line 12: the call is missing the pool argument, or I'm not getting this at all :
I wanted to know if you have any implemented code with this pattern that I can take a look on to get a better grasp on how it's used.

Thanks,
scancel.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment