首页 文章

具有多个选择的双手下拉菜单

提问于
浏览
18

我正在尝试扩展handsontable插件以支持其下拉列表中的多个选择 . 我已经尝试通过修改'dropdownEditor'来扩展内置到库中的基本编辑器https://github.com/trebuchetty/Handsontable-select2-editor/issues/7 . 我花了几个小时阅读和搜索关键字的来源,但我没有想出任何真正的用途 .

我不介意使用Angular扩展或其他原生ECMA5或扩展https://github.com/handsontable/handsontable插件的方式来解决这个问题 .

到目前为止,我唯一的想法是使用这些代码按照存在的模式实际扩展框架 . 我添加了以下指向的所有LOC: multiselectHandsontable.MultiselectDropdownCell 复制了 dropdown 方法,称为新名称,一切正常,但仍无法看到我在哪里可以找到我要找的东西 .

Handsontable.MultiselectDropdownCell = {
  editor: getEditorConstructor('multiselectdropdown'),
  renderer: getRenderer('autocomplete')
};

Handsontable.cellTypes = {
  text: Handsontable.TextCell,
  date: Handsontable.DateCell,
  numeric: Handsontable.NumericCell,
  checkbox: Handsontable.CheckboxCell,
  autocomplete: Handsontable.AutocompleteCell,
  handsontable: Handsontable.HandsontableCell,
  password: Handsontable.PasswordCell,
  dropdown: Handsontable.DropdownCell,
  multiselect: Handsontable.MultiselectDropdownCell
};

Handsontable.cellLookup = { validator: {
    numeric: Handsontable.NumericValidator,
    autocomplete: Handsontable.AutocompleteValidator
}};

我有一个修改版的下拉编辑器,它看起来像:

import {getEditor, registerEditor} from './../editors.js';
import {AutocompleteEditor} from './autocompleteEditor.js';

/**
 * @private
 * @editor MultiSelectDropdownEditor
 * @class MultiSelectDropdownEditor
 * @dependencies AutocompleteEditor
 */
class MultiSelectDropdownEditor extends AutocompleteEditor {
  prepare(row, col, prop, td, originalValue, cellProperties) {
    super.prepare(row, col, prop, td, originalValue, cellProperties);
    this.cellProperties.filter = false;
    this.cellProperties.strict = true;
  }
}

export {MultiSelectDropdownEditor};

registerEditor('multiselectdropdown', MultiSelectDropdownEditor);

此时,我不知道用户从下拉列表中选择项目时发生click事件的位置 . 调试对我来说很痛苦,因为它是通过Traceur实现的 . 我尝试在模块准备就绪后设置点击事件,并且DOM也是如此,但是根据其中一个选择下拉单元格的点击,我甚至无法触发警报 . “普通”单元格我只需简单点击即可:

$('body').on('click','#handsontable td', someAlert)

但是,菜单内容不是这样 . 右键单击以检查下拉菜单意味着首先禁用上下文菜单,如http://handsontable.com/上的菜单 . 然后您会注意到右键单击以检查任何内容将触发一个事件,该事件将关闭您要检查的下拉菜单 .

我已经通过库源代码放置了断点,我无法想出这一点 .

我唯一想做的就是找出突出显示菜单项并将其设置为活动选择的代码部分,将其转换为接受多个选择的方法(直到可用的整个选项数组,单击一个活动的项目将禁用它只是说) .

然后确保这些选择实际上在Handsontable'数据范围'中 .

多数民众赞成,我不需要它甚至在单元格中呈现所选择的内容,尽管任何帮助都会很棒,因为不幸的是,我还没有找到下拉列表中的选项时的位置 .

我也尝试过使用Select2Editor for handsontable,如http://jsfiddle.net/4mpyhbjw/40/https://github.com/trebuchetty/Handsontable-select2-editor/issues/3,但它对我的原因没什么帮助 . 以下是handontable中的下拉单元格:

http://docs.handsontable.com/0.15.1/demo-dropdown.html

最后,继承人 fiddle: http://jsfiddle.net/tjrygch6/

如果有人能帮助我,我会非常感激 . 谢谢!

UPDATE

我设法解析单元格中的值并将类型转换为包含值的数组(因此键入红色蓝色将转换包含 ['red','blue'] 的数组) . 我通过内部排序算法运行此数组,该算法解析选项并返回匹配项的索引 . 我得到了这个工作正常,我现在将数组传递给高亮方法 . 此方法传递核心库WalkOnTable的值 . 我没有看到我可以在哪里改变逻辑来选择多个值而不是取消第一个选项的突出显示 .

this.selectCell = function(row, col, endRow, endCol, scrollToCell, changeListener) {
var coords;
changeListener = typeof changeListener === 'undefined' || changeListener === true;
if (typeof row !== 'number' && !Array.isArray(row) || row < 0 || row >= instance.countRows()) {
  return false;
}
if (typeof col !== 'number' || col < 0 || col >= instance.countCols()) {
  return false;
}
if (typeof endRow !== 'undefined') {
  if (typeof endRow !== 'number' || endRow < 0 || endRow >= instance.countRows()) {
    return false;
  }
  if (typeof endCol !== 'number' || endCol < 0 || endCol >= instance.countCols()) {
    return false;
  }
}
// Normal number value, one item typed in
if (!Array.isArray(row) && typeof row === 'number'){
  coords = new WalkontableCellCoords(row, col);

  walkSelection(coords);
}

这是我认为需要 WalkontableCellCoords 被修改为接受一个数组然后在打开和关闭下拉列表时突出显示并选择这两个值的地方 . 我还需要能够通过触摸或点击事件选择多个选项 .

else {
  // Array found, apply to each value
  new WalkontableCellCoords(row[0], col);
  new WalkontableCellCoords(row[1], col);
}

function walkSelection(coords){
  priv.selRange = new WalkontableCellRange(coords, coords, coords);
  if (document.activeElement && document.activeElement !== document.documentElement && document.activeElement !== document.body) {
    document.activeElement.blur();
  }
  if (changeListener) {
    instance.listen();
  }
  if (typeof endRow === 'undefined') {
    selection.setRangeEnd(priv.selRange.from, scrollToCell);
  } else {
    selection.setRangeEnd(new WalkontableCellCoords(endRow, endCol), scrollToCell);
  }
  instance.selection.finish();
}

return true;

};

Update 2

我已经获得了内部方法来识别和部分选择DOM中的两个值,但它仍远未正确 .

Showing the selection (sort of) of both items based on two values typed into the cell, also showing the output in the console for the coord being returned from the WalkOnTable util being used behind the scenes in this handsontable plugin. Output is below

以下是要调用的方法 WalkOnTableCellCords 生成的控制台输出,这似乎是在单元格仅包含1个值(默认功能)的情况下突出显示下拉选择的内容 . 此输出是将黑蓝键入包含蓝色和黑色的下拉单元格,作为列表中的单个选项 .

extended_hot_v15-01.js:5041 DropdownEditor {
            "highlight": {
                    "row": 6,
                    "col": 0
            },
            "from":
                   {
                    "row": 4,
                    "col": 0
                   },
             "to": {
                    "row": 6,
                    "col": 0
                    }
            }

UPDATE 如果有人解决这个问题,我会亲自飞到你身边的任何地方,然后握手 . 两次 .

2 回答

  • 2

    哇 . 这么多努力 . 现在,一年多以后,它变得容易多了 .

    我成功使用了Chosen jQuery插件 . 这很简单 .

    这里's one person'的例子:https://github.com/mydea/handsontable-chosen-editor

    选择是美丽的 . 我正在使用多重选择自动完成 . 这是渲染器:

    function customDropdownRenderer(instance, td, row, col, prop, value, cellProperties) {
    
        var selectedId;
        var optionsList = cellProperties.chosenOptions.data;
    
        if(typeof optionsList === "undefined" || typeof optionsList.length === "undefined" || !optionsList.length) {
            Handsontable.TextCell.renderer(instance, td, row, col, prop, value, cellProperties);
            return td;
        }
    
        var values = (value + "").split(",");
        value = [];
        for (var index = 0; index < optionsList.length; index++) {
    
            if (values.indexOf(optionsList[index].id + "") > -1) {
                selectedId = optionsList[index].id;
                value.push(optionsList[index].label);
            }
        }
        value = value.join(", ");
    
        Handsontable.TextCell.renderer(instance, td, row, col, prop, value, cellProperties);
        return td;
    }
    

    然后我就像设置特定列一样这个:

    columns: [
        {},
        {},
        {type: 'numeric'},
        {type: 'dropdown', source: ['', 'NAME', 'FNB']},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {type: 'dropdown', source: ['', 'S', 'M']},
        {},
        {},
        {
            renderer: customDropdownRenderer,
            editor: "chosen",
            width: 150,
            chosenOptions: {
                multiple: true,
                data: productData
            }
        },
        {},
        {editor: false, readOnly: true, width: 1}, 
        {editor: false, readOnly: true, width: 1}
    ],
    
  • 4

    好的,我希望它会对你有所帮助 . 我花了一些时间阅读api并自定义代码:)

    我从 Handsontable 库(最新版本)中获取了示例代码并进行了少量更改 .

    它可能有一些错误,但它只是一个原型,所以你可以编辑并使其看起来更好 .

    出于某种原因,我没有成功使 dropdownlist 可点击 . 它似乎是z-index问题或其他css属性游戏 . 我相信你会找到解决方法 . 目前,您可以通过按住shift进行多项选择来使用键盘进行选择 .

    输出是以逗号分隔的已连接选定选项的集合 .

    例如:

    enter image description here

    enter image description here

    为了完成这项工作,在加载handontable libary之后添加此代码 . 它将扩展您的 Handsontable 细胞类型 .

    (function(Handsontable) {
        var SelectEditor = Handsontable.editors.BaseEditor.prototype.extend();
    
        SelectEditor.prototype.init = function() {
            // Create detached node, add CSS class and make sure its not visible
            this.select = document.createElement('SELECT');
            Handsontable.Dom.addClass(this.select, 'htSelectEditor');
            this.select.style.display = 'none';
    
            // Attach node to DOM, by appending it to the container holding the table
            this.instance.rootElement.appendChild(this.select);
        };
        // Create options in prepare() method
        SelectEditor.prototype.prepare = function() {
            // Remember to invoke parent's method
            Handsontable.editors.BaseEditor.prototype.prepare.apply(this, arguments);
            this.isMultiple = !!this.cellProperties.multiple;
            if (this.isMultiple) this.select.multiple = true;
            var selectOptions = this.cellProperties.selectOptions;
            var options;
    
            if (typeof selectOptions == 'function') {
                options = this.prepareOptions(selectOptions(this.row,
                    this.col, this.prop))
            } else {
                options = this.prepareOptions(selectOptions);
            }
            Handsontable.Dom.empty(this.select);
    
            for (var option in options) {
                if (options.hasOwnProperty(option)) {
                    var optionElement = document.createElement('OPTION');
                    optionElement.value = option;
                    Handsontable.Dom.fastInnerHTML(optionElement, options[option]);
                    this.select.appendChild(optionElement);
                }
            }
        };
        SelectEditor.prototype.prepareOptions = function(optionsToPrepare) {
            var preparedOptions = {};
    
            if (Array.isArray(optionsToPrepare)) {
                for (var i = 0, len = optionsToPrepare.length; i < len; i++) {
                    preparedOptions[optionsToPrepare[i]] = optionsToPrepare[i];
                }
            } else if (typeof optionsToPrepare == 'object') {
                preparedOptions = optionsToPrepare;
            }
    
            return preparedOptions;
        };
        SelectEditor.prototype.getValue = function() {
            var result = [];
            var options = this.select && this.select.options;
            var opt;
    
            for (var i = 0, iLen = options.length; i < iLen; i++) {
                opt = options[i];
    
                if (opt.selected) {
                    result.push(opt.value || opt.text);
                }
            }
    
            return result.join();
        };
    
        SelectEditor.prototype.setValue = function(value) {
            this.select.value = value;
        };
    
        SelectEditor.prototype.open = function() {
            var width = Handsontable.Dom.outerWidth(this.TD);
            // important - group layout reads together for better performance
            var height = Handsontable.Dom.outerHeight(this.TD);
            var rootOffset = Handsontable.Dom.offset(this.instance.rootElement);
            var tdOffset = Handsontable.Dom.offset(this.TD);
            var editorSection = this.checkEditorSection();
            var cssTransformOffset;
    
            if (this.select && this.select.options && this.isMultiple) {
                var height = 0;
                for (var i = 0; i < this.select.options.length - 1; i++) {
                    height += Handsontable.Dom.outerHeight(this.TD);
                }
            }
    
            switch (editorSection) {
                case 'top':
                    cssTransformOffset = Handsontable.Dom.getCssTransform(this.instance.view.wt.wtScrollbars.vertical.clone.wtTable.holder.parentNode);
                    break;
                case 'left':
                    cssTransformOffset = Handsontable.Dom.getCssTransform(this.instance.view.wt.wtScrollbars.horizontal.clone.wtTable.holder.parentNode);
                    break;
                case 'corner':
                    cssTransformOffset = Handsontable.Dom.getCssTransform(this.instance.view.wt.wtScrollbars.corner.clone.wtTable.holder.parentNode);
                    break;
            }
            var selectStyle = this.select.style;
    
            if (cssTransformOffset && cssTransformOffset !== -1) {
                selectStyle[cssTransformOffset[0]] = cssTransformOffset[1];
            } else {
                Handsontable.Dom.resetCssTransform(this.select);
            }
    
            selectStyle.height = height + 'px';
            selectStyle.minWidth = width + 'px';
            selectStyle.top = tdOffset.top - rootOffset.top + 'px';
            selectStyle.left = tdOffset.left - rootOffset.left + 'px';
            selectStyle.margin = '0px';
            selectStyle.display = '';
    
        };
    
        SelectEditor.prototype.checkEditorSection = function() {
            if (this.row < this.instance.getSettings().fixedRowsTop) {
                if (this.col < this.instance.getSettings().fixedColumnsLeft) {
                    return 'corner';
                } else {
                    return 'top';
                }
            } else {
                if (this.col < this.instance.getSettings().fixedColumnsLeft) {
                    return 'left';
                }
            }
        };
    
        SelectEditor.prototype.close = function() {
            this.select.style.display = 'none';
        };
    
        Handsontable.editors.registerEditor('dvirH', SelectEditor);
    
    })(Handsontable);
    

    使用方法:

    var container = document.getElementById("example1");
    var hot1;
    
    hot1 = new Handsontable(container, {
        data: [
            ['2008', 'Nissan', 11],
            ['2009', 'Honda', 11],
            ['2010', 'Kia', 15]
        ],
        colHeaders: true,
        contextMenu: false,
        columns: [{}, {
            editor: 'select',
            selectOptions: ['Kia', 'Nissan', 'Toyota', 'Honda'],
            //  notice that attribute. You can remove it to get a regular select
            multiple: true
        } {}]
    });
    

    here的现场演示

    为了方便你 . 如果要编辑代码,可能需要更改两种方法 .

    • prepare - 每次用户触发编辑器打开事件时都会调用 . 用于配置和操作 .

    • init - 每次单击一个单元格时都会调用该方法 . 它会创建html代码,因此您可以将其更改为复选框 .

    另一件事涉及您关于代码中的内容的问题 .

    Handsontable将任何单元格类型拆分为编辑器和渲染 . 编辑器的所有html代码可能都存在于 init 中,以防您想要更改其中一个 . value 是在编辑模式下 not 时出现在单元格中的html内容,存在于 getValue 方法中 .

    我希望它有所帮助,我希望它适合您当前的版本 .

相关问题