I am using jQuery to inject values from an JSON object into an existing, empty table.
What I want to do is fill up my table top-to-bottom, instead of what it does now - left to right. So how would I modify this snippet to accomplish what I want?
_.each(dataset, function(v, k){
$("tr td:empty:first", "#myTable tbody").html("<strong>"+v+"</strong>");
});
I guess that It would be possible to target the td in the column which has the fewest filled rows, but how?
Change your selector to:
$("tr td:empty:first:first-child", "#myTable tbody")
So that it matches only when the table cell is the first-child of it's parent, the row, so will only insert in the first column.
Edit: A method to populate the table based on a known and limited number of columns
It works by selecting each table column using the nth-child of each row, then concatenating the columns together, and filtering for the first empty one.
var cells = [].concat(
$('tr td:nth-child(1)').get(),
$('tr td:nth-child(2)').get(),
$('tr td:nth-child(3)').get(),
$('tr td:nth-child(4)').get()
)
_.each(dataset, function(v, k){
$(cells).filter(':empty:first').html("<strong>"+v+"</strong>");
});
Edit: Generic version
// create array to hold cells ordered by column
var cells = [];
// loop through desired columns adding to cells array
// hint: you can specify column `from` and `to` in the for loop definition
for(i=1;i<=4;i++)
cells = cells.concat($('tr td:nth-child('+i+')').get());
// loop through the data, populating each column in order
_.each(dataset, function(v, k){
$(cells).filter(':empty:first').html("<strong>"+v+"</strong>");
});
Is dataset an array? If so, something like this:
var self;
$('#myTable tr:has("td:empty")', function (index, elem) {
if (index < dataset.length) {
self = $(this);
self.find('td:empty').html("<strong>"+dataset[i]+"</strong>")
}
});
..fredrik
Here's what I ended up doing, using underscore.js to iterate the dataset:
var currentCol = 0;
var currentRow = 0;
var renderedCount = 0;
_.each(dataset, function(v, k) {
/* Switch to a new column when we have rendered enough (4)
* elements vertically per column and reset the rowcounter.
*/
if (renderedCount % 4 === 0) {
currentCol++;
currentRow = 0;
}
// Inject our value as HTML into the first empty TD per column
$("tr:eq("+currentRow+") td:empty:first", "#mytable tbody").html(v);
// Increment current row count and our renderedCount
currentRow++;
renderedCount++;
});
Many thanks to #BillyMoon for helping out!
Update:
To create the empty table before doing the above, I did this:
// To allow repeating strings (n) times. Borrowed from someone on SO
String.prototype.repeat = function( num ) {
return new Array( num + 1 ).join( this );
}
// Setup our wanted maximum amount of columns horizontally and our table structure
var maxColsPerRow = 4;
var rowHTML = "<tr>"+'<td></td>'.repeat(maxColsPerRow)+"</tr>";
var datasetCount = _.keys(dataset).length; // Amount of properties in our dataset calculated with underscore.js keys()-method.
// Append as many empty rows&cells to our table that we'll need
$("#myTable tbody").html( rowHTML.repeat( Math.round( datasetCount/maxColsPerRow));
Related
I need add row in datatable, but the number the colums is variable.
I try two each but not run because row.add need all row not 1 to 1.
var obj = $.parseJSON(res);
$.each(obj, function (index) {
$.each(obj[index], function (value) {
table.row.add([obj[index][value]]).draw();
});
});
Is possible add row without knowing the number of columns?
Edit:
So If you have a variable number of columns the only way you'd be able to still use the datatable most likely is by inserting blank values into the columns of rows which don't posess the maximum amount of values, ex:
table.row.add(['a','b','c','d']).draw();
//Above is a row which has all the 4 column values
//Beneath is a row with 3 out of 4 column values
table.row.add(['a','b',null,'d']).draw()
/*or*/
table.row.add(['a','b','','d']).draw()
I'm not quite sure where you've gotten the idea that row.add() needs all rows added at once.
https://datatables.net/reference/api/row.add() according to the official documentation this method is able to add single rows to your table without any problem.
Here a JSFiddle proving that a single row can be added to the already existing ones:
myTable.row.add(["Airi Satou", "This was", "added", "later", "EXAMPLE", "ITEM"]).draw();
http://jsfiddle.net/bbLjzspf/8709/
Firstly, there is an error in your script, perhaps a typo, there is an extra square bracket after [obj][index][value] "]".
var obj = $.parseJSON(res);
$.each(obj, function (index) {
$.each(obj[index], function (value) {
table.row.add(
[obj[index][value] >]<-- One too many brackets ).draw();
});
});
Then my suggestion.
First I make two little helpers...
One to count the number of columns:
$.fn.DTbl_columnCount = function () {
return $('th', $(this).find('thead')).length;
};
And since I've got no idea what kind of data is coming, the next one is all belts and buckles, erm, in a "whatever" version, that also accepts arrays ;)
$.fn.DTbl_createDataColumns = function (whatever) {
var temp = new Array($(this).DTbl_columnCount());
var value = "";
var type = jQuery.type(whatever);
var isArray = (type == "array");
if (!isArray && type != undefined)
value = whatever;
for (var i = 0; i < temp.length; i++) {
if (isArray)
if (whatever.length > i)
value = whatever[i];
temp[i] = '' + value;
value = "";
}
return temp;
};
And then to your problem, since these additions are not really part of DataTables we need to fetch the DataTable Id in the function call of DTbl_createDataColumns, here using the standard reference "#example".
var obj = $.parseJSON(res);
$.each(obj, function (index) {
$.each(obj[index], function (value) {
table.row.add(
$("#example").DTbl_createDataColumns([obj[index][value])
).draw();
});
});
So this would also work:
var myArray = ["Dodaa", "Dodee"];
table.row.add( $("#example").DTbl_getDataColumns(myArray) );
table.row.add(['a','b','','d']).draw()
didn't work for me.
I have to do
table.row.add([['a','b','','d']]).draw()
Also it is important to be consistent between data types of dictionary and array. DataTable does NOT like it if we mix dictonaries and arrays in different calls. For example we might need to do (if it is initialized with an array of dictionaries).
table.row.add([{'hdrCol1': 'a', 'hdrCol2':'b','hdrOfCol3':'', 'hdrCol4':'d'}]).draw()
I have a Kendo UI Grid with a large datasource and paging.
I have an event that fires where I know the underlying data item that I want to select, but am unsure on how to programatically page/select this item in the grid. If the item is not on the current grid page, I cannot use datasource.view() to poke through when the data is not on the current page.
Does anyone know how I can select an item by its underlying data source object?
I've got a similar situation to where i am at #:
http://jsfiddle.net/Sbb5Z/1050/
I can get the data item with the following:
change: function (e) {
var selectedRows = this.select();
var dataItem = this.dataItem(selectedRows[0]);
}
But then I don't know how to select the same row in the other grid.
Basically in the select event of one grid, I want to go select the same item in another grid. These are not the same datasource, as they have different page setups, but it is the same underlying data array.
I have the data item in the target grid -- but I have no clue how to page/select it in the target grid.
Edit:
The best I've come up with sofar is creating a datasource with the same parameters as the original, and paging through it programatically, until I find what I am looking for. Surely there must be a better way?
I've gotten this back from Telerik, and is a little cleaner:
http://jsfiddle.net/RZwQ2/
function findDataItem(theGrid, dataItem) {
//get grid datasource
var ds = theGrid.dataSource;
var view = kendo.data.Query.process(ds.data(), {
filter: ds.filter(),
sort: ds.sort()
})
.data;
var index = -1;
// find the index of the matching dataItem
for (var x = 0; x < view.length; x++) {
if (view[x].Id == dataItem.Id) {
index = x;
break;
}
}
if (index === -1) {
return;
}
var page = Math.floor(index / theGrid.dataSource.pageSize());
var targetIndex = index - (page * theGrid.dataSource.pageSize()) + 1;
//page is 1-based index
theGrid.dataSource.page(++page);
//grid wants a html element. tr:eq(x) by itself searches in the first grid!
var row = $("#grid2").find("tr:eq(" + targetIndex + ")");
theGrid.select(row);
console.log('Found it at Page: ' + page + 'index: ' + targetIndex);
}
You need to have a common id, or field in the data that you can use to uniquely identify the object in the other dataSource, because the kendo generated UID's are not going to be the same accross two different DataSource instances.
Most generally you define the id in the Model you bound to the grid, which you can use to quickly pluck items from the datasource
change: function (e) {
var selectedRows = this.select();
var dataItem = this.dataItem(selectedRows[0]);
var otherItem = otherGrid.dataSource.get(dataItem.id) // will get
}
if you don't have a common ID field specified in the model, but do know how to find the item you can loop through the data source looking for it
var selectedRows = this.select();
var dataItem = this.dataItem(selectedRows[0]);
var data = otherGrid.dataSource.view();
var otherItem;
for ( var i = 0; i < data.length; i++ ){
if( data[i].myCommonField === dataItem.myCommonField ) {
otherItem = data[i];
break;
}
}
UPDATE:
to select the item in the other grid you need to do this:
var elements = otherGrid.items(),
element;
element = elements.filter("[data-uid='" + otherItem.uid + "']")
otherGrid.select(element) // to select just the one item
//OR
otherGrid.select( otherGrid.select().add(element) ) // to add the item to the current selection
I the fiddle you provided uses a really old version of kendo Grid where this won't work...I just realized. are you stuck on the 2011 version? I can probably get something to work at least in theory but the above will work in the newer versions
essentailly you need to match the item you have to a DOM element, in later versions you can use UID because the dom elements all get that on them "data-uid" it looks like if you at id to your model: { } def you can get the tr elements to have data-id which you can use to select the right select using jquery. I use the items()1 method which also doesn't seem to exist on the early version but you can usegrid2.table.find("tr[data-id=]")` instead I believe
Assume div id will be Grid then first we need find the kendoGrid
var grid = $("#Grid").data("kendoGrid");
then call the grid.select() to select the currently selected one
finally call the grid.dataItem() to get the selected item.
var selectedDataItem = grid.dataItem(grid.select());
To expand upon others, I have a method that takes a single (or multiple) ids to match against:
function selectItems(grid, idAr)
{
if(!idAr instanceof Array)idAr = [idAr];
var items = grid
.items()
.filter(function(i, el)
{
return idAr.indexOf(grid.dataItem(el).Id) !== -1;
});
grid.select(items);
}
* Obviously Id could be replaced by any field that is in your data item.
Use for selection:
selectItems(grid, "5");
selectItems(grid, ["6", "7"]);
I want to get all the rows of JQGrid in a javascript variable in a certain order.
I was able to get the data of all rows by :
var rowData = $("#gridId").jqGrid("getRowData");
But I want the rows to appear in the same order as that I entered in the grid.
I am trying to create a table and get its column details through the grid. So I require the order of the columns to remain the same. Any suggestions?
You can explicitly push the jqgrid rowdata to an array and then convert that to json string.
To do that you may use a function like the one given below to iterate through the jqgrid rows:(From Oleg's answer)
var gridData= {};
var $grid = jQuery("#gridId"), rows = $grid[0].rows, cRows = rows.length,
iRow, rowId, row, cellsOfRow;
for (iRow = 0; iRow < cRows; iRow++) {
row = rows[iRow];
if ($(row).hasClass("jqgrow")) {
cellsOfRow = row.cells;
// row represent the row with cellsOfRow.length cells.
// So one can use for loop with iCol between 0 and cellsOfRow.length
// cellsOfRow[iCol] represent the cell with the index iCol
// and $(cellsOfRow[iCol]).text() - the text of the cell
}
}
Inside the loop you can push the celldata to an array.
gridData['column_index1'] = $(cellsOfRow[0]).text();
gridData['column_index2'] = $(cellsOfRow[1]).text();
.............................................
Finally convert gridData to json string using
var result = JSON.stringify(gridData) ;
I'm writing some JS to sort an HTML table: the HTML table has a header row, clicking on the header of a column sorts the rows according to that column. My general algorithm is as follows:
Convert HTML into a JavaScript array.
Sort JavaScript array.
Reconvert JavaScript array to HTML table.
Right now, my code replaces all of the innerHTML elements of the table. Basically, after it sorts the JS array, it touches every node of the table, rewriting it's innerHTML value. I feel like there might be a more efficient way to do this, but I can't think of it.
EDIT:
Here's the code that does part (3) of the algorithm outlined above. I edited out the irrelevant parts (just some string transformations). The indices are a little weird because I skipped the header row when I converted the table to an array, so I'm compensating for that when rewriting the table.
for (var i = 0; i < numRows-1; i++){
for (var j = 0; j < numCols; j++){
var arrCell = tableArr[i][cols[j]];
tableEl.rows[i+1].cells[j].innerHTML = arrCell;
}
}
You could remove the rows from the DOM, do the sort, then add them back onto the DOM.
var tbody = table.tBodies[0];
var placeholder = document.createElement('tbody');
table.replaceChild(placeholder, tbody);
//sort rows in tbody
table.replaceChild(tbody, placeholder);
Use an array to sort the values and a document fragment to perform the update.
function sortRows(tbody, compare, sortDesc) {
//convert html collection to array
var rows = [].slice.call(tbody.rows);
//sort to desired order
rows.sort(compare);
if (sortDesc) {
rows.reverse();
}
//update table
var fragment = document.createDocumentFragment();
rows.forEach(function (row) {
fragment.appendChild(row);
});
tbody.appendChild(fragment);
}
The complexity will be in the compare function. You will need to take into account the column index and any type conversions and caching you want.
This is a basic example that converts the cell text content to an integer.
function sortTable(table, columnIndex) {
while (table && table.tagName !== 'TABLE') {
table = table.parentNode;
}
if (table) {
sortRows(table.tBodies[0], function compare(topRow, bottomRow) {
var topValue = parseInt(topRow.cells[columnIndex].textContent, 10);
var bottomValue = parseInt(bottomRow.cells[columnIndex].textContent, 10);
return (topValue - bottomValue);
});
}
}
function sortRows(tbody, compare, sortDesc) {
//convert html collection to array
var rows = [].slice.call(tbody.rows);
//sort to desired order
rows.sort(compare);
if (sortDesc) {
rows.reverse();
}
//update table
var fragment = document.createDocumentFragment();
rows.forEach(function (row) {
fragment.appendChild(row);
});
tbody.appendChild(fragment);
}
<table>
<thead>
<tr><th onclick="sortTable(this, 0)">Sort</th><th onclick="sortTable(this, 1)">Sort</th></tr>
</thead>
<tbody>
<tr><td>1</td><td>25</td></tr>
<tr><td>3</td><td>12</td></tr>
<tr><td>2</td><td>40</td></tr>
<tr><td>10</td><td>25</td></tr>
</tbody>
</table>
If the data is stored on the server - I would send an Ajax request to the server with the sorting type (ASC, DESC, etc. ), sort the data on the server (using PHP, etc) and then receive the sorted data in JavaScript and write the table completely new.
You can use jQuery and its Datatables plugin.
In your datatable statement, you can add
"bSort": true
and the job will be done by the script. Although if you can find a pure JS way I suggest you use it so you don't add useless weight to your web page.
I'm looking for some ideas of how to accomplish this as I am hitting a wall on it.
I have a table that displays data pulled from a MySQL db. The table goes in a sequence of a row of 13 cells with displayed data followed by a hidden row of one cell. The hidden row is toggled by clicking a link in cell index 1 of the previous row. Like so:
row 1 : click this cell to show row 2 : another cell : another cell : ad nauseum till we get to 13 :
row 2 which is hidden
row 3 : click this cell to show row 2 : another cell : another cell : ad nauseum till we get to 13 :
row 4 which is hidden
...
so using jquery I pulled all the rows, then set a test to determine if it was a displayed row or hidden row, if it was displayed then I put that row and the following one into an object and then placed that object into another object, like so:
//this function is for sorting the data in the table
$(".sort").click(function() {
//get what column we are sorting by
var sortBy = $(this).attr('sortBy');
//set the colnum to sort by
if (sortBy == "itemname") {
var sortCol = 1;
} else if (sortBy == "priority") {
var sortCol = 2;
} else if (sortBy == "livedate") {
var sortCol = 10;
} else if (sortBy == "status") {
var sortCol = 11;
} else if (sortBy == "designer") {
var sortCol = 12;
}
//get the table data
var tableData = getTableData("NO", "null", "YES", sortBy);
//get all the rows
var tableRowArray = $("#productTableBody tr");
//declare new table object
var tableObj = new Object;
var rowPackage = new Object;
//loop through tableRowArray and put rows into packages of two, the main row and the hidden row
for(var t=0; t<tableRowArray.length; t++) {
if($(tableRowArray[t]).children(":first").attr('class') == "colorCode") {
rowPackage[t] = $(tableRowArray[t]).children();
rowPackage[t+1] = $(tableRowArray[t+1]).children();
//dump rows into tableObj
tableObj[t] = rowPackage;
}
//clean out rowPackage
rowPackage = {};
}
var x=-2;
for(thisRow in tableObj) {
x = x+2;
var sortItem = $(tableObj[thisRow][x][sortCol]).html();
//ack! -->getting stumped here
}
});
I've also collected which column the user wants to sort by. I can then find the cell the user wants to sort by. I know I need to pull that info, put into an array and sort but I guess I am getting stumped on how to apply the sorted array back to my tableObj so I can rewrite the table body HTML...the reason I am getting stumped is that some of the data to be sorted will be identical, for example if sorting by designer the data could be this {"a", "b", "c", "c", "a", "a", ""a"}, which when sorted would be a, a, a, a, b, c, c, but since some are identical I can't go back and loop through the object and find the entry that matches the first item in my sorted array, 4 items will match it. So how do I determine which entry in the object matches up with the first a in the sorted list?
Thanks in advance.
That's a tough one, but I suppose there is hardly anything impossible in this life.
I would go like this (using Underscore library)
var packs = [];
// assuming you always have even number of tr's
$("#productTableBody tr:odd").each(function(i, tr){
packs.push( {main: tr, sub: tr.next()} );
// tr.next() will be :even, so it's not yet selected
// now detach rows from the table
// note the order - next() wont work otherwise
tr.next().remove();
tr.remove();
});
var sortedPacks = _(packs).sortBy(function(pack){
return $(pack.main).find('td:nth('+sortCol')').text();
});
// now in sortedPacks you have touples of rows sorted by "main" rows sortCol column
// and you would probably want to restore the table now
$.each(packs, function(i, pack){
$("#productTableBody").append(pack.main).append(pack.sub);
});
The code may not reflect your situation perfectly, but I suppose you should be able to get the main idea.
Not really to easy to get what you are asking for here, but this will at least enable you to get the data sorted.
Start by collecting your data into an array, eg data, each row can be represented by either an array or an object.
Now simply call
data.sort(function(a, b){
// select the two values from a and b depending on the column to sort by
a = a[colNr];
b = b[colNr];
return a == b ? 0 : a < b ? -1 : 1;
});
Now you can easily rebuild your table based on the sorted array.
If you during data-collection also added a reference to the row to the array/object, then you can now remove all rows from the table, loop over the data array, and add each node back to the table.