Browse files

Add optionsAttr binding which allows setting arbitrary attributes on …

…the options in a select, similar to the attr binding.
  • Loading branch information...
1 parent 28169e7 commit 9fdc307c2d8b769c4f35640677d0f0f8d49b4ad3 @amonat committed Dec 30, 2011
Showing with 43 additions and 8 deletions.
  1. +16 −7 spec/defaultBindingsBehaviors.js
  2. +27 −1 src/binding/defaultBindings.js
View
23 spec/defaultBindingsBehaviors.js
@@ -401,17 +401,26 @@ describe('Binding: Options', {
value_of(displayedOptions).should_be(["A", "B", "C"]);
},
- 'Should accept optionsText and optionsValue params to display subproperties of the model values': function() {
+ 'Should accept optionsText, optionsValue and optionsAttr params to display subproperties of the model values': function() {
var modelValues = new ko.observableArray([
- { name: 'bob', id: ko.observable(6) }, // Note that subproperties can be observable
- { name: ko.observable('frank'), id: 13 }
- ]);
- testNode.innerHTML = "<select data-bind='options:myValues, optionsText: \"name\", optionsValue: \"id\"'><option>should be deleted</option></select>";
- ko.applyBindings({ myValues: modelValues }, testNode);
+ { name: 'bob', id: ko.observable(6), hobby: 'chess' }, // Note that subproperties can be observable
+ { name: ko.observable('frank'), id: 13, hobby: 'mancala' }
+ ]);
+ var hello = function(item) {
+ return 'I like ' + item.hobby;
+ };
+ testNode.innerHTML = "<select data-bind='options:myValues, optionsText: \"name\", optionsValue: \"id\", optionsAttr: {firstAttribute: \"hobby\", \"second-attribute\": true, thirdAttribute: hello}'><option>should be deleted</option></select>";
+ ko.applyBindings({ myValues: modelValues, hello: hello }, testNode);
var displayedText = ko.utils.arrayMap(testNode.childNodes[0].childNodes, function (node) { return node.innerHTML; });
var displayedValues = ko.utils.arrayMap(testNode.childNodes[0].childNodes, function (node) { return node.value; });
+ var firstAttributeValues = ko.utils.arrayMap(testNode.childNodes[0].childNodes, function (node) { return node.getAttribute("firstAttribute"); });
+ var secondAttributeValues = ko.utils.arrayMap(testNode.childNodes[0].childNodes, function (node) { return node.getAttribute("second-attribute"); });
+ var thirdAttributeValues = ko.utils.arrayMap(testNode.childNodes[0].childNodes, function (node) { return node.getAttribute("thirdAttribute"); });
value_of(displayedText).should_be(["bob", "frank"]);
value_of(displayedValues).should_be([6, 13]);
+ value_of(firstAttributeValues).should_be(["chess", "mancala"]);
+ value_of(secondAttributeValues).should_be(["true", "true"]);
+ value_of(thirdAttributeValues).should_be(["I like chess", "I like mancala"]);
},
'Should accept function in optionsText param to display subproperties of the model values': function() {
@@ -1530,4 +1539,4 @@ describe('Binding: Foreach', {
ko.applyBindings(viewModel, testNode);
value_of(testNode).should_contain_html('<div data-bind="foreach:someitems"><section data-bind="text: $data">alpha</section><section data-bind="text: $data">beta</section></div>');
}
-});
+});
View
28 src/binding/defaultBindings.js
@@ -238,6 +238,32 @@ ko.bindingHandlers['options'] = {
if ((optionText === null) || (optionText === undefined))
optionText = "";
+ // Apply some attributes to the option element
+ var optionsAttrValue = allBindings['optionsAttr'];
+ if (typeof optionsAttrValue === "object") {
+ for (var attrName in optionsAttrValue) {
+ if (typeof attrName == "string") {
+ var attrValue = ko.utils.unwrapObservable(optionsAttrValue[attrName]);
+
+ var attrResult;
+ if (typeof attrValue == "function")
+ attrResult = attrValue(value[i]); // Given a function; run it against the data value
+ else if (typeof attrValue == "string")
+ attrResult = value[i][attrValue]; // Given a string; treat it as a property name on the data value
+ else
+ attrResult = attrValue; // Given no optionsText arg; use the data value itself
+
+ // To cover cases like "attr: { checked:someProp }", we want to remove the attribute entirely
+ // when someProp is a "no value"-like value (strictly null, false, or undefined)
+ // (because the absence of the "checked" attr is how to mark an element as not checked, etc.)
+ if ((attrResult === false) || (attrResult === null) || (attrResult === undefined))
+ option.removeAttribute(attrName);
+ else
+ option.setAttribute(attrName, attrResult.toString());
+ }
+ }
+ }
+
ko.utils.setTextContent(option, optionText);
element.appendChild(option);
@@ -541,4 +567,4 @@ ko.bindingHandlers['foreach'] = {
};
ko.jsonExpressionRewriting.bindingRewriteValidators['foreach'] = false; // Can't rewrite control flow bindings
ko.virtualElements.allowedBindings['foreach'] = true;
-ko.exportSymbol('ko.allowedVirtualElementBindings', ko.virtualElements.allowedBindings);
+ko.exportSymbol('ko.allowedVirtualElementBindings', ko.virtualElements.allowedBindings);

0 comments on commit 9fdc307

Please sign in to comment.