Derrick.Jiao 发表于 2022-2-22 15:21:48

自定义单元格实现查询下拉(AutoComplete)

本帖最后由 Derrick.Jiao 于 2022-2-22 15:32 编辑

背景:在一些实际的使用场景中,有的朋友有这样一个需求,根据用户输入的字符,去后台ajax查询,动态加载数据到下拉列表中。我们知道,想要实现下拉列表,可以用,list实现,这是学习指南的链接,但是原生的list不支持搜索以及模糊匹配。https://demo.grapecity.com.cn/sp ... p-downs/list/purejs

于是我们可以通过自定义单元格来实现,就像下面这个demo。但是,下面这个demo,又可能不太满足需求,demo的数据是hardcode的,并且不能像list那样,去存储类似的键值对。
https://demo.grapecity.com.cn/Sp ... /demos/autoComplete

那么这篇帖子就是教大家如何实现一个满足搜索,并且能够从后台查询的这样一个自定义单元格。那这个自定义单元格需要与第三方的插件配合,能够让我们事半功倍,这样我们就只要专注于自定义单元格的逻辑即可。整体逻辑就是,查询到响应的lastname,那么就会返回这个那么与id,查询不到,那么也能正常填写,id为-1。



另外,这个插件叫Select2,是一款基于JQuery的下拉列表插件,主要用来优化select,支持单选和多选,同时也支持分组显示、列表检索、远程获取数据等众多好用的功能。这是项目地址:https://select2.org/ ,感兴趣的朋友可以去了解一下。没时间也不用担心,我在demo中做了“保姆级”的注释,包教包会,不会再来~

在开始前,我已经默认你是会自定义单元格了,如果还不太会,可以看下我们学习指南的教程
https://demo.grapecity.com.cn/sp ... types/custom/purejs

那我们就开始吧。我们先指定一个typeName,并且挂在window上, 这样spread可以通过typeName进行查找。主要用在序列化与反序列化以及自定义单元格的复制粘贴
function AutoCompleteCellType() {
            this.typeName = "MyAutoComplete";
      }

      window["MyAutoComplete"] = AutoCompleteCellType;
下一步就是继承base这个cellType,这里就不赘述了。再往下我们就是要进行方法的重写,第一个就是重写createEditorElement。该方法的意思是创建编辑元素,因为在每次进入编辑状态是都会创建编辑框,这个时候就会触发createEditorElement来创建编辑框相关元素。在该方法中我们一般将编辑框的基本dom元素建立起来,并作为上下文return出去。
AutoCompleteCellType.prototype.createEditorElement = function (context) {
            var editor;
            editor = document.createElement("div");

            //声明了这个属性之后,该dom会被SpreadJS认为是SpreadJS本身的一部分,这样在SpreadJS本身的生命周期做处理的时候也会包含该dom
            editor.setAttribute("gcUIElement", "gcEditingInput");
            editor.appendChild(document.createElement("select"));
            editor.children.style.width = "100%";
            editor.children.style.height = "100%";

            //获取单元格的矩阵区域宽高,并给到select2作为其宽高
            var cellRect = context.sheet.getCellRect(context.row, context.col);
            editor.style.width = cellRect.width + "px";
            editor.style.height = cellRect.height + "px";

            //监听选中,选择完成后结束编辑
            $(editor).find("select").on("select2:select", function (e) {
                context.sheet.endEdit();
            });

            //监听open,对于自定义的元素,需要添加gcUIElement属性,
            //如果元素或者其父元素没有该属性,点击创建的组件便会直接退出编辑状态无法编辑
            $(editor).find("select").on("select2:open", function (e) {
                $(".select2-dropdown").attr("gcUIElement", "gcEditingInput");
            });

            return editor;
      };
接着就是绘制在单元格上进行绘制的paint方法。这里面我们实际不需要在上面做其他设置所以直接调用apply,执行在canvas上绘制的形式。
AutoCompleteCellType.prototype.paint = function (ctx,value,x,y,w,h,style,options) {
            if (value) {
                GC.Spread.Sheets.CellTypes.Base.prototype.paint.apply(this, );
            }
      };
再下来就是触发编辑器的方法activateEditor。该方法在每次激活编辑状态时触发,触发的顺序是先触发createEditorElement之后再触发activateEditor,并且activateEditor中可以获取到eContext这个上下文,该上下文就是createEditorElement返回的结果。这里面,我们通过ajax去请求我们的数据源,拿到数据源后,我们可以通过select2的内置的方法进行搜索和匹配。
    AutoCompleteCellType.prototype.activateEditor = function (eContext,cellStyles,cellRect) {
                $(eContext).find("select").select2({
                  ajax: {
                        url: "https://raw.githubusercontent.com/kshkrao3/JsonFileSample/master/select2resp.json",
                        // url:"http://localhost/data.json",
                        dataType: "json",
                        delay: 250,
                        data: function (params) {
                            return {
                              q: params.term, // search term
                                    
                            };
                        },
                        processResults: function (data, params) {
                        // parse the results into the format expected by Select2
                            var resData = [];
                            console.log(data)
                            data.forEach(function (value) {
                              if (value.LastName.indexOf(params.term) != -1) resData.push(value);
                            });
                            resData = $.map(resData, function (item) {
                              return {
                                    text: item.LastName,
                                    id: item.Id,
                              };
                            });
                            if (resData.length === 0) {
                              resData.push({
                                    text: params.term,
                                    id: -1,
                              });
                            }
                            return {
                              results: resData,
                            };
                        },
                        cache: true,
                  },
                  minimumInputLength: 1,
                });

                setTimeout(() => {
                  $(eContext).find("select").select2("open");
                });
            };当我们查询完成后,需要将下拉框销毁,用的也是select2的方法AutoCompleteCellType.prototype.deactivateEditor = function (eContext, context) {
            $(eContext).find("select").select2("destroy");
      };
再接着就是关于编辑器与单元格之间值的设置与获取,我们可以重写下面两个方法,将单元格的值以遮掩给一个id+value的对象返回。
AutoCompleteCellType.prototype.getEditorValue = function (eContext) {
                var data = $(eContext).find("select").select2("data");
                if (!data || !data.length) {
                  return null;
                }
                return Object.assign(
                  {},
                  {
                        id: data.id,
                        value: data.text,
                  }
                );
            };
            AutoCompleteCellType.prototype.setEditorValue = function (eContext, val) {
                if (!val) {
                  return;
                }

                var op = new Option(val.value, val.id, true, true);
                $(eContext).find("select").append(op).trigger("change");
            };
最后就是处理保留键。例如当按键为以下按键时,按下箭头下键return true也就是不会进入下一个单元格,而是在自定义的列表中向下选择。其他也是同理。
   AutoCompleteCellType.prototype.isReservedKey = function (e, context) {
                if (
                  context.isEditing &&
                  (e.keyCode == 40 || e.keyCode == 38 || e.keyCode == 13 || e.keyCode == 27)
                ) {
                  return true;
                }
                return GC.Spread.Sheets.CellTypes.Text.prototype.isReservedKey.apply(
                  this,
                  arguments
                );
            };
以上就是关于这个自定义单元格的讲解,最重要还是拿到代码亲自试一试。以防请求的数据丢失,这边也单独把这个json上传,后续也可以部署在本地进行访问测试~


页: [1]
查看完整版本: 自定义单元格实现查询下拉(AutoComplete)