Created
October 11, 2014 21:47
-
-
Save yousefcisco/db7db210026c08301744 to your computer and use it in GitHub Desktop.
Backbone.stickit handler that allows function as select label
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Backbone.Stickit.addHandler({ | |
selector: 'select', | |
events: ['change'], | |
update: function($el, val, model, options) { | |
var optList, | |
selectConfig = options.selectOptions, | |
list = selectConfig && selectConfig.collection || undefined, | |
isMultiple = $el.prop('multiple'); | |
// If there are no `selectOptions` then we assume that the `<select>` | |
// is pre-rendered and that we need to generate the collection. | |
if (!selectConfig) { | |
selectConfig = {}; | |
var getList = function($el) { | |
return $el.map(function() { | |
return {value:this.value, label:this.text}; | |
}).get(); | |
}; | |
if ($el.find('optgroup').length) { | |
list = {opt_labels:[]}; | |
// Search for options without optgroup | |
if ($el.find('> option').length) { | |
list.opt_labels.push(undefined); | |
_.each($el.find('> option'), function(el) { | |
list[undefined] = getList(Backbone.$(el)); | |
}); | |
} | |
_.each($el.find('optgroup'), function(el) { | |
var label = Backbone.$(el).attr('label'); | |
list.opt_labels.push(label); | |
list[label] = getList(Backbone.$(el).find('option')); | |
}); | |
} else { | |
list = getList($el.find('option')); | |
} | |
} | |
// Fill in default label and path values. | |
selectConfig.valuePath = selectConfig.valuePath || 'value'; | |
selectConfig.labelPath = selectConfig.labelPath || 'label'; | |
var addSelectOptions = function(optList, $el, fieldVal) { | |
_.each(optList, function(obj) { | |
var option = Backbone.$('<option/>'), optionVal = obj; | |
var fillOption = function(text, val) { | |
option.text(text); | |
optionVal = val; | |
// Save the option value as data so that we can reference it later. | |
option.data('stickit_bind_val', optionVal); | |
if (!_.isArray(optionVal) && !_.isObject(optionVal)) option.val(optionVal); | |
}; | |
var text, val; | |
if (obj === '__default__') { | |
text = fieldVal.label, | |
val = fieldVal.value; | |
} else { | |
text = _.isFunction(selectConfig.labelPath) ? selectConfig.labelPath.call(this, obj) : evaluatePath(obj, selectConfig.labelPath), | |
val = evaluatePath(obj, selectConfig.valuePath); | |
} | |
fillOption(text, val); | |
// Determine if this option is selected. | |
var isSelected = function() { | |
if (!isMultiple && optionVal != null && fieldVal != null && optionVal === fieldVal) { | |
return true; | |
} else if (_.isObject(fieldVal) && _.isEqual(optionVal, fieldVal)) { | |
return true; | |
} | |
return false; | |
}; | |
if (isSelected()) { | |
option.prop('selected', true); | |
} else if (isMultiple && _.isArray(fieldVal)) { | |
_.each(fieldVal, function(val) { | |
if (_.isObject(val)) val = evaluatePath(val, selectConfig.valuePath); | |
if (val === optionVal || (_.isObject(val) && _.isEqual(optionVal, val))) | |
option.prop('selected', true); | |
}); | |
} | |
$el.append(option); | |
}); | |
}; | |
$el.find('*').remove(); | |
// The `list` configuration is a function that returns the options list or a string | |
// which represents the path to the list relative to `window` or the view/`this`. | |
if (_.isString(list)) { | |
var context = window; | |
if (list.indexOf('this.') === 0) context = this; | |
list = list.replace(/^[a-z]*\.(.+)$/, '$1'); | |
optList = evaluatePath(context, list); | |
} else if (_.isFunction(list)) { | |
optList = applyViewFn.call(this, list, $el, options); | |
} else { | |
optList = list; | |
} | |
// Support Backbone.Collection and deserialize. | |
if (optList instanceof Backbone.Collection) { | |
var collection = optList; | |
var refreshSelectOptions = function() { | |
var currentVal = getAttr(model, options.observe, options); | |
applyViewFn.call(this, options.update, $el, currentVal, model, options); | |
}; | |
// We need to call this function after unstickit and after an update so we don't end up | |
// with multiple listeners doing the same thing | |
var removeCollectionListeners = function() { | |
collection.off('add remove reset sort', refreshSelectOptions); | |
}; | |
var removeAllListeners = function() { | |
removeCollectionListeners(); | |
collection.off('stickit:selectRefresh'); | |
model.off('stickit:selectRefresh'); | |
}; | |
// Remove previously set event listeners by triggering a custom event | |
collection.trigger('stickit:selectRefresh'); | |
collection.once('stickit:selectRefresh', removeCollectionListeners, this); | |
// Listen to the collection and trigger an update of the select options | |
collection.on('add remove reset sort', refreshSelectOptions, this); | |
// Remove the previous model event listener | |
model.trigger('stickit:selectRefresh'); | |
model.once('stickit:selectRefresh', function() { | |
model.off('stickit:unstuck', removeAllListeners); | |
}); | |
// Remove collection event listeners once this binding is unstuck | |
model.once('stickit:unstuck', removeAllListeners, this); | |
optList = optList.toJSON(); | |
} | |
if (selectConfig.defaultOption) { | |
var option = _.isFunction(selectConfig.defaultOption) ? | |
selectConfig.defaultOption.call(this, $el, options) : | |
selectConfig.defaultOption; | |
addSelectOptions(["__default__"], $el, option); | |
} | |
if (_.isArray(optList)) { | |
addSelectOptions(optList, $el, val); | |
} else if (optList.opt_labels) { | |
// To define a select with optgroups, format selectOptions.collection as an object | |
// with an 'opt_labels' property, as in the following: | |
// | |
// { | |
// 'opt_labels': ['Looney Tunes', 'Three Stooges'], | |
// 'Looney Tunes': [{id: 1, name: 'Bugs Bunny'}, {id: 2, name: 'Donald Duck'}], | |
// 'Three Stooges': [{id: 3, name : 'moe'}, {id: 4, name : 'larry'}, {id: 5, name : 'curly'}] | |
// } | |
// | |
_.each(optList.opt_labels, function(label) { | |
var $group = Backbone.$('<optgroup/>').attr('label', label); | |
addSelectOptions(optList[label], $group, val); | |
$el.append($group); | |
}); | |
// With no 'opt_labels' parameter, the object is assumed to be a simple value-label map. | |
// Pass a selectOptions.comparator to override the default order of alphabetical by label. | |
} else { | |
var opts = [], opt; | |
for (var i in optList) { | |
opt = {}; | |
opt[selectConfig.valuePath] = i; | |
opt[selectConfig.labelPath] = optList[i]; | |
opts.push(opt); | |
} | |
opts = _.sortBy(opts, selectConfig.comparator || selectConfig.labelPath); | |
addSelectOptions(opts, $el, val); | |
} | |
}, | |
getVal: function($el) { | |
var selected = $el.find('option:selected'); | |
if ($el.prop('multiple')) { | |
return _.map(selected, function(el) { | |
return Backbone.$(el).data('stickit_bind_val'); | |
}); | |
} else { | |
return selected.data('stickit_bind_val'); | |
} | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment