From d8e08085de1189c01bae28a77a556d75be831015 Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Sun, 13 Jan 2013 15:26:47 +0400 Subject: [PATCH] optgroups --- CHANGELOG.txt | 1 + src/editable-form/editable-form-utils.js | 29 +++++++++++----- src/inputs/list.js | 42 ++++++++++++++++-------- src/inputs/select.js | 21 ++++++++---- test/unit/select.js | 39 +++++++++++++++++++++- 5 files changed, 101 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index d4bbf7f..16ef758 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -4,6 +4,7 @@ X-editable changelog Version 1.4.1 wip ---------------------------- +[enh] select: support of OPTGROUP via `children` key in source (vitalets) [enh] checklist: set checked via prop instead of attr (vitalets) diff --git a/src/editable-form/editable-form-utils.js b/src/editable-form/editable-form-utils.js index af4689a..4eb6aee 100644 --- a/src/editable-form/editable-form-utils.js +++ b/src/editable-form/editable-form-utils.js @@ -136,17 +136,28 @@ if(!sourceData || value === null) { return []; } - - //convert to array - if(!$.isArray(value)) { - value = [value]; - } - /*jslint eqeq: true*/ - var result = $.grep(sourceData, function(o){ - return $.grep(value, function(v){ return v == o.value; }).length; + var isValArray = $.isArray(value), + result = [], + that = this; + + $.each(sourceData, function(i, o) { + if(o.children) { + result = result.concat(that.itemsByValue(value, o.children)); + } else { + /*jslint eqeq: true*/ + if(isValArray) { + if($.grep(value, function(v){ return v == o.value; }).length) { + result.push(o); + } + } else { + if(value == o.value) { + result.push(o); + } + } + /*jslint eqeq: false*/ + } }); - /*jslint eqeq: false*/ return result; }, diff --git a/src/inputs/list.js b/src/inputs/list.js index 7ecc469..0aab53d 100644 --- a/src/inputs/list.js +++ b/src/inputs/list.js @@ -200,35 +200,45 @@ List - abstract class for inputs that have source option loaded from js array or * convert data to array suitable for sourceData, e.g. [{value: 1, text: 'abc'}, {...}] */ makeArray: function(data) { - var count, obj, result = [], iterateEl; + var count, obj, result = [], item, iterateItem; if(!data || typeof data === 'string') { return null; } if($.isArray(data)) { //array - iterateEl = function (k, v) { + /* + function to iterate inside item of array if item is object. + Caclulates count of keys in item and store in obj. + */ + iterateItem = function (k, v) { obj = {value: k, text: v}; if(count++ >= 2) { - return false;// exit each if object has more than one value + return false;// exit from `each` if item has more than one key. } }; for(var i = 0; i < data.length; i++) { - if(typeof data[i] === 'object') { - count = 0; - $.each(data[i], iterateEl); - if(count === 1) { + item = data[i]; + if(typeof item === 'object') { + count = 0; //count of keys inside item + $.each(item, iterateItem); + //case: [{val1: 'text1'}, {val2: 'text2} ...] + if(count === 1) { result.push(obj); - } else if(count > 1 && data[i].hasOwnProperty('value') && data[i].hasOwnProperty('text')) { - result.push(data[i]); - } else { - //data contains incorrect objects + //case: [{value: 1, text: 'text1'}, {value: 2, text: 'text2'}, ...] + } else if(count > 1) { + //removed check of existance: item.hasOwnProperty('value') && item.hasOwnProperty('text') + if(item.children) { + item.children = this.makeArray(item.children); + } + result.push(item); } } else { - result.push({value: data[i], text: data[i]}); + //case: ['text1', 'text2' ...] + result.push({value: item, text: item}); } } - } else { //object + } else { //case: {val1: 'text1', val2: 'text2, ...} $.each(data, function (k, v) { result.push({value: k, text: v}); }); @@ -251,12 +261,16 @@ List - abstract class for inputs that have source option loaded from js array or List.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, { /** Source data for list. - If **array** - it should be in format: `[{value: 1, text: "text1"}, {...}]` + If **array** - it should be in format: `[{value: 1, text: "text1"}, {value: 2, text: "text2"}, ...]` For compability, object format is also supported: `{"1": "text1", "2": "text2" ...}` but it does not guarantee elements order. If **string** - considered ajax url to load items. In that case results will be cached for fields with the same source and name. See also `sourceCache` option. If **function**, it should return data in format above (since 1.4.0). + + Since 1.4.1 key `children` supported to render OPTGROUPs (select input only). + Example `[{text: "group1", children: [{value: 1, text: "text1"}, {value: 2, text: "text2"}]}, ...]`. + @property source @type string | array | object | function diff --git a/src/inputs/select.js b/src/inputs/select.js index f08b512..0a0b4a7 100644 --- a/src/inputs/select.js +++ b/src/inputs/select.js @@ -31,14 +31,21 @@ $(function(){ $.extend(Select.prototype, { renderList: function() { this.$input.empty(); - - if(!$.isArray(this.sourceData)) { - return; - } - for(var i=0; i<this.sourceData.length; i++) { - this.$input.append($('<option>', {value: this.sourceData[i].value}).text(this.sourceData[i].text)); - } + var fillItems = function($el, data) { + if($.isArray(data)) { + for(var i=0; i<data.length; i++) { + if(data[i].children) { + $el.append(fillItems($('<optgroup>', {label: data[i].text}), data[i].children)); + } else { + $el.append($('<option>', {value: data[i].value}).text(data[i].text)); + } + } + } + return $el; + }; + + fillItems(this.$input, this.sourceData); this.setClass(); diff --git a/test/unit/select.js b/test/unit/select.js index d4f80d9..cb5cb20 100644 --- a/test/unit/select.js +++ b/test/unit/select.js @@ -689,6 +689,43 @@ $(function () { }, timeout); }, timeout); - }); + }); + + asyncTest("optgroup", function () { + var + selected = 2, + e = $('<a href="#" data-type="select" data-value="'+selected+'" data-url="post.php"></a>').appendTo(fx).editable({ + pk: 1, + source: [ + {text: 'groups', children: groups}, + {value: 'v1', text: 't1', children: ['a', 'b', 'c']}, + {value: 'v2', text: 't2'} + ] + }); + + equal(e.text(), groups[selected], 'text shown'); + + e.click(); + var p = tip(e); + ok(p.is(':visible'), 'container visible'); + ok(p.find('select').length, 'select exists'); + equal(p.find('select').find('option').length, size + 3 + 1, 'options loaded'); + equal(p.find('select').val(), e.data('editable').value, 'selected value correct'); + + equal(p.find('select').find('optgroup').length, 2, 'optgroup loaded'); + equal(p.find('select').find('optgroup').eq(0).children().length, size, 'optgroup items ok'); + + selected = 'a'; + p.find('select').val(selected); + p.find('form').submit(); + + setTimeout(function() { + ok(!p.is(':visible'), 'popover closed') + equal(e.data('editable').value, selected, 'new value saved') + equal(e.text(), 'a', 'new text shown') + e.remove(); + start(); + }, timeout); + }); });