【问题标题】:Multiple tables with individual search inputs in serverSide mode using DataTables使用 DataTables 在服务器端模式下具有单独搜索输入的多个表
【发布时间】:2019-09-17 10:16:56
【问题描述】:

应用程序正在使用:

  • 数据表 1.10.18
  • jQuery 3.2.1
  • PHP 后端
  • lodash 4.17.4

应用程序包含一个由多个数据表组成的网页。其中每一个都使用serverSide: true(服务器端模式)通过返回 JSON 数据的 ajax 端点获取数据。

表初始化如下:

  1. 在页面加载时会呈现几个<table>。我正在使用 jquery .each() 为每个初始化 DataTable:

    $.each($('table'), function () {
    
        $(this).DataTable({
            processing: true,
            serverSide: true,
            searching: false,
            ajax: {
                data: {
                   table_id: $(this).attr('id') 
            },
            url: '/get-data.json', 
        },
        ...
    });
    
  2. 每个<table> 都有一个ID。这是通过 data: 属性中的 ajax 传递的。端点/get-data.json根据表ID返回数据。换句话说,它根据这个ID知道应该为“哪个表”获取数据。

我希望能够在表格上进行搜索,但必须在服务器端完成。出于这个原因,我在 (1) 中的初始化代码设置了 searching: false,因为这有效地禁用了 DataTables 提供的客户端搜索工具(我们不能在这种情况下使用它,因为搜索必须在服务器端完成)。

我面临的问题是如何为每个表创建搜索输入、进行 ajax 调用并更新相应的表。我希望在输入 >=3 个字符后实时搜索。这个问题的关键是 1 个搜索输入负责搜索 1 个数据表 - 它 不是 一个搜索功能,其中输入可以更新“页面上的任何/每个表”,这是其他常用描述的模式问题。 1 个输入:在这种情况下搜索 1 个表。

我的计划如下 - 第 (2) 点中引用的每个表都有一个 ID。我需要创建独特的输入。因此,如果我有 ID 为 #table1、#table2、#table3 的表,我可以轻松创建:

<input type="text" name="table1_search" id="table1_search"> 
<input type="text" name="table2_search" id="table2_search"> 
<input type="text" name="table3_search" id="table3_search"> 

然后我检测输入是否发生了任何变化:

$('input[type="text"]').bind("keyup change input",
        function (e) {
            // Ignore tab key for keyup event otherwise it'll fire an ajax request that does nothing useful.
            if (e.which !== 9) {
                processSearch.call(this);
            } else {
                e.preventDefault();
            }
        });

    var prev_value = {};
    function processSearch() {
        var obj = $(this),
                search_id = obj.attr('id'), // ID of input
                search_value = obj.val(); // Value of input

        // There's been no change to the field, ignore.
        if (prev_value[search_id] === search_value) {
            return;
        }

        prev_value[search_id] = search_value;

        /* Wait until at least 3 characters have been entered, or user has cleared the input */
        if (search_value.length >= 3 || (!search_value)) {
            debouncedDraw({search_id: search_id, search_value: search_value});
        }
    }

上面的代码在等待输入 >=3 个字符方面完成了我所需要的。然后我正在执行一个名为debouncedDraw 的函数,它传递一个包含search_id 和search_value 的对象。这些分别指的是输入 ID 和值,例如如果我在#table1_search 中输入“foo”,那么对象是:

{search_id: 'table1_search', search_value: 'foo'}

debouncedDraw 函数如下所示。这是使用lodash 来限制函数可以触发的速率。这里的重点是根据我几年前在这里提出的一个问题来阻止它发出不必要的 ajax 请求:DataTables - kill ajax requests when a new one has started:

var debouncedDraw = _.debounce(function (opts) {
    console.log(opts);
}, 500);

目前这只是console.log上面给出的对象。

我不确定目前进行的最佳方式。我需要通过 ajax 重新运行/get-data.json,然后更新相应的表。

我可以访问请求数据并根据下划线拆分search_id,以计算出数据用于哪个表ID(例如table1_search目标#table1)。然后我需要将此数据写回相应的表(在这种情况下为#table1)。

我不禁想我会以一种令人费解的方式来解决这个问题,并想知道 DataTables 本身是否有更好的方法来支持这一点?这似乎是一个非常基本的要求(serverSide 模式下的多个可搜索表)。但我找不到任何具体说明如何执行此操作的帖子。

【问题讨论】:

    标签: javascript php jquery ajax datatables


    【解决方案1】:

    我多年来经历的所有“陷阱”都封装在下面的 sn-p 中。这是我在创建新数据表时经常使用的基本模板。您可以使用此模式在页面上创建任意数量的数据表。

    我个人会为每个表使用不同的 ajax url 路径/路由,以便表逻辑位于后端的单独文件中......但可以将所有数据逻辑放在单个后端文件中。我修改了我常用的模板以适应它。

    
    <script> //I usually put the script section in the head tag
    
    var table_1; //declare your table var here and initialize as a datatable inside document ready below.
    
    $(document).ready(function() {
    
        table_1 = $('#table_1').DataTable( {
            dom: "Bfrtip",
            ajax: {
                url: "/get-data.json?table=table_1",  //add query string var for backend routing
                type: "POST"  //use POST to not have to deal with url encoding various characters
            },      
            serverSide: true,
            searchDelay: 2000,  // use this instead of custom debounce
            processing: true, // optional visual indicator that a search has been sent to backend
            lengthMenu: [ 10, 25, 50, 75, 100 ], // define per page limits. first value will be the default
            buttons: [
                "pageLength" // per page drop down button. i usually override/extend the default button
            ],      
            columns: [ // column definitions of json data fields
                { data: "col_1", title: "ID", width: "1%" },  // width: 1% makes col width as small as possible
                { data: "col_2", title: "Label 2", visible:false }, //visible: false allows you access to field data without displaying to user
                { data: "col_3", title: "Label 3", render: function ( data, type, row ) { //render allows combining of fields into single column
                        return data + ' <small>('+row.col_2+')</small>'; // data will be col_3 value. row.col_2 is how you reference col_2 value
                } },
                { data: "col_4", title: "Label 4", searchable:false }, //searchable: false set this field to not be used in search
            ],
            rowId: 'col_1' //sets the tr row id to the value in this column. useful for DOM and other manipulation later
        } );
    }
    
    </script>
    
    <table id="table_1" class="table table-striped table-bordered table-sm" style="width:100%"></table>
    
    <!-- If you define title attributes in col definitions above you don't need to create html table headers/footers. Just an empty table tag will do. -->
    

    使用此模式,您可以将数据表随附的内置搜索输入用于您的用例,并在所有表上进行服务器端处理。

    在我的疯狂背后有一个方法,我试图在每一行的脚本 cmets 中记录它。如果您对某事有任何疑问,请告诉我。我认为这是值得赏金的。

    作为参考,在使用数据表开发新应用程序时,我基本上住在这个页面上https://datatables.net/reference/option/

    编辑 1

    在您现有的 debounced drawTable 函数中,您可以执行以下操作:

    function drawTable(id) {
        $('#'+id).DataTable().ajax.url( 'get-data.json?table_id='+id+'&foo=bar' ); //update ajax url of existing dt - if necessary
        $('#'+id).DataTable().search(search_input_val).draw(); // fire ajax request with value from your custom search input
    
    }
    
    

    我很确定您需要将“搜索”设置为 true 才能使此方法起作用。

    编辑 2

    我刚刚想到的另一种方式,不使用 dt 搜索。通过修改后的 url 传递所有数据并加载/重新加载。

    $('#'+id).DataTable().ajax.url( 'get-data.json?table_id='+id+'&search=foo' ).load();
    
    

    如果您在输入字段上使用按钮单击侦听器或 onblur 侦听器并触发上述相同的命令,那么您可以摆脱所有去抖动的东西。

    你见过吗? https://datatables.net/reference/api/%24.fn.dataTable.util.throttle()

    我以前从未使用过它,但它看起来像去抖动。页面上的示例显示它被用于 .search()

    【讨论】:

    • 我已经尝试过了,不幸的是,这是一个比我所拥有的更糟糕的解决方案。我们使用自定义输入并进行去抖动的原因是为了确保它不会发出毫无意义的 ajax 请求 - 请参阅我的问题中链接的 SO 帖子。即使您使用searchDelay 然后键入关键字,它也会发出多个ajax 请求(最初是一个,后来是延迟的)。我的代码缓解了这一点,并确保在执行搜索之前输入了 >=3 个字符。我认为给出的代码中没有其他任何东西可以改进它?
    • 我明白你的意思,多 ajax 请求是在输入你的主要问题吗?还是您当前解决方案的“破坏性”方面?
    • 这是问题中提到的所有内容;)它面临着多重挑战。链接的 SO 帖子非常重要,因为这对性能非常关键,尤其是当有多个表时。销毁/重新创建似乎是一个性能瓶颈 - 并且在 DataTables 文档中也被提及为不可取的。所以这是我现在没有解决方案。
    • 好吧,你为什么不在你的 debounce 函数中使用 .search() API 呢?触发 dt 搜索而不是销毁。
    • 嗯,这就是问题所在。如果您有可以演示如何做到这一点的代码,我很乐意看到它。
    【解决方案2】:

    我已经实现了以下,但更喜欢更好的解决方案,因为我认为这不是有效的,而且绝对不优雅!

    从问题中获取代码,我修改了 debounce 函数如下:

    var debouncedDraw = _.debounce(function (opts) {
       // Destroy the existing DataTable. 
       $('#' + opts.search_region).DataTable().destroy(); 
    
       // Re-run the drawTable method to get the new DataTable with the search results
       drawTable(opts.search_region);
    }, 500);
    

    我介绍了一个名为drawTable 的函数,它采用&lt;table&gt; 的ID 并运行DataTables 初始化代码。还修改了 ajax 对象,以考虑输入到给定表 ID 的搜索关键字输入中的任何内容:

    function drawTable(id) {
        $id = $('#'+id); // Convert string ID to jquery identifier
    
        $id.DataTable({
            // DataTable initialisation code as per question
            ajax: {
                data: {
                   table_id: id,  
                   keywords: $('input[name="keywords_' + id + '"]').val() // search keywords for table_id
                },
                url: '/get-data.json', 
            },
            // ... code as per question
        });
    }
    

    修改了$.each(),使其在页面加载时检测每个&lt;table&gt;的ID并调用drawTable:

    $.each($('table'), function () {
        drawTable($(this).attr('id'));
    });
    

    这“有效”是因为它在页面加载时创建每个所需的 DataTable,并且还处理搜索。搜索输入名称已修改为以下格式:keywords_ 加上表的 ID,例如keywords_table1.

    我不认为这很有效,因为我必须在我的 DataTable 上调用 destroy。根据docs:

    这会对页面的性能造成非常显着的影响,因为涉及到大量的计算和 DOM 操作,所以如果您可以避免这种情况并使用 API,我们强烈建议您这样做!

    但是我这样做的原因也与相同的文档中给出的一样:

    DataTables 不允许在初始化时以外的任何时间更改初始化选项。初始化后对表的任何操作都必须通过 API 完成

    好吧,我没有使用客户端搜索功能,因为我必须在服务器端进行搜索。所以我不确定通过 API 操作表格是否真的会在这种情况下有所帮助。

    有没有更好的方法来实现这一点?

    【讨论】:

    • 为了澄清,您是否在后端使用了 PHP 的 Datatables“编辑器”? editor.datatables.net我已经制作了几个数据表重型应用程序。本周刚做了一个,所有服务器端的页面上有 8 个。编辑器让事情变得更容易。
    • 另外设置“searching”为false,因为你想实现服务器端搜索没有意义。这迫使您使用您提到的多个手动编码输入字段重新发明轮子。你可以有 search:true AND serverSide:true.
    • @Aaron 不,我没有使用编辑器,因为这组特定的表格只显示数据。应用程序的这一部分没有编辑它的功能。
    • 知道了,我问的原因是因为它带有多种语言的后端库。我也在“仅显示”数据表中使用这些库。
    猜你喜欢
    • 2016-02-11
    • 2011-12-28
    • 1970-01-01
    • 1970-01-01
    • 2016-12-30
    • 2017-12-28
    • 1970-01-01
    • 2013-12-28
    • 2017-09-09
    相关资源
    最近更新 更多