Handsontable 下拉菜单有多个选择

Posted

技术标签:

【中文标题】Handsontable 下拉菜单有多个选择【英文标题】:Handsontable dropdowns with multiple selections 【发布时间】:2015-11-03 01:33:25 【问题描述】:

我正在尝试扩展可动手做的插件以支持其下拉列表中的多项选择。我已经尝试按照建议https://github.com/trebuchetty/Handsontable-select2-editor/issues/7 修改“dropdownEditor”来扩展库中内置的基本编辑器。我花了几个小时阅读和搜索关键字的来源,但我没有想出任何真正有用的东西。

我不介意使用 Angular 扩展或其他原生 ECMA5 或 6 扩展 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);

此时,当用户从下拉列表中选择一个项目时,我不知道点击事件发生在哪里。调试对我来说一直很痛苦,因为它是通过 Traceur 进行的。我尝试在模块准备好并且DOM也准备好之后设置单击事件,但是我什至无法通过单击一个选择下拉单元格来获得触发警报。我可以通过简单的点击获得“正常”单元格:

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

但是,菜单内容并非如此。右键单击检查下拉菜单意味着首先禁用上下文菜单,如http://handsontable.com/ 上的上下文菜单。然后您会注意到右键单击检查任何内容都会触发一个事件,该事件会关闭您尝试检查的下拉菜单。

我在库源代码中都设置了断点,但我无法弄清楚这一点。

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

然后确保这些选择实际上在 Handsontable 的“数据范围”内。

就是这样,我什至不需要它来在单元格中呈现已选择的内容,尽管任何帮助都会非常有用,因为不幸的是,当下拉菜单中的选项被呈现时,我还没有找到位置.

我也尝试过使用为 handsontable 制作的 Select2Editor,如 http://jsfiddle.net/4mpyhbjw/40/ 和 https://github.com/trebuchetty/Handsontable-select2-editor/issues/3 所示,但它对我的事业没有多大帮助。 Handsontable 中的下拉单元格如下所示:

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

最后,这是一个小提琴:http://jsfiddle.net/tjrygch6/

如果有人可以在这里帮助我,我将非常感激。谢谢!

更新

我已设法解析单元格中的值并将类型转换为包含值的数组(因此键入 red blue 将转换为包含 ['red','blue'] 的数组)。我已经通过内部排序算法运行了这个数组,该算法解析选项并返回匹配项的索引。我得到这个工作正常,我现在将数组传递给 highlight 方法。此方法将值传递给核心库 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;

;

更新 2

我已经获得了在 DOM 中识别和部分选择这两个值的内部方法,但它仍然远非正确。

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

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

更新如果有人解决了这个问题,我会亲自飞到你所在的任何地方,和你握手。两次。

【问题讨论】:

【参考方案1】:

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

我从Handsontable 库(最新版本)中获取了示例代码,并做了一些小改动。

它可能存在一些错误,但它只是一个原型,因此您可以进行编辑并使其看起来更好。

由于某种原因,我没有成功使dropdownlist 成为可点击的。似乎是 z-index 问题或其他 css 属性游戏。我相信你会找到解决方法。 无论如何,现在你可以使用键盘进行选择,按住 shift 进行多选。

输出是由逗号分隔的连接选定选项的集合。

例如:

为了使这项工作在您加载可动手做的库后添加此代码。它将扩展您的 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现场演示

为了让你轻松。如果您想编辑代码,您可能需要更改 2 种方法。

    prepare - 每次用户触发编辑器打开事件时都会调用。用于配置和操作。 init - 每次单击单元格时都会调用该方法。它会创建 html 代码,例如,您可以将其更改为复选框。

另一件事与您关于代码中的内容的问题有关。

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

希望对您有所帮助,并希望它适合您当前的版本。

【讨论】:

Dvir,首先让我非常感谢您迄今为止所付出的努力。其次,您能否在演示中提供上述解决方案?由于显示了许多错误,我无法使提供的代码正常工作。我什至尝试在 setValue() 之后调试那个,但是结果发现 SelectEditor 是未定义的。你如何提供对 SelectEditor 的访问,你只通过 Handsontable?再次感谢,如果您可以在演示中重现这些结果,我将继续使用它。 Dvir 我无法在我的 MacBook Air 上获得多项选择,明天将检查 chrome/windows。我正在按下衬衫和向下或换档和标签 - 我按下正确的图案吗?再次感谢! 是的。用键盘试试。 我已经尝试了我能想到的所有按键组合,不幸的是我仍然无法一次选择 2 个项目。如果我做错了什么,我深表歉意。你能告诉我你用的是什么浏览器和操作系统+版本吗?我在多个浏览器中都试过了。 Windows 10 和 chrome 最新版本。双击单元格,对话框将打开。然后单击滚动条,并使用键盘在选项之间导航。按住 shift 并按向下键或向上键可选择多个选项。从对话框中单击,您将看到您的选择。【参考方案2】:

哇。这么多努力。现在,一年多过去了,这要容易得多。

我成功使用了 Chosen jQuery 插件。这很容易。

这是一个人的例子: 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
],

【讨论】:

以上是关于Handsontable 下拉菜单有多个选择的主要内容,如果未能解决你的问题,请参考以下文章

Handsontable 中的 Handsontable 下拉高度调整

如何在handsontable上过滤时仅在下拉菜单上显示不包含HTML内容的文本?

Excel数据有效性设置的下拉菜单内容有多个,但选择时只能单选,如何设置下拉菜单,可以在选择时多选?

在 python 中使用 selenium 从下拉菜单中选择多个选项

Odoo[12.0] : 如何创建下拉菜单并在下拉菜单中显示所有菜单以及选择多个菜单

Ant design - 如何在每次选择后自动关闭选择(“标签”或“多个”模式)下拉菜单?