We have a Kendo TreeList that works fine. Data shows, everything shows in the hierarchy correctly. The problem is, we need to group each two columns into another "superset" group.
The column headings (the names above are not real) are too long if not grouped as shown, and they lose useful context.
I tried adding an HTML table above the TreeList, but that doesn't look right. And it doesn't work if the user resizes the columns. Also the toolbar (for Excel export) is in the way, so it doesn't even look like it's part of the TreeList.
I also looked at wrapping the text in the columns, but from what I've seen, that's really iffy too.
It seems like an extra row as shown above (with the ability to merge some columns, like with an HTML table) is the best way to go. Despite scouring the web, I couldn't find a way to do this. Is this even possible with a Kendo TreeList?
This has been solved. Not by me, but by another developer on our team who's insanely good at JavaScript.
The trick is to edit the TreeList's HTML and CSS through JavaScript. You can bind to any event, but we do it on page load:
<script>
$(document).ready(function () {
// anything in here will get executed when the page is loaded
addTopRowToTreeList();
});
function addTopRowToTreeList() {
// grab the thead section
// your HTML may be different
var foo = $('#MyTreeList').children('div.k-grid-header').children('div.k-grid-header-wrap');
var tableChild = foo.children('table');
var headChild = tableChild.children('thead');
var bottomRow = headChild.children('tr');
var topRow = $('<tr>').attr('role', 'row');
// bottom cell should draw a border on the left
bottomRow.children('th').eq(0).addClass('k-first');
// add blank cell
var myNewCell = $('<th>').addClass('k-header').attr('colspan', '1')
var headerString = '';
var headerText = $('<span>').addClass('k-link').text(headerString);
myNewCell.append(headerText);
topRow.append(myNewCell);
// ... add remaining cells, like above
headChild.prepend(topRow);
}
</script>
That's all there is to it!
Related
What is the best way to set a saved grid state after the angular-slickgrid has already been created? The Grid State/Presets - Wiki explains setting the saved state on load by setting the gridOptions.presets. In my case, I would like to update the grid state when the underlying saved state has changed in local storage (perhaps saved from another instantiation of the app), and apply the state to the current slickgrid. If I update the gridOptions.presets, is there a method I can call to force the grid to update with the new presets?
Please note that I'm the author of Angular-Slickgrid.
The answer is No it's called Presets for a reason, it only works when creating the grid...but you can still do it with a few method calls. So if you really wanted to use the Grid State then you'll have to save it yourself and then reload the entire grid after applying all previous State. The Grid State that can be applied dynamically are the Filters and Sorting which you can see in Example 4 and Example 25 (with a button click or a dropdown selection like the last example). I did later add a method to change the columns as well and that one is demoed under this Slickgrid-Universal Example 11, in fact that demo will show you exactly the way you want to do it, you can follow the code here.
for a short code sample, you'll need to get the angularGrid instance from (onAngularGridCreated) and then use it to dynamically change the grid. It shows you all the options, you can skip any of them if you don't need or want to change that option.
angularGridReady(angularGrid: AngularGridInstance) {
this.angularGrid = angularGrid;
}
// the `selectedView` should be the result of your Grid State
changeGridView(selectedView: GridState) {
if (selectedView) {
const columns = selectedView?.columns ?? [];
const filters = selectedView?.filters ?? [];
const sorters = selectedView?.sorters ?? [];
this.angularGrid.filterService.updateFilters(filters as CurrentFilter[]);
this.angularGrid.sortService.updateSorting(sorters as CurrentSorter[]);
this.angularGrid.gridStateService.changeColumnsArrangement(columns);
// if you have a frozen grid (pinning)
this.angularGrid.gridService.setPinning(pinning);
} else {
// to reset the grid
this.angularGrid.filterService.clearFilters();
this.angularGrid.sortService.clearSorting(); this.angularGrid.gridStateService.changeColumnsArrangement([...this.columnDefinitions].map(col => ({ columnId: `${col.id}` })));
// if you have a frozen grid (pinning)
this.angularGrid.gridService.clearPinning();
}
// you might want to scroll back to top of the grid if filters are changed
this.angularGrid.slickGrid.scrollColumnIntoView(0);
}
You might not need to re-render the grid but in case the grid UI doesn't show correctly, you could force a re-render of the grid by invalidating all its rows
this.angularGrid.slickGrid.invalidate();
Is there any way i can hide HOT columns from javascript?
The requirement is such that the column to hide will come as a parameter in javascript and based on that the respective column will show hide accordingly.
The HOT has rowHeaders and colHeaders and the data with 20 columns.
Please advise.
OUTDATED SOLUTION
Ok I founnd a possible solution. I tested it out on my own system but it's actually quite simple.
You should be using a customRenderer in your columns option. Read up about this if you aren't already. The idea is that you're giving each cell its own renderer. In this custom function, you can do something like this:
var colsToHide = [3,4,6]; // hide the fourth, fifth, and seventh columns
function getCustomRenderer() {
return function(instance, td, row, col, prop, value, cellProperties) {
if (colsToHide.indexOf(col) > -1) {
td.hidden = true;
} else {
td.hidden = false;
}
}
}
What this renderer does is hide the cells that the var colsToHide specify. All you do now is add a DOM element that lets the user pick which and so every time the table gets rendered (which happens basically after any change, or manually triggered need be), the cells in the columns specified will be hidden, keeping the data array intact like you described. And when not in colsToHide they are re-rendered so make sure you get that working as well.
Here I implemented it with very basic functionality. Just enter the index of a column into the input fields and watch the magic happen.
http://jsfiddle.net/zekedroid/LkLkd405/2/
Better Solution: handsontable: hide some columns without changing data array/object
I have 6 textboxes at the top of the screen that update an entire column(one textbox per column) based on any changes. I was selecting the columns based on their class (.l#). Here is the code (issues to follow):
function UpdateField() {
var ctrl = this;
var id = parseInt(ctrl.id.replace("item", ""), 10) - 1;
var bound = [".l1", ".l7", ".l8", ".l9"];
var fields = $(bound[id]);
for (var i = 0; i < fields.length; i++)
{
fields[i].innerHTML = $(ctrl).val();
}
};
which is bound to the keyup event for the text areas. Issues are:
1) initially fields.length was -1 as I didn't want to put data in the "add new
row" section at the bottom. However, when running it, I noticed the
final "real" record wasn't being populated. Also, when stepping through, I
noticed that the "new row" field was before the "last row" field.
2) when doing it this way, it is purely superficial: if I double click the field,
the real data hasn't been changed.
so in the grand scheme of things, I know that I was doing it wrong. I'm assuming it involves updating the data and then forcing a render, but I'm not certain.
Figured out how to do it. Modified the original code this way:
function UpdateField() {
var ctrl = this;
var id = parseInt(ctrl.id.replace("item", ""), 10) - 1;
var bound = ['title1', 'title2', 'title3', 'title4'];
var field = bound[id];
for (var i = 0; i < dataView.getLength(); i++)
{
var item = dataView.getItem(i);
item[field] = $(ctrl).val();
dataView.updateItem(i, item);
}
grid.invalidate();
};
I have 6 textboxes (item1-item6) that "bind" to fields in the sense that if I change data in a textbox, it updates all of the rows and any new rows added also have this data.
Parts where the two issues can be explained this way:
1) to work around that, though still it would be a presentational fix and not a real updating of the underlying data, one could force it to ignore if it had the active class attached. Extra work, and not in the "real" direction one is going for (masking the field).
2) It was pretty obvious with the original implementation (though it was all I could figure out via Chrome Dev Tools that I could modify at the time) that it was merely updating a div's content and not actually interacting with the data underneath. Would look nice, and perhaps one could just pull data from the item1-item6 boxes in place of the column if it is submitted, but if someone attempts to modify the cell, they'll be looking at the real data again.
I need to select a specific row in kendoGrid but NOT by data-uid (as data-uid is changed when the grid dataSource is loaded again) but by the row itemID. I saw posts but they only select the row by uid which is not what I needed, I actually need to restart the HTML5 application and when grid is loaded, a specific item should be selected. This is what I've been seeing
Demo: jsfiddle.net/rusev/qvKRk/3/
e.g. the object has OrderID as ID, and every time the grid is loaded, it will be the same, unlike uid, I want to know how will I be able to select a row with OrderID, instead of uid.
You cam mix row itemID and data.uid, I guess.
var grid = $("#Grid").data("kendoGrid");
var dataItem = $("#Grid").data("kendoGrid").dataSource.get(itemID);
var row = $("#Grid").data("kendoGrid").tbody.find("tr[data-uid='" + dataItem.uid + "']");
Going along with what umais has mentioned, the better approach, since there is no built in functionality for this as of yet, would be to iterate through all the records to find the one you need. The function that I built will work even if there are pages of data. The only other way that I can think of doing this would be do do a secondary ajax call; But this works well. Note that i haven't tested it with more than 2000 records.
var dataGrid = $("#GATIPS").data("kendoGrid").dataSource;
var numOfRows = dataGrid.total();
var currentPageSize = dataGrid.pageSize();
dataGrid.pageSize(numOfRows);
var dataGridData = dataGrid.data();
for (var i = 0; i < numOfRows; i++) {
if (dataGridData[i].uid == e)
return dataGridData[i];
}
dataGrid.pageSize(currentPageSize); // reset the view
e is the UID. However this can be substituted for what ever variable you need just replace the check.
a work around that I managed to have, was to go through all rows and check which row model has that ID equal to the parameter, and then get that row data-uid and select the item through data-uid. It's working fine for me, since there were no suggestion, it's the better answer for now.
Well, accordingly to what I have done (and worked for me), and even though the work around isn't the prettiest, set one more Column, with your model id and with ClientTemplate then create any html object (div in my case) inside it give it a html id of your id, so when ever you need it, you just have to go and look with something like:
grid.dataItem($("td div#id").closest("tr"));
Because remember that the dataItem method is waiting for a selector then you get your selectedItem as regular one.
EDIT:
I forgot to say, that you should (or could) use the style property
display:none
If you don't want to display that col.
I'm using jqGrid with cell editing. I have setup the colModel properties using the editrules option. Everything works fine in that if I edit a cell and try to save an invalid value the grid displays an error dialog, but I need to know how to position the error message dialog that comes up because in the case of my layout it ends up behind a video. I'm not quite sure how to hook into this and there don't seem to be any obvious options on how to do it.
In this case the dialog I would be trying to manipulate is the one with ID of info_dialog.
Also I'm using the clientArray option for cellsubmit.
I realize this is rather old but upon searching I didn't find any indication this might have been added since, so I figured now that I've figured it out I'd let everyone know how I solved the positioning of mine.
$(document).ready(function ()
{
$.jgrid.jqModal = $.extend($.jgrid.jqModal || {}, {
beforeOpen: centerInfoDialog
});
});
function centerInfoDialog()
{
var $infoDlg = $("#info_dialog");
var $parentDiv = $infoDlg.parent();
var dlgWidth = $infoDlg.width();
var parentWidth = $parentDiv.width();
$infoDlg[0].style.left = Math.round((parentWidth - dlgWidth) / 2) + "px";
}
From what I could find in the jqGrid source code, you can add a beforeOpen and an afterOpen. In my case I'd rather position the thing before it's displayed (duh!). Would be nice if there was a parameter to hook it up in the grid declaration, but this does the trick in the mean time.
I hope this helps someone! I spent most of my afternoon on this!
Default value of zIndex parameter of info_dialog is 1000. The function info_dialog from grid.common.js part of jqGrid will be called from grid.celledit.js without usage a 4-th parameter which can change the option.
So the best pragmatical way which I could recomend you is to decrease zIndex value of your div with the video so that it will be less then 1000.