根据第一个和第二个菜单选择填充第三个下拉菜单

Posted

技术标签:

【中文标题】根据第一个和第二个菜单选择填充第三个下拉菜单【英文标题】:Populate third drop down menu based on first & second menu selection 【发布时间】:2021-12-11 07:40:50 【问题描述】:

我正在使用以下代码动态填充三个下拉菜单“project_select”、“hr_select”和“id_select”。

$(document).ready(function() 

  var table = $('#example1').DataTable(
    data: dataSet1,
    orderCellsTop: true,
    columns: [
        data: "Project_Name"
      ,
      
        data: "ID"
      ,
      
        data: "HR"
      
    ],
    initComplete: function() 
      this.api().columns([0, 1, 2]).every(function() 
        var column = this;
        var colIdx = column.index();
        var node;
        var select;
        if (colIdx === 0) 
          node = $('#project_select');
          select = $('<select id="project_s" style="width: 20%"><option value=""></option></select>');
        
        if (colIdx === 2) 
          node = $('#hr_select');
          select = $('<select id="hr_s" style="width: 20%" multiple><option value=""></option></select>');
        
        if (colIdx === 1) 
          node = $('#id_select');
          select = $('<select id="id_s" style="width: 20%" multiple><option value=""></option></select>');

        

        select.appendTo($(node).empty())
          .on('change', function() 
            var val = $(this).val();
            if (colIdx === 0) 
              val = $.fn.dataTable.util.escapeRegex(val);
              column.search(val).draw();
              rebuildPositionSelect();
              rebuildIDSelect();
            
            if (colIdx === 2) 
              const vals = $('option:selected', this).map(function(index, element) 
                return $.fn.dataTable.util.escapeRegex($(element).val());
              ).toArray().join('|');
              column.search(vals.length > 0 ? '^(' + vals + ')$' : '', true, false).draw();
              rebuildIDSelect();
             else 
              const vals = $('option:selected', this).map(function(index, element) 
                return $.fn.dataTable.util.escapeRegex($(element).val());
              ).toArray().join('|');
              column.search(vals.length > 0 ? '^(' + vals + ')$' : '', true, false).draw();
            
          );

        column.data().unique().sort().each(function(val) 
          select.append('<option value="' + val + '">' + val + '</option>')
        );

      );


      function rebuildPositionSelect() 
        var select = $('#hr_select select').empty().append('<option value=""></option>');
        var column = table.column(2, 
          search: 'applied'
        );
        column.search('').draw();
        column.data().unique().sort().each(function(val) 
          select.append('<option value="' + val + '">' + val + '</option>');
        );
      

      function rebuildIDSelect() 
        var select = $('#id_select select').empty().append('<option value=""></option>');
        var column = table.column(1, 
          search: 'applied'
        );
        column.search('').draw();
        column.data().unique().sort().each(function(val) 
          select.append('<option value="' + val + '">' + val + '</option>');
        );
      

      $('#project_s').select2(
        placeholder: "Select Project:",
        allowClear: true,
        width: 'resolve'
      )
      $('#hr_s').select2(
        placeholder: "Select Hr(s):",
        closeOnSelect: false,
        allowClear: true,
        tags: true,
        width: 'resolve'
      );
      $('#id_s').select2(
        placeholder: "Select ID:",
        closeOnSelect: false,
        allowClear: true,
        tags: true,
        width: 'resolve'
      );


    
  );

);

目前,“hr_select”和“id_select”菜单根据用户在“project_select”中的单个选择填充值。我正在努力根据用户在“hr_select”中的多项选择来进一步填充“id_select”菜单。

错误示例:用户选择项目 A 和小时 0、10、20。“id_select”菜单应仅填充值“Spike”。该菜单当前填充了“Spike”和“Hit”的值。

关于如何修复我的函数rebuildIDSelect 以修复此错误的任何想法?

https://jsfiddle.net/dfahsjdahfsudaf/nL6q21g9/63/

谢谢。

编辑:“id_select”菜单应该只填充值 Spike,因为当您过滤项目 A 和小时 0、10、20 的整体数据表时。Spike 是表中列出的唯一值那些价值观。 Hit 仅与项目 A 和 Hours 0、10 相关联。因为 Hit 未在表中列为 Hour 20 的行。当用户选择 Hours 0,10 时,它不应显示在“id_select”菜单中,并且20 来自“hr_select”菜单。

【问题讨论】:

您能否说明为什么在您的示例中“id_select”菜单应该只填充值“Spike”?您使用的规则是什么? @andrewJames 我已经编辑了我的问题来回答你的问题。我希望我的回答有意义。 【参考方案1】:

逻辑要求的是基于所选 HR 值的 ID 值的交集。这比仅使用 0 or 10 or 20 复杂得多,因为它要求您考虑整个列中的数据。

以下方法不是一个完整的解决方案 - 我将在最后提到至少一个问题。另外,我怀疑可能有一种更有效的方法来实现以下内容,但这应该向您展示一种方法:

    构建一个对象,该对象显示每个项目和 HR 值组合的 ID 值集。

这是执行此操作的代码(使用 Fiddle 中的示例代码):

hrToIdMapper = ;

// builds an object which uses HR for the keys and 
// an array of IDs for each HR:
this.api().rows().every(function() 
  var row = this;
  let key = row.data().Project_Name + '-' + row.data().HR;
  let val = row.data().ID;
  if ( hrToIdMapper.hasOwnProperty(key) ) 
    // could be optimized to avoid array duplicates:
    hrToIdMapper[key].push( val ); 
   else 
    hrToIdMapper[key] = [ val ];
  
);

由此产生的数据是:


  "A-0": [ "Hit", "Spike" ],
  "A-10": [ "Hit", "Spike" ],
  "A-20": [ "Spike" ],
  "B-0": [ "Kick" ],
  "B-10": [ "Kick" ],
  "B-20": [ "Kick" ]

稍后我们将使用这些数据来帮助确定我们需要的正确结果的交集。

    if (colIdx === 2) 部分添加逻辑以构建选定值的数组。

逻辑:

const intersectInputs = $('option:selected', this).map(function(index, element) 
  return $(element).val();
).toArray().map(x => proj + '-' + x);

因此,例如,如果您选择了 Project = "A" 和 HR = "0, 10, 20",那么数组将如下所示:

[ "A-0", "A-10", "A-20" ]

这基本上是一个键数组,我们可以在步骤 1 中创建的数据结构中看到它们。

    使用上面显示的两种数据结构来构建我们需要的“交集结果”:
function buildIntersection(vals) 
  var arrs = [];

  vals.forEach((val) =>  
    arrs.push( hrToIdMapper[val] );
   );

  // iterate over each array:
  var results = arrs.reduce(function(prev, curr, idx, arr) 
    // find the overall intersection across all these arrays:
    return prev.filter(value => curr.includes(value))
  );

  return results;

这会从第 2 步获取我们的数据数组,并且对于该数组中的每个项目,它会从第 1 步中找到相关的“Hit/Spike/Kick”数组。

对于这些“Hit/Spike/Kick”数组中的每一个,它会找到交集(即存在于所有“Hit/Spike/Kick”数组中的那些值。

在我的示例中,结果是一个仅包含“Spike”的新数组:

[ "Spike" ]
    我们可以将此值传递给rebuildIDSelect() 函数,以确保在 ID 列中仅显示相关值。

这是一个演示:

var dataSet1 = [
    "Project_Name": "A",
    "ID": "Hit",
    "HR": "0",
  ,
  
    "Project_Name": "A",
    "ID": "Hit",
    "HR": "10",
  ,
  
    "Project_Name": "A",
    "ID": "Spike",
    "HR": "0",
  ,
  
    "Project_Name": "A",
    "ID": "Spike",
    "HR": "10",
  ,
  
    "Project_Name": "A",
    "ID": "Spike",
    "HR": "20",
  ,
  
    "Project_Name": "B",
    "ID": "Kick",
    "HR": "0",
  ,
  
    "Project_Name": "B",
    "ID": "Kick",
    "HR": "10",
  ,
  
    "Project_Name": "B",
    "ID": "Kick",
    "HR": "20",
  

];

$(document).ready(function() 

  var table = $('#example1').DataTable(
    data: dataSet1,
    orderCellsTop: true,
    columns: [
        data: "Project_Name"
      ,
      
        data: "ID"
      ,
      
        data: "HR"
      
    ],
    initComplete: function() 

      hrToIdMapper = ;

      // builds an object which uses HR for the keys and 
      // an array of IDs for each HR:
      this.api().rows().every(function() 
        var row = this;
        let key = row.data().Project_Name + '-' + row.data().HR;
        let val = row.data().ID;
        if ( hrToIdMapper.hasOwnProperty(key) ) 
          // could be optimized to avoid array duplicates:
          hrToIdMapper[key].push( val ); 
         else 
          hrToIdMapper[key] = [ val ];
        
      );
  
      this.api().columns([0, 1, 2]).every(function() 
        var column = this;
        var colIdx = column.index();
        var node;
        var select;
        if (colIdx === 0) 
          node = $('#project_select');
          select = $('<select id="project_s" style="width: 20%"><option value=""></option></select>');
        
        if (colIdx === 2) 
          node = $('#hr_select');
          select = $('<select id="hr_s" style="width: 20%" multiple><option value=""></option></select>');
        
        if (colIdx === 1) 
          node = $('#id_select');
          select = $('<select id="id_s" style="width: 20%" multiple><option value=""></option></select>');

        

        select.appendTo($(node).empty())
          .on('change', function() 
            var val = $(this).val();
            if (colIdx === 0) 
              val = $.fn.dataTable.util.escapeRegex(val);
              column.search(val).draw();
              rebuildPositionSelect();
              rebuildIDSelect([]);
            

            if (colIdx === 2) 
              var proj = $("#select2-project_s-container").text();

              const vals = $('option:selected', this).map(function(index, element) 
                return $.fn.dataTable.util.escapeRegex($(element).val());
              ).toArray().join('|');
              column.search(vals.length > 0 ? '^(' + vals + ')$' : '', true, false).draw();

              const intersectInputs = $('option:selected', this).map(function(index, element) 
                return $(element).val();
              ).toArray().map(x => proj + '-' + x);
 
             let intersect = buildIntersection(intersectInputs);
             
              rebuildIDSelect(intersect);

             else 
              const vals = $('option:selected', this).map(function(index, element) 
                return $.fn.dataTable.util.escapeRegex($(element).val());
              ).toArray().join('|');
              column.search(vals.length > 0 ? '^(' + vals + ')$' : '', true, false).draw();
            
          );

        column.data().unique().sort().each(function(val) 
          select.append('<option value="' + val + '">' + val + '</option>')
        );

      );

      function buildIntersection(vals) 
        var arrs = [];

        vals.forEach((val) =>  
          arrs.push( hrToIdMapper[val] );
         );

        var results = arrs.reduce(function(prev, curr, idx, arr) 
          return prev.filter(value => curr.includes(value))
        );

        return results;
      

      function rebuildPositionSelect() 
        var select = $('#hr_select select').empty().append('<option value=""></option>');
        var column = table.column(2, 
          search: 'applied'
        );
        column.search('').draw();
        column.data().unique().sort().each(function(val) 
          select.append('<option value="' + val + '">' + val + '</option>');
        );
      

      function rebuildIDSelect(intersect) 
        var select = $('#id_select select').empty().append('<option value=""></option>');
        var column = table.column(1,  search: 'applied' );
        if (intersect.length === 0) 
          column.search('').draw();
          column.data().unique().sort().each(function(val) 
            select.append('<option value="' + val + '">' + val + '</option>');
          );
         else 
          intersect.forEach((val) =>  
            select.append('<option value="' + val + '">' + val + '</option>');
           );
          column.search('^(' + intersect.join('|') + ')$', true, false).draw();
        
      

      $('#project_s').select2(
        placeholder: "Select Project:",
        allowClear: true,
        width: 'resolve'
      )
      $('#hr_s').select2(
        placeholder: "Select Hr(s):",
        closeOnSelect: false,
        allowClear: true,
        tags: true,
        width: 'resolve'
      );
      $('#id_s').select2(
        placeholder: "Select ID:",
        closeOnSelect: false,
        allowClear: true,
        tags: true,
        width: 'resolve'
      );


    
  );

);
<!doctype html>
<html>

  <head>
    <meta charset="UTF-8">
    <title>Demo</title>
    <script src="https://code.jquery.com/jquery-3.5.1.js"></script>
    <script src="https://cdn.datatables.net/1.10.22/js/jquery.dataTables.js"></script>
    <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.22/css/jquery.dataTables.css">
    <link rel="stylesheet" type="text/css" href="https://datatables.net/media/css/site-examples.css">
    <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
    <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>

  </head>

  <body>

     <div style="margin: 20px;">

      <div>
        <span>Project: </span>
        <span id="project_select"></span>
        <span> HR: </span>
        <span id="hr_select"></span>
        <span>ID: </span>
        <span id="id_select"></span>
      </div>

      <br><br>

      <table id="example1" class="display dataTable cell-border" style="width:100%">
        <thead>
          <tr>
            <th>Project_Name</th>
            <th>ID</th>
            <th>HR</th>
          </tr>
        </thead>
      </table>


    </div>



  </body>

</html>

最终结果是这样的:


演示不完整

剩下的一个问题(可能还有更多!):一旦用户做出选择并创建了屏幕截图所示的最终结果,用户可以选择然后删除 ID 下拉列表中的“Spike”标签-下。这将导致重新显示不需要的“命中”行。

处理这样的边缘情况可能会产生很多额外的复杂性。

最后一点,这只是我个人的看法:总的来说,无论如何,如果不清楚为什么他们会看到他们所看到的结果,这最终可能会让用户感到困惑。如果下拉逻辑太复杂,用户可能会怀疑他们看到的内容的正确性。

【讨论】:

以上是关于根据第一个和第二个菜单选择填充第三个下拉菜单的主要内容,如果未能解决你的问题,请参考以下文章

使用 thymeleaf、spring boot 的动态下拉菜单

根据另一个下拉列表中的选择填充一个下拉列表,然后重定向

Wordpress 选项页面中动态生成的下拉菜单

使用与第一个下拉列表相同的值填充第二个下拉列表

jquery依赖下拉菜单选择

第一个下拉菜单更改第二个下拉菜单相关项目显示