jquery - 根据第一个和第二个菜单选择填充第三个下拉菜单
问题描述
我正在使用以下代码动态填充三个下拉菜单“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”菜单。
解决方案
逻辑要求的是基于所选 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”标签。这将导致重新显示不需要的“命中”行。
处理这样的边缘情况可能会产生很多额外的复杂性。
最后一点,这只是我个人的看法:总的来说,无论如何,如果不清楚他们为什么看到他们所看到的结果,它最终可能会给用户带来混乱的体验。如果下拉逻辑变得过于复杂,用户可能会怀疑他们所看到的内容的正确性。
推荐阅读
- sql - 两列 SQL 之间的天数
- c# - WCF C# 系统初始化失败
- javascript - 在 ReactJS 中修改 state 对象直接重新渲染组件
- jquery - 如何序列化表单数据以包含子集合?
- rx-java2 - RxJava2 connectableObservables - autoConnect(2) - 为什么不等待 2 个订阅者调用 connect?
- laravel - 很难从 Laravel 集合中获取 Vuejs 中的关联表记录
- codenameone - cn1 - 尝试在空对象引用上调用虚拟方法“boolean java.util.ArrayList.add(java.lang.Object)”
- php - 如何在 mysql 数据库中以 UTF-8 格式保存数据?
- amazon-web-services - 在 AWS 中的何处保存图像?
- r - 如何在R中的两列之间对较小的值进行排序?