javascript 如何在没有ajax的情况下在select2 4.0中启用无限滚动

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32756698/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 15:43:21  来源:igfitidea点击:

How to enable infinite scrolling in select2 4.0 without ajax

javascriptjqueryjquery-select2jquery-select2-4

提问by Paperback Writer

I am using select2with custom data adapter. All of the data provided to select2is generated locally in web page (so no need to use ajax). As querymethod can generate a lot of results (about 5k) opening select box is quite slow.

我正在使用select2自定义数据适配器。提供给的所有数据select2都是在网页本地生成的(因此无需使用ajax)。由于query方法可以产生很多结果(大约5k),打开选择框很慢。

As a remedy, I wanted to use infinite scroll. Documentationfor custom data adapter says that querymethod should receive pageparameter together with term:

作为补救措施,我想使用无限滚动。自定义数据适配器的文档说明该query方法应pageterm以下参数一起接收参数:

@param params.page The specific page that should be loaded. This is typically provided when working with remote data sets, which rely on pagination to determine what objects should be displayed.

@param params.page 应该加载的特定页面。这通常是在处理远程数据集时提供的,这些数据集依赖于分页来确定应该显示哪些对象。

But it does not: only termis present. I tried to return more: trueor more: 1000, but this didn't help. I guess this is because, by default, infinite scroll is enabled iff ajax is enabled.

但它没有:只有term存在。我试图返回more: trueor more: 1000,但这没有帮助。我猜这是因为,默认情况下,如果 ajax 启用无限滚动是启用的

I am guessing that enabling infinite scroll will involve using amd.require, but I am not sure what to do exactly. I tried this code:

我猜想启用无限滚动将涉及使用amd.require,但我不确定该怎么做。我试过这个代码:

$.fn.select2.amd.require(
    ["select2/utils", "select2/dropdown/infiniteScroll"],
    (Utils, InfiniteScroll) =>
      input.data("select2").options.options.resultsAdapter = 
        Utils.Decorate(input.data("select2").options.options.resultsAdapter, InfiniteScroll)
)

This is coffee script, but I hope that it is readable for everyone. inputis DOMelement containing select box - I earlier did input.select2( //options )

这是咖啡脚本,但我希望它对每个人都可读。inputDOM包含选择框的元素 - 我之前做过input.select2( //options )

My question is basically, how do I enable infinite scroll without ajax?

我的问题基本上是,如何在没有ajax?

回答by Paperback Writer

Select2will only enable infinite scroll, if ajaxis enabled. Fortunately we can enable it and still use our own adapter. So putting empty object into ajaxoption will do the trick.

Select2如果ajax启用,将只启用无限滚动。幸运的是,我们可以启用它并仍然使用我们自己的适配器。因此,将空对象放入ajaxoption 即可解决问题。

$("select").select2({
  ajax: {},
  dataAdapter: CustomData
});

Next, define your own data adapter. Inside it, inn querypush paginationinfo into callback.

接下来,定义您自己的数据适配器。在它里面,客栈querypagination信息推送到回调中。

    CustomData.prototype.query = function (params, callback) {
        if (!("page" in params)) {
            params.page = 1;
        }
        var data = {};
        # you probably want to do some filtering, basing on params.term
        data.results = items.slice((params.page - 1) * pageSize, params.page * pageSize);
        data.pagination = {};
        data.pagination.more = params.page * pageSize < items.length;
        callback(data);
    };

Here is a full fiddle

这是一个完整的小提琴

回答by mothmonsterman

Expanding on this answerto show how to retain the search functionality that comes with select2. Thanks Paperback Writer!

扩展此答案以展示如何保留 select2 附带的搜索功能。感谢平装作家

Also referenced this exampleof how to achieve infinite scrolling using a client side data source, with select2 version 3.4.5.

还参考了如何使用客户端数据源实现无限滚动的示例,select2 版本为 3.4.5。

This example uses the oringal options in a select tag to build the list instead of item array which is what was called for in my situation.

此示例使用 select 标签中的 oringal 选项来构建列表,而不是在我的情况下需要的项目数组。

function contains(str1, str2) {
    return new RegExp(str2, "i").test(str1);
}

CustomData.prototype.query = function (params, callback) {
    if (!("page" in params)) {
        params.page = 1;
    }
    var pageSize = 50;
    var results = this.$element.children().map(function(i, elem) {
        if (contains(elem.innerText, params.term)) {
            return {
                id:[elem.innerText, i].join(""),
                text:elem.innerText
            };
        }
    });
    callback({
        results:results.slice((params.page - 1) * pageSize, params.page * pageSize),
        pagination:{
            more:results.length >= params.page * pageSize
        }
    });
};

Here is a jsfiddle

这是一个jsfiddle

回答by prograhammer

I felt the answers above needed better demonstration. Select2 4.0.0 introducesthe ability to do custom adapters. Using the ajax: {}trick, I created a custom dataAdapter jsonAdapterthat uses local JSON directly. Also notice how Select2's 4.0.0 release has impressive performance using a big JSON string. I used an online JSON generatorand created 10,000 names as test data. However, this example is very muddy. While this works, I would hope there is a better way.

我觉得上面的答案需要更好的演示。Select2 4.0.0引入了定制适配器的能力。使用这个ajax: {}技巧,我创建了一个jsonAdapter直接使用本地 JSON的自定义 dataAdapter 。还要注意 Select2 的 4.0.0 版本如何使用大 JSON 字符串获得令人印象深刻的性能。我使用了一个在线 JSON 生成器并创建了 10,000 个名称作为测试数据。但是,这个例子非常混乱。虽然这有效,但我希望有更好的方法。

See the full fiddle here: http://jsfiddle.net/a8La61rL/

在这里查看完整的小提琴:http: //jsfiddle.net/a8La61rL/

 $.fn.select2.amd.define('select2/data/customAdapter', ['select2/data/array', 'select2/utils'],
    function (ArrayData, Utils) {
        function CustomDataAdapter($element, options) {
            CustomDataAdapter.__super__.constructor.call(this, $element, options);
        }

        Utils.Extend(CustomDataAdapter, ArrayData);

        CustomDataAdapter.prototype.current = function (callback) {
            var found = [],
                findValue = null,
                initialValue = this.options.options.initialValue,
                selectedValue = this.$element.val(),
                jsonData = this.options.options.jsonData,
                jsonMap = this.options.options.jsonMap;

            if (initialValue !== null){
                findValue = initialValue;
                this.options.options.initialValue = null;  // <-- set null after initialized              
            }
            else if (selectedValue !== null){
                findValue = selectedValue;
            }

            if(!this.$element.prop('multiple')){
                findValue = [findValue];
                this.$element.html();     // <-- if I do this for multiple then it breaks
            }

            // Query value(s)
            for (var v = 0; v < findValue.length; v++) {              
                for (var i = 0, len = jsonData.length; i < len; i++) {
                    if (findValue[v] == jsonData[i][jsonMap.id]){
                       found.push({id: jsonData[i][jsonMap.id], text: jsonData[i][jsonMap.text]}); 
                       if(this.$element.find("option[value='" + findValue[v] + "']").length == 0) {
                           this.$element.append(new Option(jsonData[i][jsonMap.text], jsonData[i][jsonMap.id]));
                       }
                       break;   
                    }
                }
            }

            // Set found matches as selected
            this.$element.find("option").prop("selected", false).removeAttr("selected");            
            for (var v = 0; v < found.length; v++) {            
                this.$element.find("option[value='" + found[v].id + "']").prop("selected", true).attr("selected","selected");            
            }

            // If nothing was found, then set to top option (for single select)
            if (!found.length && !this.$element.prop('multiple')) {  // default to top option 
                found.push({id: jsonData[0][jsonMap.id], text: jsonData[0][jsonMap.text]}); 
                this.$element.html(new Option(jsonData[0][jsonMap.text], jsonData[0][jsonMap.id], true, true));
            }

            callback(found);
        };        

        CustomDataAdapter.prototype.query = function (params, callback) {
            if (!("page" in params)) {
                params.page = 1;
            }

            var jsonData = this.options.options.jsonData,
                pageSize = this.options.options.pageSize,
                jsonMap = this.options.options.jsonMap;

            var results = $.map(jsonData, function(obj) {
                // Search
                if(new RegExp(params.term, "i").test(obj[jsonMap.text])) {
                    return {
                        id:obj[jsonMap.id],
                        text:obj[jsonMap.text]
                    };
                }
            });

            callback({
                results:results.slice((params.page - 1) * pageSize, params.page * pageSize),
                pagination:{
                    more:results.length >= params.page * pageSize
                }
            });
        };

        return CustomDataAdapter;

    });

var jsonAdapter=$.fn.select2.amd.require('select2/data/customAdapter');

回答by Robert McKee

I found it was just easier to hiHyman the ajax adapter rather than creating a whole new CustomAdapter like the above answers. The above answers don't actually seem to support paging because they all start from array, which doesn't support paging. It also doesn't support delayed processing.

我发现劫持 ajax 适配器比像上面的答案一样创建一个全新的 CustomAdapter 更容易。上面的答案实际上似乎并不支持分页,因为它们都从不支持分页的数组开始。它也不支持延迟处理。

window.myarray = Array(10000).fill(0).map((x,i)=>'Index' + i);
    
let timer = null;
$('select[name=test]')
    .empty()
    .select2({
        ajax: {
            delay: 250,
            transport: function(params, success, failure) {
                let pageSize = 10;
                let term = (params.data.term || '').toLowerCase();
                let page = (params.data.page || 1);

                if (timer)
                    clearTimeout(timer);

                timer = setTimeout(function(){
                    timer = null;
                    let results = window.myarray // your base array here
                    .filter(function(f){
                        // your custom filtering here.
                        return f.toLowerCase().includes(term);
                    })
                    .map(function(f){
                        // your custom mapping here.
                        return { id: f, text: f}; 
                    });

                    let paged = results.slice((page -1) * pageSize, page * pageSize);

                    let options = {
                        results: paged,
                        pagination: {
                            more: results.length >= page * pageSize
                        }
                    };
                    success(options);
                }, params.delay);
            }
        },
        tags: true
    });
<link href="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.7/css/select2.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.7/js/select2.full.min.js"></script>
<select name='test' data-width="500px"><option>test</option></select>

回答by lofihelsinki

Here's a shorter, searchable version for Select2 v4 that has paging. It uses lo-dashfor searching.

这是具有分页功能的 Select2 v4的较短的可搜索版本。它使用lo-dash进行搜索。

EDITNew fiddle: http://jsfiddle.net/nea053tw/

编辑新小提琴:http: //jsfiddle.net/nea053tw/

$(function () {
    items = []
    for (var i = 0; i < 1000; i++) {
        items.push({ id: i, text : "item " + i})
    }

    pageSize = 50

    jQuery.fn.select2.amd.require(["select2/data/array", "select2/utils"],

    function (ArrayData, Utils) {
        function CustomData($element, options) {
            CustomData.__super__.constructor.call(this, $element, options);
        }
        Utils.Extend(CustomData, ArrayData);

        CustomData.prototype.query = function (params, callback) {

            var results = [];
            if (params.term && params.term !== '') {
              results = _.filter(items, function(e) {
                return e.text.toUpperCase().indexOf(params.term.toUpperCase()) >= 0;
              });
            } else {
              results = items;
            }

            if (!("page" in params)) {
                params.page = 1;
            }
            var data = {};
            data.results = results.slice((params.page - 1) * pageSize, params.page * pageSize);
            data.pagination = {};
            data.pagination.more = params.page * pageSize < results.length;
            callback(data);
        };

        $(document).ready(function () {
            $("select").select2({
                ajax: {},
                dataAdapter: CustomData
            });
        });
    })
});

The search loop is originally from these old Select4 v3 functions: https://stackoverflow.com/a/25466453/5601169

搜索循环最初来自这些旧的 Select4 v3 函数:https://stackoverflow.com/a/25466453/5601169

回答by Max

This is not a direct answer: after struggling a lot with this for a while, I ended up switching to selectize. Select2's support for non-Ajax search, since version 4, is terribly complicated, bordering the ridiculous, and not documented well. Selectize has explicit support for non-Ajax search: you simply implement a function that returns a list.

这不是一个直接的答案:在为此苦苦挣扎了一段时间后,我最终切换到了选择。Select2 对非 Ajax 搜索的支持,从第 4 版开始,非常复杂,近乎荒谬,并且没有很好的记录。Selectize 明确支持非 Ajax 搜索:您只需实现一个返回列表的函数。