I was wondering if there is any known way to efficiently add a "Reorder" feature to my datatables in dc.js. It would be great if my users, after having done their selection with the charts, could decide according to which column the filtered rows should be ordered (by clicking on the column header for example).
Any ideas where to start?
Thanks a lot
I like to use JQuery datatables for this: http://datatables.net/
First add the table and the header row:
<table id="dc-data-table" class="list table table-striped table-bordered">
<thead>
<tr>
<th>Country</th>
<th>Users</th>
</tr>
</thead>
</table>
Next build your dimensions like normal:
var ndx = crossfilter(data),
countryDimension = ndx.dimension(function (d) {
return d.country;
}),
Then bind the jquery data table:
var datatable = $("#dc-data-table").dataTable({
"bPaginate": false,
"bLengthChange": false,
"bFilter": false,
"bSort": true,
"bInfo": false,
"bAutoWidth": false,
"bDeferRender": true,
"aaData": countryDimension.top(Infinity),
"bDestroy": true,
"aoColumns": [
{ "mData": "country", "sDefaultContent": ""},
{ "mData": "users", "sDefaultContent": " " }
]
});
Finally, hook it into dc.js if you want the table to reflect the filters of your other charts:
function RefreshTable() {
dc.events.trigger(function () {
alldata = countryDimension.top(Infinity);
datatable.fnClearTable();
datatable.fnAddData(alldata);
datatable.fnDraw();
});
}
for (var i = 0; i < dc.chartRegistry.list().length; i++) {
var chartI = dc.chartRegistry.list()[i];
chartI.on("filtered", RefreshTable);
}
Here is a jsFiddle that demonstrates this: http://jsfiddle.net/djmartin_umich/d55My/
Here is another table sorting method that only uses standard jQuery and dc.js
Define your table html with column headers.
<table id="data-table">
<thead>
<th class="data-table-col" data-col="a">Field A</th>
<th class="data-table-col" data-col="b">Field B</th>
<th class="data-table-col" data-col="c">Field C</th>
</thead>
</table>
Setup the DC.js dataTable. The only important thing is the columns configuration. Use the format that does not dynamically create headers
var dataDim = xf.dimension(function(d) {return d.a;});
var dataTable = dc.dataTable("#data-table")
.columns([
function(d) {
return d.a;
},
function(d) {
return d.b;
},
function(d) {
return d.c;
}
]);
Then, just attach a click event handler to the column headers and use DC.js to sort the table. Edit - you must also change the dimension so that the sortBy sorts all data, not just displayed data
$('#data-table').on('click', '.data-table-col', function() {
var column = $(this).attr("data-col");
dataDim.dispose();
dataDim = xf.dimension(function(d) {return d[column];});
dataTable.dimension(dataDim)
dataTable.sortBy(function(d) {
return d[column];
});
dataTable.redraw();
});
Related
I am working with variable data sets and I can barely predetermine the data columns until the data is fetched from the API service. I have worked out a way to render the clarity datagrid ("#clr/angular": "4.0.8") dynamically as follows:
private fetchSiloData(uuid: string) {
const sub = this.siloService.getSiloData(uuid).subscribe({
next: resp => {
this.siloData = resp;
// extract columns from the data and mark the first 5 columns as visible for datagrid columns
if (resp.length) {
this.columns = Object.keys(resp[0]).map((key, index) => {
if (index < 5) {
return { name: key, hidden: false };
}
return { name: key, hidden: true };
});
}
},
complete: () => {
if (sub) {
sub.unsubscribe();
}
},
});
}
In my component html, I have the following:
<clr-datagrid>
<clr-dg-column *ngFor="let col of columns" [clrDgField]="col.name">
<ng-container *clrDgHideableColumn="{ hidden: col.hidden }">
{{ col.name }}
</ng-container>
</clr-dg-column>
<clr-dg-row *clrDgItems="let row of siloData" [clrDgItem]="row">
<clr-dg-cell *ngFor="let col of columns">{{ row[col.name] }}</clr-dg-cell>
</clr-dg-row>
<clr-dg-footer>
{{ pagination.firstItem + 1 }} - {{ pagination.lastItem + 1 }} of
{{ pagination.totalItems }} rows
<clr-dg-pagination #pagination [clrDgPageSize]="20"></clr-dg-pagination>
</clr-dg-footer>
</clr-datagrid>
With the above, my expectation was to:
Get a neatly formatted datagrid as would if I had knowledge of my data columns
The clarity datagrid to automatically hide data cells corresponding to hidden columns
Selecting and unselecting columns to work.
However here is my result with a sample data source:
I even went ahead to create a pure pipe to filter out data cells with hidden columns and the effect of that is I now get a better initial view as far as cell count is concerned.
<clr-dg-row *clrDgItems="let row of siloData" [clrDgItem]="row">
<clr-dg-cell *ngFor="let col of columns | visibleColumns">{{
row[col.name]
}}</clr-dg-cell>
</clr-dg-row>
How do I go about my need for a dynamically rendered datagrid but still be able to have the datagrid column selection functionality work as expected? Or rather, how do I go about extending the datagrid to cover my use case? Thank you in advance.
I used an example of bootstrap datatable to have a sortable table on my symfony project. I use datatables.net
With this javascript code :
$(document).ready(function() {
$('#datatable').DataTable();
} );
And my table in HTML :
<table id="datatable" class="table table-striped">
<thead>
<tr>...
The only problem is that I don't want all my columns to be sortable (some columns only contain a checkbox or a button for example). Moreover with this table and this javascript code I automatically have a search bar as well as Previous Next buttons and other options that appear automatically. I would like to be able to modify this default template by removing all these options that I don't need. Do you have an idea how to adapt this datatable?
Thanks in advance
You should read documentation for datatables. https://datatables.net/extensions/rowreorder/examples/initialisation/restrictedOrdering.html
Example:
// add class reorder on column, that you want to order
$(document).ready(function() {
var table = $('#example').DataTable( {
rowReorder: true,
columnDefs: [
{ orderable: true, className: 'reorder', targets: 0 },
{ orderable: false, targets: '_all' }
]
} );
} );
I'm new with Lit-element and I'm trying to modify an exsiting library. This library creates a table of checkboxes based on three arrays. Arrays AAA and BBB are used as indexes, and array CCC is used to set the "checked" property.
These arrays can be realoaded via ajax, so the initial data is reset.
The problem is, if I manually check/uncheck one checkbox an reset the data, the checkbox is not updated and the manual changes persist.
I've tried to add all three arrays to the "properties" section. Also I've created getters and setters. And finally I've tried to do a full requestUpdate", but nothing works.
Here is a piece of code (EDITED: replaced ?checked by .checked):
render(){
return html`
...
${this.AAA.map(A => html`
<tr>
...
${this.BBB.map(B => html`
<td>
...
<input type="checkbox" .checked=${this.CCC[A].includes(B)} id="${A}:${B}"/>
...
</td>
`)}
</tr>
`)}
...
`
}
static get properties() {
return {
//(added by me) Same for BBB and CCC
AAA: { type: Array, hasChanged: (newVal, oldVal) => {
return true;
}},
...
};
}
//(added by me) Same for BBB and CCC
set AAA(newAAA){
const oldAAA = this.AAA;
this._AAA = newAAA;
this.requestUpdate('AAA', oldAAA);
}
get AAA(){
return this._AAA
}
Ajax Data might look like this:
{
"AAA": [
"xxx",
"yyy",
"zzz"
],
"BBB": [
"111",
"222",
"333"
]
"CCC": {
"xxx": [
"111"
],
"yyy": [
"222",
"333"
],
"zzz": [
"222"
]
}
}
Here, a little example where you can try the problem: https://stackblitz.com/edit/js-fadxme
Some advise?
Thanks.
I've managed to do it modifying the shadowRoot/renderRoot in the "updated" method.
Input now is declared as:
<input type="checkbox" data-aaa="${A}" data-bbb="${B}" id="${A}:${B}"/>
instead of:
<input type="checkbox" .checked=${this.CCC[A].includes(B)} id="${A}:${B}"/>
And here the updated method:
updated(changedProperties) {
var inputs = this.shadowRoot.querySelectorAll("input[type='checkbox']");
inputs.forEach(elem => {
let A = elem.getAttribute('data-aaa');
let B = elem.getAttribute('data-bbb');
if(A && B){
elem.checked = this.CCC[A] ? this.CCC[A].includes(B) : false;
}
})
super.updated(changedProperties);
}
Here the working example: https://stackblitz.com/edit/js-fadxme
Anyway... what is preferible to use, shadowRoot or renderRoot?
With the last google chart version, v42 some of the stylish I have for table have changed. This is the actual code I have:
https://jsfiddle.net/Khrys/mLgn76fu/
google.setOnLoadCallback(drawTable);
function drawTable() {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Name');
data.addColumn('number', 'Salary');
data.addColumn('boolean', 'Full Time Employee');
data.addRows([
['Mike', {v: 10000, f: '$10,000'}, true],
['Jim', {v:8000, f: '$8,000'}, false],
['Alice', {v: 12500, f: '$12,500'}, true],
['Bob', {v: 7000, f: '$7,000'}, true]
]);
var table = new google.visualization.Table(document.getElementById('table_div'));
google.visualization.events.addListener(table, 'ready', function () {
$(".google-visualization-table-table").find('*').each(function (i, e) {
var classList = e.className ? e.className.split(/\s+/) : [];
$.each(classList, function (index, item) {
if (item.indexOf("google-visualization") === 0) {
$(e).removeClass(item);
}
});
});
$(".google-visualization-table-table")
.removeClass('google-visualization-table-table')
.addClass('table table-striped table-condensed table-hover')
.css("width", "85%");
});
table.draw(data);
}
This isn't working anymore. This should read the classes from bootstrap and render the table with bootstrap classes for table.
The function applying the style is applying it to the parent div. I changed to add .find('table') to get to the table child.
https://jsfiddle.net/mLgn76fu/7/
$(".google-visualization-table")
.removeClass('google-visualization-table')
.find('table')
.addClass('table table-striped table-condensed table-hover')
.css("width", "85%");
});
That works as a quick solution. In fact, the real problem and a better solution would be to rewrite the class "killer" you have as that is not doing what you expect. That is why the class ends up as google-visualization-table and not with table-table on the end as you expected.
I would just kill all classes, like this:
$(".google-visualization-table").find('*').each(function (i, e) {
$(e).removeClass();
});
And then add your bootstrap ones like in the fiddle or code above.
If I create a table in a Template, is there a way to add a row of "reactive totals" at the bottom of the table using Meteor? Or do I have to write the code to sum the columns, the same way I would in ordinary JavaScript?
For example, given the table...
State Sales
NY 1234
FL 670
TX 2306
Total 4210
Can I use some reactive technique to calculate and keep the Total cell showing 4210 up-to-date as other cells change?
Another way to do this is to use MongoDB $sum aggregator
Taking Erik Johnson's example, you need to do this:
// HTML
<div id="allSales">
<h2><span>Total sales: </span>{{allSales}}</h2>
</div>
// JS
Template.templatename.helpers({
allSales: function() {
var allSales = allStates.aggregate( [ { $group: { _id: null, total: { $sum: "$sales" } } } ]);
return allSales.total;
},
});
This is how I've done it - I'm sure there's probably an easier way, but I'm still a beginner at Meteor:
// HTML
<div id="allSales">
<h2><span>Total sales: </span>{{allSales}}</h2>
</div>
// JS
Template.templatename.helpers({
allSales: function() {
var allStates = Statescollection.find();
var mappedItems = allStates.map((function(allStates) {return allStates.sales;}));
var allSales = _.reduce(mappedItems, function(memo, num){ return memo + num; }, 0);
return allSales;
},
});
I've created the package peppelg:mongo-sum. With it, MyCollection.find().sum('field') will return the sum of all the values in the field 'field'.
I had the same issue, and have found the following solution for both columns and rows of a table
In Meteor, how can i do a sum of some computed fields in a table?