From 8c3198fc2a9f92bebd72d537c473d582cf68b5cf Mon Sep 17 00:00:00 2001 From: delambo Date: Thu, 10 Jan 2013 22:03:17 -0500 Subject: [PATCH] handle multiple checkboxes and checkbox value attributes; issue #40 --- README.md | 2 +- backbone.stickit.js | 37 ++++++++++++++++++++++++++++++++----- test/bindData.js | 44 ++++++++++++++++++++++++++++++++++++++++++++ test/index.html | 10 ++++++++++ 4 files changed, 87 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e9af6c7..a251fe6 100644 --- a/README.md +++ b/README.md @@ -262,7 +262,7 @@ The following is a list of the supported form elements, their binding details, a - input[type=number] will update the model with a Number value - `keyup`, `change`, `cut`, and `paste` events are used for handling - input[type=checkbox] - - `checked` property determined by the truthiness of the model attribute or the result of `onGet` + - `checked` property determined by the truthiness of the model attribute or if the checkbox "value" attribute is defined, then its value is used to match against the model. If a binding selector matches multiple checkboxes then it is expected that the observed model attribute will be an array of values to match against the checkbox value attributes. - `change` event is used for handling - input[type=radio] - model attribute value matched to a radio group `value` attribute diff --git a/backbone.stickit.js b/backbone.stickit.js index 70abcd9..e59a50c 100644 --- a/backbone.stickit.js +++ b/backbone.stickit.js @@ -229,10 +229,25 @@ var getElVal = function($el, isHTML) { var val; if (isFormEl($el)) { - if (isCheckbox($el)) val = $el.prop('checked'); - else if (isNumber($el)) val = Number($el.val()); + if (isNumber($el)) val = Number($el.val()); else if (isRadio($el)) val = $el.filter(':checked').val(); - else if (isSelect($el)) { + else if (isCheckbox($el)) { + if ($el.length > 1) { + val = _.reduce($el, function(memo, el) { + if ($(el).prop('checked')) memo.push($(el).val()); + return memo; + }, []); + } else { + val = $el.prop('checked'); + // If the checkbox has a value attribute defined, then + // use that value. Most browsers use "on" as a default. + var boxval = $el.val(); + if (boxval != 'on' && boxval != null) { + if (val) val = $el.val(); + else val = null; + } + } + } else if (isSelect($el)) { if ($el.prop('multiple')) { val = $el.find('option:selected').map(function() { return $(this).data('stickit_bind_val'); @@ -276,8 +291,20 @@ if (!evaluateBoolean(view, config.updateView, val)) return; if (isRadio($el)) $el.filter('[value="'+val+'"]').prop('checked', true); - else if (isCheckbox($el)) $el.prop('checked', !!val); - else if (isInput($el) || isTextarea($el)) $el.val(val); + else if (isCheckbox($el)) { + if ($el.length > 1) { + // There are multiple checkboxes so we need to go through them and check + // any that have value attributes that match what's in the array of `val`s. + val || (val = []); + _.each($el, function(el) { + if (_.indexOf(val, $(el).val()) > -1) $(el).prop('checked', true); + else $(el).prop('checked', false); + }); + } else { + if (_.isBoolean(val)) $el.prop('checked', val); + else $el.prop('checked', val == $el.val()); + } + } else if (isInput($el) || isTextarea($el)) $el.val(val); else if (isContenteditable($el)) $el.html(val); else if (isSelect($el)) { var optList, list = selectConfig.collection, isMultiple = $el.prop('multiple'); diff --git a/test/bindData.js b/test/bindData.js index 9d7cdfe..3a9cd43 100644 --- a/test/bindData.js +++ b/test/bindData.js @@ -1022,4 +1022,48 @@ $(document).ready(function() { equal(model.get('water'), 'dasina'); }); + test('checkbox multiple', function() { + + model.set({'water':['fountain', 'dasina']}); + view.model = model; + view.templateId = 'jst18'; + view.bindings = { + '.boxes': 'water' + }; + $('#qunit-fixture').html(view.render().el); + + equal(view.$('.boxes[value="fountain"]').prop('checked'), true); + equal(view.$('.boxes[value="evian"]').prop('checked'), false); + equal(view.$('.boxes[value="dasina"]').prop('checked'), true); + + model.set('water', ['evian']); + equal(view.$('.boxes[value="fountain"]').prop('checked'), false); + equal(view.$('.boxes[value="evian"]').prop('checked'), true); + equal(view.$('.boxes[value="dasina"]').prop('checked'), false); + + view.$('.boxes[value="dasina"]').prop('checked', true).change(); + equal(model.get('water').length, 2); + equal(_.indexOf(model.get('water'), 'evian') > -1, true); + equal(_.indexOf(model.get('water'), 'dasina') > -1, true); + }); + + test('checkbox (single with value defined)', function() { + + model.set({'water':null}); + view.model = model; + view.templateId = 'jst19'; + view.bindings = { + '.box': 'water' + }; + $('#qunit-fixture').html(view.render().el); + + equal(view.$('.box').prop('checked'), false); + + model.set('water', 'fountain'); + equal(view.$('.box').prop('checked'), true); + + view.$('.box').prop('checked', false).change(); + equal(model.get('water'), null); + }); + }); diff --git a/test/index.html b/test/index.html index 5b8e83c..5a1e628 100644 --- a/test/index.html +++ b/test/index.html @@ -96,6 +96,16 @@

+ + + +