微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么我的 Handsontable 自定义编辑器中的复选框不切换?

如何解决为什么我的 Handsontable 自定义编辑器中的复选框不切换?

我使用的是最新的 Handsontable 9.0.0 版,没有任何框架,我正在尝试遵循 Cell editor 开发人员指南,但我不知所措。

我的要求是在一个单元格中显示几个复选框和一个文本框(不是我的想法)。我的想法是让单元格的数据是一个小的 json 字符串 {"Attr1": true,"Attr2": false} 并有一个自定义渲染器/编辑器来解析单元格值并适当地设置复选框。

在这里做了一个小提琴:http://jsfiddle.net/9k1x4z6b/2/

我为自定义属性列创建了一个类和一个渲染器函数,并像这样为该列设置了渲染器和编辑器

    class CustomAttributesEditor extends Handsontable.editors.BaseEditor {
    /**
    * Initializes editor instance,DOM Element and mount hooks.
    */

    // constructor (props) {
    //     super(props)
    // }

    prepare(row,col,prop,td,originalValue,cellProperties) {
        // Invoke the original method...
        super.prepare(row,cellProperties);

        td.innerHTML = '';

        this.AttributeNames = ['Attr1','Attr2'];
        this.ctrls = {};
        let parsedValue = JSON.parse(Handsontable.helper.stringify(originalValue));

        // Create checkBox controls
        for (let i = 0; i < this.AttributeNames.length; i++) {
            let AttributeName = this.AttributeNames[i];

            let span = document.createElement('span');
            span.style.whiteSpace = 'Nowrap';

            let checkBox = document.createElement('input');
            this.ctrls[AttributeName] = checkBox;
            checkBox.type = 'checkBox';

            if (parsedValue[AttributeName] == 'yes') {
                checkBox.checked = true;
            }
            
            let label = document.createElement('label');
            label.innerHTML = AttributeName;
            label.htmlFor = checkBox.id;

            span.appendChild(checkBox);
            span.appendChild(label);
            td.appendChild(span);
            td.appendChild(document.createElement('br'));
        }

        // Create a control that is shown/hidden when the "Attr2" checkBox is toggled
        let CustomAttributesAttr3SubDiv = document.createElement('div');
        var label = document.createElement('label');
        label.innerHTML = "Attr3 supplier:";
        CustomAttributesAttr3SubDiv.appendChild(label);
        
        var CustomAttributesAttr3 = document.createElement('input');
        if (parsedValue.hasOwnProperty('Attr3')) {
            CustomAttributesAttr3.value = parsedValue['Attr3'];
        }
        this.ctrls['Attr3'] = CustomAttributesAttr3;
        this.AttributeNames.push('Attr3');
        CustomAttributesAttr3.setAttribute('title','Attr3');
        CustomAttributesAttr3.style.width = '12em';

        CustomAttributesAttr3SubDiv.appendChild(CustomAttributesAttr3);
        CustomAttributesAttr3SubDiv.appendChild(document.createElement('br'));
        td.appendChild(CustomAttributesAttr3SubDiv);

        let Attr2CheckBox = this.ctrls['Attr2'];
        //CustomAttributes_ShowHideValueCtrl(Attr2CheckBox);
        $(Attr2CheckBox).off('change').on('change',function () {
            //CustomAttributes_ShowHideValueCtrl(this); // irrelevant to checkBox problem. function shows Attr3 input when Attr2CheckBox is checked,hides otherwise
        });

        //preventDefault();
    }

    getValue(){
        // This function returns the set value of the controls
        let ctrls = this.ctrls;
        let resultDict = {};
        for (let ctrlID in ctrls){
            let ctrl = ctrls[ctrlID];
            let FormattedAttributeName = ctrlID.replaceAll(' ','_');
            let val = null;
            if (ctrl.type == 'checkBox'){
                if (ctrl.checked == true) {
                    val = 'yes';
                } else {
                    val = null;
                }
            } else {
                val = ctrl.value;
            }
            resultDict[FormattedAttributeName] = val;
        }

        return JSON.stringify(resultDict)
    }

    setValue(value){
        // this function sets the value of the controls to match the data value

        let parsedValue = {};
        try {
            parsedValue = JSON.parse(Handsontable.helper.stringify(value));
        } catch (exc) {
            for (let i = 0; i < this.AttributeNames.length; i++) {
                parsedValue[this.AttributeNames[i]] = 'no';
            }
        }

        let ctrls = this.ctrls;
        let resultDict = {};
        for (let ctrlID in ctrls){
            let ctrl = ctrls[ctrlID];
            let FormattedAttributeName = ctrlID.replaceAll(' ','_');
            let val = parsedValue[FormattedAttributeName];
            if (ctrl.type == 'checkBox'){
                if (val == 'yes'){
                    ctrl.checked = true;
                } else {
                    ctrl.checked = false;
                }
            } else {
                ctrl.value = val;
            }
        }
        
    }

    saveValue(value,ctrlDown){
        super.saveValue(value,ctrlDown);
    }

    open(){}
    close(){}
    focus(){}
}

function CustomAttributesRenderer(instance,row,value,cellProperties) {
    // This function shows labels for the checked Attr1-3 values
    let AttributeNames = ['Attr1','Attr2','Attr3'];
    parsedValue = JSON.parse(Handsontable.helper.stringify(value));
    
    Handsontable.dom.empty(td);
    for (let i = 0; i < AttributeNames.length; i++) {
        let AttributeName = AttributeNames[i];

        let span = document.createElement('span');
        span.style.whiteSpace = 'Nowrap';

        if (parsedValue[AttributeName] == 'yes') {
          let label = document.createElement('label');
            label.innerHTML = AttributeName;
          span.appendChild(label);
            td.appendChild(span);
        }

        td.appendChild(document.createElement('br'));
    }

    return td;
}


document.addEventListener("DOMContentLoaded",function () {
    var container = document.getElementById('divBFEPartMatrix');

    var hot = new Handsontable(container,{
        data: [
            [JSON.stringify({"Attr1": "yes","Attr2": "yes","Attr3": ""})],[JSON.stringify({"Attr1": "yes","Attr3": "somevalue"})],[JSON.stringify({"Attr1": "no","Attr2": "no",],columns: [
            {renderer: CustomAttributesRenderer,editor: CustomAttributesEditor}
        ],rowHeaders: true,colHeaders: true,filters: true,dropdownMenu: true
    });
})

结果显示正确,由于渲染器未显示复选框,单元格最初没有可见的复选框,然后当您单击单元格时,复选框出现。问题是当您单击复选框时,它不会切换。

我认为 handsontable 中的某些内容正在重新创建 td 并消除状态更改,但我不知道如何防止这种情况发生。是否有一个完全有效的自定义编辑器小提琴或我可以参考的东西来找出如何防止冒泡?

如果您能提供任何帮助,我们将不胜感激。

解决方法

好吧,又折腾了几天,我想出了我需要添加的完整代码集来完成这项工作。发布在这里以防它帮助其他人尝试在 handsontable 中制作自定义单元格编辑器/渲染器。

class SubstitutePartEditor extends Handsontable.editors.BaseEditor {
    init(){
        // This function creates the edit div
        let div = document.createElement('div');
        this.div = div;
        div.style.display = 'none';
        div.style.position = 'absolute';
        div.style.width = 'auto';
        div.style.backgroundColor = 'white';

        this.AttributeNames = ['The waste bin is part of the waste cart','This item includes SUPPLIER tapestry'];
        this.ctrls = {};


        let cbIsSubstitutePart = document.createElement('input');
        this.ctrls['IsSubstitutePart'] = cbIsSubstitutePart;
        cbIsSubstitutePart.type = 'checkbox';
        div.appendChild(cbIsSubstitutePart);

        div.appendChild(document.createElement('br'));
        let SubstitutePartSubDiv = document.createElement('div');
        SubstitutePartSubDiv.style.display = 'inline-block';
        div.appendChild(SubstitutePartSubDiv);

        let inputSubstitutePart = document.createElement('textarea');
        this.ctrls['SubstitutePart'] = inputSubstitutePart;
        inputSubstitutePart.style.width = '220px';
        inputSubstitutePart.style.height = '88px';
        SubstitutePartSubDiv.appendChild(inputSubstitutePart);

        this.hot.rootElement.appendChild(div);
    }

    UpdateDependentControls(){
        let RequiredCtrl = this.ctrls['IsSubstitutePart'];
        let Ctrl = this.ctrls['SubstitutePart'];

        if (RequiredCtrl.checked == true){
            Ctrl.style.display = '';
        } else {
            Ctrl.style.display = 'none';
        }

        $(RequiredCtrl).off('change').on('change',function(){
            if (RequiredCtrl.checked == true){
                Ctrl.style.display = '';
            } else {
                Ctrl.style.display = 'none';
            }
        });

    }

    getValue(){
        // This function returns the set value of the controls
        let ctrls = this.ctrls;
        let resultDict = {};
        for (let ctrlID in ctrls){
            let ctrl = ctrls[ctrlID];
            let FormattedAttributeName = ctrlID.replaceAll(' ','_');
            let val = null;
            if (ctrl.type == 'checkbox'){
                if (ctrl.checked == true) {
                    val = 'yes';
                } else {
                    val = null;
                }
            } else {
                val = ctrl.value;
            }
            resultDict[FormattedAttributeName] = val;
        }

        return JSON.stringify(resultDict)
    }

    setValue(value){
        // this function sets the value of the controls to match the data value
        let parsedValue = {};
        try {
            parsedValue = JSON.parse(Handsontable.helper.stringify(value));
        } catch (exc) {
            parsedValue = {
                IsSubstitutePart: 'no',SubstitutePart: "This item requires a waiver from the operator's foreign regulatory agency,<FOREIGN REGULATORY AGENCY NAME>."
            };
        }

        let ctrls = this.ctrls;
        let resultDict = {};
        for (let ctrlID in ctrls){
            let ctrl = ctrls[ctrlID];
            let FormattedAttributeName = ctrlID.replaceAll(' ','_');
            let val = parsedValue[FormattedAttributeName];
            if (ctrl.type == 'checkbox'){
                if (val == 'yes'){
                    ctrl.checked = true;
                } else {
                    ctrl.checked = false;
                }
            } else {
                ctrl.value = val;
            }
        }
        
    }

    saveValue(value,ctrlDown){
        super.saveValue(value,ctrlDown);
    }

    open() {
      this._opened = true;
      this.refreshDimensions();
      this.UpdateDependentControls();
      this.div.style.display = '';
    }

    refreshDimensions() {
        this.TD = this.getEditedCell();

        // TD is outside of the viewport.
        if (!this.TD) {
            this.close();
            return;
        }

        const { wtOverlays } = this.hot.view.wt;
        const currentOffset = Handsontable.dom.offset(this.TD);
        const containerOffset = Handsontable.dom.offset(this.hot.rootElement);
        const scrollableContainer = wtOverlays.scrollableElement;
        const editorSection = this.checkEditorSection();
        let width = Handsontable.dom.outerWidth(this.TD) + 1;
        let height = Handsontable.dom.outerHeight(this.TD) + 1;
        let editTop = currentOffset.top - containerOffset.top - 1 - (scrollableContainer.scrollTop || 0);
        let editLeft = currentOffset.left - containerOffset.left - 1 - (scrollableContainer.scrollLeft || 0);
        let cssTransformOffset;

        switch (editorSection) {
        case 'top':
          cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.topOverlay.clone.wtTable.holder.parentNode);
          break;
        case 'left':
          cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.leftOverlay.clone.wtTable.holder.parentNode);
          break;
        case 'top-left-corner':
          cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.topLeftCornerOverlay.clone.wtTable.holder.parentNode);
          break;
        case 'bottom-left-corner':
          cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.bottomLeftCornerOverlay.clone.wtTable.holder.parentNode);
          break;
        case 'bottom':
          cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.bottomOverlay.clone.wtTable.holder.parentNode);
          break;
        default:
          break;
        }

        if (this.hot.getSelectedLast()[0] === 0) {
            editTop += 1;
        }
        if (this.hot.getSelectedLast()[1] === 0) {
            editLeft += 1;
        }

        const selectStyle = this.div.style;

        if (cssTransformOffset && cssTransformOffset !== -1) {
            selectStyle[cssTransformOffset[0]] = cssTransformOffset[1];
        } else {
            Handsontable.dom.resetCssTransform(this.div);
        }

        const cellComputedStyle = Handsontable.dom.getComputedStyle(this.TD,this.hot.rootWindow);

        if (parseInt(cellComputedStyle.borderTopWidth,10) > 0) {
            height -= 1;
        }
        if (parseInt(cellComputedStyle.borderLeftWidth,10) > 0) {
            width -= 1;
        }

        selectStyle.height = `${height}px`;
        selectStyle.minWidth = `${width}px`;
        selectStyle.top = `${editTop}px`;
        selectStyle.left = `${editLeft}px`;
        selectStyle.margin = '0px';
    }

    getEditedCell() {
        const { wtOverlays } = this.hot.view.wt;
        const editorSection = this.checkEditorSection();
        let editedCell;

        switch (editorSection) {
            case 'top':
                editedCell = wtOverlays.topOverlay.clone.wtTable.getCell({
                    row: this.row,col: this.col
                });
                this.select.style.zIndex = 101;
                break;
            case 'corner':
                editedCell = wtOverlays.topLeftCornerOverlay.clone.wtTable.getCell({
                    row: this.row,col: this.col
                });
                this.select.style.zIndex = 103;
                break;
            case 'left':
                editedCell = wtOverlays.leftOverlay.clone.wtTable.getCell({
                    row: this.row,col: this.col
                });
                this.select.style.zIndex = 102;
                break;
            default:
                editedCell = this.hot.getCell(this.row,this.col);
                this.div.style.zIndex = '';
                break;
        }

        return editedCell < 0 ? void 0 : editedCell; 
    }

    focus() {
        this.div.focus();
    }

    close() {
        this._opened = false;
        this.div.style.display = 'none';
    }
}
function SubstitutePartRenderer(instance,td,row,col,prop,value,cellProperties) {
    // This function draws the multi checkboxes for the SubstitutePart input field
    // Note: if AttributeNames changes you must also update BFEPartMatrix_Edit.ascx line ~240 to match (<- this is where the data is saved)
    //Handsontable.renderers.HtmlRenderer.apply(this,arguments);

    let parsedValue = {};
    try {
        parsedValue = JSON.parse(Handsontable.helper.stringify(value));
    } catch {
        // nothing to do
    }

    Handsontable.dom.empty(td);

    let div = document.createElement('div');
    //div.style.whiteSpace = 'nowrap';
    div.style.display = 'block';
    td.appendChild(div);

    if (parsedValue.hasOwnProperty('IsSubstitutePart')) {
        if (parsedValue.IsSubstitutePart == 'yes') {
            
        } else {
            td.innerHTML = 'N/A';
            return;
        }
    } else {
        td.innerHTML = 'N/A';
        return;
    }

    let SubstitutePartSubDiv = document.createElement('div');
    SubstitutePartSubDiv.style.display = 'inline-block';
    div.appendChild(SubstitutePartSubDiv);

    // text area
    let inputSubstitutePart = document.createElement('label');
    inputSubstitutePart.innerHTML = parsedValue['SubstitutePart'].escape();
    inputSubstitutePart.style.width = '220px';
    inputSubstitutePart.style.height = '88px';
    SubstitutePartSubDiv.appendChild(inputSubstitutePart);

    return td;
}

然后像这样设置热列的渲染和编辑器

columns: [
        {renderer: CustomAttributesRenderer,editor: CustomAttributesEditor}
    ],

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。