问题
I created two tables that relate. When I select one or more items from Table A, Table B is resized according to that selection and vice versa.
However, I need to lock the selection of the two tables at the same time. For example, if I click on the sellers Miguel
andSede
, in Table B the values and cities corresponding to these two sellers will be rendered. And if in Table B I click on the cities of Faro
andLoulé
, automatically the sellers Miguel
andSede
should be deselected.
What happens at the moment is that I can make selections in both tables at the same time, as shown in the image below:
I need to create a rule where if I have a selection in Table A and click on Table B. The line selected in Table A has no background color and vice versa.
When I make a selection, some lines are empty, but they still appear, as shown in the image below:
How can I prevent these lines from appearing if there is no selection?
I apologize for asking two questions at the same time, as it is the same code I have found better to use here.
Here's my code, thanks in advance.
var vendedorTable = dc.dataTable("#Vendedores");
var citiesTable = dc.dataTable("#cities");
var url = 'https://gist.githubusercontent.com/bernalvinicius/3cece295bc37de1697e7f83418e7fcc9/raw/a5820379ec6eae76ee792495cc5dd1685c977a73/vendedores.json';
d3.json(url).then(function(data) {
data.forEach(function(d) {
var myCrossfilter = crossfilter(data);
var all = myCrossfilter.groupAll();
dc.dataCount(".dc-data-count")
.dimension(myCrossfilter)
.group(all);
vendedorDim = myCrossfilter.dimension(function(d) {
return d.vendnm;
});
var vendedorGroup = vendedorDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
citiesDim = myCrossfilter.dimension(function(d) {
return d.zona;
});
var citiesGroup = citiesDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
function reduceAdd(p, v) {
p.totalAno += +v.Vendas_Ano;
p.totalHomologo += +v.Vendas_Ant;
return p;
}
function reduceRemove(p, v) {
p.totalAno -= v.Vendas_Ano;
p.totalHomologo -= v.Vendas_Ant;
return p;
}
function reduceInitial() {
return {
totalAno: 0,
totalHomologo: 0,
};
}
//Fake Dimension
rank = function(p) {
return ""
};
function checkRows(d) {
if (d.value.totalAno <= 0 || isNaN(d.value.totalAno) || d.value.totalHomologo <= 0 || isNaN(d.value.totalHomologo)) {
return 0;
}
return d;
}
//vendedorTable
vendedorTable.width(500)
.height(480)
.dimension(vendedorGroup)
.group(rank)
.columns([function(d) {
d = checkRows(d);
while (d != 0) {
return d.key;
}
},
function(d) {
d = checkRows(d);
while (d != 0) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
},
function(d) {
d = checkRows(d);
while (d != 0) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
}
])
.sortBy(function(d) {
return d.value.totalAno
})
.order(d3.descending)
//CityTable
citiesTable.width(500)
.height(480)
.dimension(citiesGroup)
.group(rank)
.columns([function(d) {
d = checkRows(d);
while (d != 0) {
return d.key;
}
},
function(d) {
d = checkRows(d);
while (d != 0) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
},
function(d) {
d = checkRows(d);
while (d != 0) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
}
])
.sortBy(function(d) {
return d.value.totalAno
})
.order(d3.descending)
vendedorTable.on('pretransition', function(table) {
table.selectAll('td.dc-table-column')
.on('click', function(d) {
let filters = table.filters().slice();
if (filters.indexOf(d.key) === -1)
filters.push(d.key);
else
filters = filters.filter(k => k != d.key);
if (filters.length === 0)
vendedorDim.filter(null);
else
vendedorDim.filterFunction(function(d) {
return filters.indexOf(d) !== -1;
})
table.replaceFilter([filters]);
dc.redrawAll();
});
let filters = table.filters();
table.selectAll('tr.dc-table-row')
.classed('sel-rows', d => filters.indexOf(d.key) !== -1);
});
citiesTable.on('pretransition', function(table) {
table.selectAll('td.dc-table-column')
.on('click', function(d) {
let filters = table.filters().slice();
if (filters.indexOf(d.key) === -1)
filters.push(d.key);
else
filters = filters.filter(k => k != d.key);
if (filters.length === 0)
citiesDim.filter(null);
else
citiesDim.filterFunction(function(d) {
return filters.indexOf(d) !== -1;
})
table.replaceFilter([filters]);
dc.redrawAll();
});
let filters = table.filters();
table.selectAll('tr.dc-table-row')
.classed('sel-rows', d => filters.indexOf(d.key) !== -1);
});
dc.renderAll();
});
$('#reset').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
dc.redrawAll();
});
$('#resetTable').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
dc.redrawAll();
});
$('#resetTable2').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
dc.redrawAll();
});
});
<head>
<style>
.dc-table-group {
visibility: collapse;
}
tr.dc-table-row.sel-rows {
background-color: lightblue;
}
</style>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.4.1.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dc/2.1.8/dc.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.js"></script>
<script src="https://d3js.org/d3.v5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dc/3.1.8/dc.js"></script>
<title>Vendedores</title>
</head>
<body>
<div class="container-fluid">
<div class="row content">
<div class="col-md-10" style="padding-left: 20px;">
<div class="row marginClass">
<h4 class="pull-left" id="Introduction"><small>Dados fictícios da empresa | Exemplo de Pesquisa
Detalhada |
</small></h4>
<h6 class="dc-data-count" style="float: left;margin-left:5px;">
<span>
<span class="filter-count"></span> selecionado dentre
<span class="total-count"></span> registros |
<a id="reset"> Reset All </a>
</span>
</h6>
</div>
<div class="col-md-6">
<br>
<a id="resetTable"> Reset</a>
<table class="table" id="Vendedores">
<thead>
<tr>
<th>Sales</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
<div class="col-md-6">
<br>
<a id="resetTable2"> Reset</a>
<table class="table" id="cities">
<thead>
<tr>
<th>City</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
</body>
回答1:
I'm not sure that I understand the first question.
As for the second question, currently you are filtering out the data inside of the formatting functions for the table:
.columns([function(d) {
d = checkRows(d);
while (d != 0) {
return d.key;
}
},
function(d) {
d = checkRows(d);
while (d != 0) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
},
function(d) {
d = checkRows(d);
while (d != 0) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
}
])
That works, but it's a weird way to go about it for two reasons:
while(...)
means "repeat until false".if(...)
would be more idiomatic here.- More importantly, this still prints the rows, but if the values are zero or NaN, it prints
undefined
(the default return value for a function in JavaScript) and this shows up as an empty cell.
It would be better to filter the data before it gets to the table. You can do this using a fake group. Specifically, you can create an object which supports the .top(N)
function that the table will use to pull data, but skips any rows where either of these values is close to zero:
function remove_empty_bins(source_group) {
return {
top: function (N) {
return source_group.all().filter(function(d) {
return d.value.totalAno > 1e-3 &&
d.value.totalHomologo > 1e-3; // does it have a value?
}).slice(0, N);
}
};
}
Apply this function to each of your groups:
vendedorTable.width(500)
.height(480)
.dimension(remove_empty_bins(vendedorGroup))
citiesTable.width(500)
.height(480)
.dimension(remove_empty_bins(citiesGroup))
And now your formatting functions can stick to formatting!
.columns([function(d) {
return d.key;
},
function(d) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
},
function(d) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
])
var vendedorTable = dc.dataTable("#Vendedores");
var citiesTable = dc.dataTable("#cities");
function remove_empty_bins(source_group) {
return {
top: function (N) {
return source_group.all().filter(function(d) {
return d.value.totalAno > 1e-3 &&
d.value.totalHomologo > 1e-3; // does it have a value?
}).slice(0, N);
}
};
}
var url = 'https://gist.githubusercontent.com/bernalvinicius/3cece295bc37de1697e7f83418e7fcc9/raw/a5820379ec6eae76ee792495cc5dd1685c977a73/vendedores.json';
d3.json(url).then(function(data) {
data.forEach(function(d) {
var myCrossfilter = crossfilter(data);
var all = myCrossfilter.groupAll();
dc.dataCount(".dc-data-count")
.dimension(myCrossfilter)
.group(all);
vendedorDim = myCrossfilter.dimension(function(d) {
return d.vendnm;
});
var vendedorGroup = vendedorDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
citiesDim = myCrossfilter.dimension(function(d) {
return d.zona;
});
var citiesGroup = citiesDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
function reduceAdd(p, v) {
p.totalAno += +v.Vendas_Ano;
p.totalHomologo += +v.Vendas_Ant;
return p;
}
function reduceRemove(p, v) {
p.totalAno -= v.Vendas_Ano;
p.totalHomologo -= v.Vendas_Ant;
return p;
}
function reduceInitial() {
return {
totalAno: 0,
totalHomologo: 0,
};
}
//Fake Dimension
rank = function(p) {
return ""
};
function checkRows(d) {
if (d.value.totalAno <= 0 || isNaN(d.value.totalAno) || d.value.totalHomologo <= 0 || isNaN(d.value.totalHomologo)) {
return 0;
}
return d;
}
//vendedorTable
vendedorTable.width(500)
.height(480)
.dimension(remove_empty_bins(vendedorGroup))
.group(rank)
.columns([function(d) {
return d.key;
},
function(d) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
},
function(d) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
])
.sortBy(function(d) {
return d.value.totalAno
})
.order(d3.descending)
//CityTable
citiesTable.width(500)
.height(480)
.dimension(remove_empty_bins(citiesGroup))
.group(rank)
.columns([function(d) {
d = checkRows(d);
while (d != 0) {
return d.key;
}
},
function(d) {
d = checkRows(d);
while (d != 0) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
},
function(d) {
d = checkRows(d);
while (d != 0) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
}
])
.sortBy(function(d) {
return d.value.totalAno
})
.order(d3.descending)
vendedorTable.on('pretransition', function(table) {
table.selectAll('td.dc-table-column')
.on('click', function(d) {
let filters = table.filters().slice();
if (filters.indexOf(d.key) === -1)
filters.push(d.key);
else
filters = filters.filter(k => k != d.key);
if (filters.length === 0)
vendedorDim.filter(null);
else
vendedorDim.filterFunction(function(d) {
return filters.indexOf(d) !== -1;
})
table.replaceFilter([filters]);
dc.redrawAll();
});
let filters = table.filters();
table.selectAll('tr.dc-table-row')
.classed('sel-rows', d => filters.indexOf(d.key) !== -1);
});
citiesTable.on('pretransition', function(table) {
table.selectAll('td.dc-table-column')
.on('click', function(d) {
let filters = table.filters().slice();
if (filters.indexOf(d.key) === -1)
filters.push(d.key);
else
filters = filters.filter(k => k != d.key);
if (filters.length === 0)
citiesDim.filter(null);
else
citiesDim.filterFunction(function(d) {
return filters.indexOf(d) !== -1;
})
table.replaceFilter([filters]);
dc.redrawAll();
});
let filters = table.filters();
table.selectAll('tr.dc-table-row')
.classed('sel-rows', d => filters.indexOf(d.key) !== -1);
});
dc.renderAll();
});
$('#reset').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
dc.redrawAll();
});
$('#resetTable').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
dc.redrawAll();
});
$('#resetTable2').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
dc.redrawAll();
});
});
<head>
<style>
.dc-table-group {
visibility: collapse;
}
tr.dc-table-row.sel-rows {
background-color: lightblue;
}
</style>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.4.1.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dc/2.1.8/dc.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.js"></script>
<script src="https://d3js.org/d3.v5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dc/3.1.8/dc.js"></script>
<title>Vendedores</title>
</head>
<body>
<div class="container-fluid">
<div class="row content">
<div class="col-md-10" style="padding-left: 20px;">
<div class="row marginClass">
<h4 class="pull-left" id="Introduction"><small>Dados fictícios da empresa | Exemplo de Pesquisa
Detalhada |
</small></h4>
<h6 class="dc-data-count" style="float: left;margin-left:5px;">
<span>
<span class="filter-count"></span> selecionado dentre
<span class="total-count"></span> registros |
<a id="reset"> Reset All </a>
</span>
</h6>
</div>
<div class="col-md-6">
<br>
<a id="resetTable"> Reset</a>
<table class="table" id="Vendedores">
<thead>
<tr>
<th>Sales</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
<div class="col-md-6">
<br>
<a id="resetTable2"> Reset</a>
<table class="table" id="cities">
<thead>
<tr>
<th>City</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
</body>
来源:https://stackoverflow.com/questions/59955008/how-to-remove-empty-rows-in-tables-when-they-are-zero-and-how-to-allow-selectio