Skip to content

Instantly share code, notes, and snippets.

@mromanoff
Created December 28, 2014 01:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mromanoff/b47a47c2fe18e9483ec0 to your computer and use it in GitHub Desktop.
Save mromanoff/b47a47c2fe18e9483ec0 to your computer and use it in GitHub Desktop.
Backbone-forms dynamic forms
<div class="container">
<h1>Backbone forms</h1>
<p>dynamic fields</p>
</div>
console.clear();
$(function () {
'use strict';
var Field = function (form, editor) {
this.form = form;
this.editor = editor;
};
Field.prototype = {
// actual callbacks
toggleCheckbox: function () {
return (_.isEqual(this.getValue(),this.getCondition())) ? this.showTarget() : this.hideTarget();
},
toggleRadio: function () {
console.log('%ccondition:', 'color: white; background: chocolate', this.getCondition());
console.log('%cvalue:', 'color: white; background: LightCoral ', this.getValue());
var passTest = _.some(this.getCondition(), function(item){
return _.isEqual(item, this.getValue());
}, this);
return (passTest) ? this.showTarget() : this.hideTarget();
},
toggleHelp: function () {
return (_.isEqual(this.getValue(),this.getCondition()[0])) ? this.showHelp() : this.hideHelp();
},
getWaisteToHipRatio: function () {
console.group('getWaisteToHipRatio');
console.log('%cvalues to divide: %o', 'color: lime; background: #444', this.getTarget());
console.groupEnd();
var fields = this.getTarget();
this.form.on(this.getEvents(), function (form, editor) {
var a = +form.fields[fields[0]].getValue();
var b = +form.fields[fields[1]].getValue();
var result = _.isNaN(this.divide(a, b)) ? '' : this.divide(a, b).toFixed(2);
this.editor.setValue(result);
}, this);
},
getSumOfSkinFolds: function () {
console.group('getSumOfSkinFolds');
console.log('%cvalues to sum: %o', 'color: lime; background: #444', this.getTarget());
console.groupEnd();
var fields = this.getTarget();
//TODO do it better with _.values() + _.reduce()
this.form.on(this.getEvents(), function (form, editor) {
var a = +form.fields[fields[0]].getValue();
var b = +form.fields[fields[1]].getValue();
var c = +form.fields[fields[2]].getValue();
var d = +form.fields[fields[3]].getValue();
var sum = _.reduce([a, b, c, d], function (memo, num) {
return memo + num;
}, 0);
var result = _.isNaN(sum) ? '' : sum.toFixed();
this.editor.setValue(result);
}, this);
},
getLeanBodyMass: function () {
console.group('getLeanBodyMass');
console.log('%cCalculation = Body Weight - (Body Weight x Body Fat%) %o', 'color: lime; background: #444', this.getTarget());
console.groupEnd();
var fields = this.getTarget();
//TODO do it better with _.values() + _.reduce()
this.form.on(this.getEvents(), function (form, editor) {
var a = +form.fields[fields[0]].getValue();
var b = +form.fields[fields[1]].getValue();
//Calculation = Body Weight - (Body Weight x Body Fat%) Body Fat = 0.0(5)
var result = this.subtract(a, this.multiply(a, this.divide(b, 100)).toFixed(2));
this.editor.setValue( _.isNaN(result) ? '' : result);
}, this);
},
// helper funcntions
getTarget: function () {
return this.editor.$el.data('target').split(',');
},
getEvents: function() {
return _.map(this.getTarget(), function(field){
return field + ':change';
}).join(' ');
},
getCondition: function () {
return this.editor.$el.data('condition').split(',');
},
getValue: function () {
return this.editor.getValue();
},
hideHelp: function () {
this.editor.$el.find('.' + this.getTarget() + '-block').slideUp(200);
},
showHelp: function () {
this.editor.$el.find('.' + this.getTarget() + '-block').slideDown(200);
},
hideTarget: function () {
var fieldType;
_.each(this.getTarget(), function (fieldName) {
//console.info('filed', fieldName);
//console.log('Editor', this.form.fields[fieldName].editor);
//console.log('Editor schema type', this.form.schema[fieldName].type);
fieldType = this.form.schema[fieldName].type;
switch(fieldType) {
case 'TextArea':
this.form.fields[fieldName].$el.find('textarea').val('');
break;
case 'Radio':
this.form.fields[fieldName].$el.find('[name=' + fieldName +']').prop('checked', false);
break;
default:
return;
};
//this.form.commit();
//console.log('commited', this.form);
this.form.fields[fieldName].$el.hide();
},this);
},
showTarget: function () {
_.each(this.getTarget(), function (field) {
this.form.fields[field].$el.slideDown(200);
}, this);
},
subtract: function (x, y) {
return x - y;
},
multiply: function (x, y) {
return x * y;
},
divide: function (a, b) {
return (b === 0) ? a : (a / b);
},
bind: function (callback) {
this.form.on(this.editor.key + ':change', function () {
this[callback]();
}, this);
},
init: function (callback) {
console.log('callback', callback);
if(_.isFunction(this[callback])) {
this[callback]();
this.bind(callback);
}
}
};
// let's assume this object we get from server
var serverResponse = {
// describe fields
schema: {
medical01: {
type: "Radio",
title: "Do you experience an irregular or racing heart rate during rest or exercise?",
options: [
{
val: "yes",
label: "Yes"
},{
val: "no",
label: "No"
}
],
help: "we recommend that you see a doctor before your Equifit or before you begin an exercise program.",
fieldAttrs: {
"data-bind": "toggleHelp",
"data-target": "help",
"data-condition": "yes"
}
},
'medical01-dynamic': {
type: 'Checkboxes',
title: 'Head or <b>Neck</b>',
fieldAttrs: {
'data-bind': 'toggleCheckbox',
'data-target': 'medical01-dynamic-01,medical01-dynamic-02,medical01-dynamic-03',
'data-condition': "medical13-1"
},
"options": [
{
"val": "medical13-1",
"label": "Hip"
}
]
},
'medical01-dynamic-01': {
type: 'TextArea',
title: 'Area (Left, Right / Upper, Mid, Lower)',
editorAttrs: {maxlength: 100, title: 'Tooltip help'}
},
'medical01-dynamic-02': {
type: 'TextArea',
title: 'When it happened?'
},
'medical01-dynamic-03': {
type: 'Radio',
title: 'Do you drink?',
options: [{
val: 'yes',
label: 'Yes'
}, {
val: 'no',
label: 'No'
}]
},
drink: {
type: 'Radio',
title: 'Do you drink?',
options: [{
val: 'yes',
label: 'Yes'
}, {
val: 'no',
label: 'No'
}],
fieldAttrs: {
'data-bind': 'toggleRadio',
'data-target': 'beer,milk',
'data-condition': 'yes'
}
},
beer: {
type: 'Text',
title: 'only beer',
help: 'good choice'
},
milk: {
type: 'Text',
title: 'goat milk',
help: 'you need help'
},
// three radios
goal: {
type: 'Radio',
title: 'Have you ever achieved this goal in the past?',
options: [{
val: 'yes',
label: 'Yes'
}, {
val: 'no',
label: 'No'
},{
val: 'partialy',
label: 'Partialy'
}],
fieldAttrs: {
'data-bind': 'toggleRadio',
'data-target': 'goalHow',
'data-condition': 'yes'
}
},
goalHow: {
type: 'TextArea',
title: 'if so, when and how?',
editorAttrs: {maxlength: 50}
},
// toggle 5 radio buttons, show extra only for 1,2,3,4 options
goals14: {
type: "Radio",
title: "On a scale of 1 - 5, how committed are you to each goal?",
fieldClass: "bbf-radiobuttons",
fieldAttrs: {
"data-bind": "toggleRadio",
"data-target": "goals14-dynamic1",
"data-condition": "goals14-1,goals14-2,goals14-3,goals14-4"
},
options: [
{
"val": "goals14-1",
"label": "1"
},
{
"val": "goals14-2",
"label": "2"
},
{
"val": "goals14-3",
"label": "3"
},
{
"val": "goals14-4",
"label": "4"
},
{
"val": "goals14-5",
"label": "5"
}
]
},
"goals14-dynamic1": {
"type": "TextArea",
"title": "If you are not a 5 out 5 committed, what would make you a 5?",
"editorAttrs": {"maxlength": 100}
},
waist: {
type: 'Text',
title: 'Waist Circumference',
editorClass: 'input-small',
help: 'inches'
},
hip: {
type: 'Text',
title: 'Hip Circumference',
editorClass: 'input-small',
help: 'inches'
},
waistHipRatio: {
type: 'Text',
title: 'Waist to Hip Ratio',
editorClass: 'input-small',
editorAttrs: {
readonly: true
},
fieldAttrs: {
'data-bind': 'getWaisteToHipRatio',
'data-target': 'waist,hip'
}
},
tricep: {
type: "Text",
title: "tricep",
editorClass: 'input-small',
help: 'mm'
},
abdomen: {
type: "Text",
title: "abdomen",
editorClass: 'input-small',
help: 'mm'
},
suprailiac: {
type: "Text",
title: "suprailiac",
editorClass: 'input-small',
help: 'mm'
},
midThigh: {
type: "Text",
title: "midThigh",
editorClass: 'input-small',
help: 'mm'
},
sumOfSkinFolds: {
type: "Text",
title: "Sum of skin folds",
editorClass: 'input-small',
editorAttrs: {
readonly: true
},
fieldAttrs: {
'data-bind': 'getSumOfSkinFolds',
'data-target': 'tricep,abdomen,suprailiac,midThigh'
}
},
bodyWeight: {
type: "Text",
title: "Weight",
editorClass: 'input-small',
help: 'lbs'
},
bodyFat: {
type: "Text",
title: "Body Fat Percentage",
editorClass: 'input-small',
help: '%'
},
leanBodyMass: {
type: "Text",
title: "Lean Body Mass",
editorClass: 'input-small',
editorAttrs: {
readonly: true
},
help: 'lbs',
fieldAttrs: {
'data-bind': 'getLeanBodyMass',
'data-target': 'bodyWeight,bodyFat'
}
}
},
fieldsets: [{
legend: 'Toggle Help',
fields: [ 'medical01']
},
{
legend: 'Toggle checkbox',
fields: ['medical01-dynamic','medical01-dynamic-01','medical01-dynamic-02', 'medical01-dynamic-03']
},
{
legend: 'Toggle radio Yes/No',
fields: ['drink','milk','beer']
},
{
legend: 'Toggle radio Yes/No/Partialy',
fields: ['goal', 'goalHow']
},
{
legend: 'Toggle radio 1,2,3,4 and 5',
fields: ['goals14', 'goals14-dynamic1']
},
{
legend: 'Body measurements',
fields: ['waist', 'hip', 'waistHipRatio']
},
{
legend: 'Skinfold Measurement',
fields: [
'tricep',
'abdomen',
'suprailiac',
'midThigh',
'sumOfSkinFolds']
},
{
legend: 'Lean Body Mass(lbs)',
fields: [
'bodyWeight',
'bodyFat',
'leanBodyMass']
}
],
// data will be populte these fields
data: {}
};
// these all Front End stuff.
var User = Backbone.Model.extend({
schema: serverResponse.schema,
});
// you can load data in model instance or as a defaults
var user = new User(serverResponse.data);
var form = new Backbone.Form({
model: user,
fieldsets: serverResponse.fieldsets
}).render();
_.each(form.fields, function (editor) {
//console.log('editor', editor);
if (editor.$el.data('bind')) {
var callback = editor.$el.data('bind'),
field = new Field(form, editor);
field.init(callback);
}
}, this);
$('.container').append(form.el);
});
/* custom rules */
ul{
list-style-type: none;
}
[data-target="help"] .help-block {
display: none;
color: #c09853;
padding: 8px 35px 8px 14px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
background-color: #fcf8e3;
border: 1px solid #fbeed5;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
margin-top: 20px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment