Access rendered items - javascript

Playing around with Slickgrid. But got some questions that I'm not figurate out.
I have made an Regex filter on two X cells, and it works surprisingly well.
But for every time you filter or make other actions i want to flash all incorrect fields with cellFlash or highlighter.
Atm i have made a formatter with same regex as filter uses but it seems not 100% correct.
The problem when i'm using cellFlash is that it trigger the animation on ALL rows not only the rows thats rendered.
I'm not sure that i'm triggering the flashcell on correct callback/stage, I did it on my filter function i saved all incorrect rows in an array then i loop them throught and trigger flash.
So is it possible to get all items thats rendered in Viewport? Haven't find any information about this. Only data i can get out from getRenderedViewport.. is pxls. getRenderedRange() or getViewport()..

If you need to do processing on each data item in the current slick grid viewport, you can use getRenderedRange() to get the range of data item indexes that are rendered. You could then use that to get each visible data item
function forEachItemInViewport(fn) {
var range = slickGrid.getRenderedRange();
var bottom = range.bottom;
while(bottom--) {
var dataItem = slickGrid.getDataItem(bottom);
fn(dataItem);
}
}
forEachItemInViewport(function (item) {
// do your work on each item in viewport
});

Related

Can I have expanded one node only on load in Tabulator.js?

I am using nested data trees in Tabulator.js and if I use the option dataTreeStartExpanded:true all nested rows will be expanded. I want only one be expanded on load/render? Is that possible?
Here is working jsFiddle to play around.
I found that there is a div with class tabulator-data-tree-control-expand or tabulator-data-tree-control-collapse. Changing the name in dev tools does nothing.
Currently it looks like
But I want to be like that after the web page loads
Maybe I can somehow click the + to expand. Tabulator has a listener there
But I do not know how to call it for that particular +.
oh, I found it in documentation.
.. a function that will be passed in a row component and the level of that row in the table (starting at 0), it should return a boolean value to indicate the rows expansion state:
var table = new Tabulator("#example-table", {
dataTree:true,
dataTreeStartExpanded:function(row, level){
return row.getData().driver; //expand rows where the "driver" data field is true;
},
});
and then in data you have to use new property driver:true

How to create a snappy User Interface with lots of local data?

I have a JSON file with 15000+ items. My JSON file has about 7MB. I'd like to display all results in a table (without pagination) and add a few checkboxes to display or hide the items under certain conditions. To simplify as much as possible, let's say I have a checkbox to display items from Paris only:
The problem is that the UI is not smooth. Clicking on the checkbox takes about one second to refresh the table. It's unacceptable.
I'm using fetch() to download JSON data and then keep it locally. When user clicks on the checkbox, here's what I did:
document.getElementById('paris-only').addEventListener('click', updateTable);
function updateTable() {
const parisOnly = document.getElementById('paris-only').checked;
items.forEach(item => item.visible = !parisOnly || item.city === 'Paris');
refreshTable();
}
function refreshTable() {
[...document.getElementById('items-table').children].forEach((tr, index) => {
tr.classList.toggle('hidden', !items[index].visible);
});
}
.hidden {
display: none;
}
I tried to play with setTimeout(_, 0), but it doesn't help much. It makes the UI freeze less, but it still takes a lot of time to refresh the table.
Is there any strategy available to make such an interface snappy? One of my ideas was to consider <canvas>, which makes drawing many elements quick, but it doesn't seem very natural for the task.
Another option would be to load data with fetch() on scroll, but I'd like to have all data loaded just once if possible.
What else should I consider?
I'd recommend using classes to signify if a given node should be shown or hidden (e.g. every node with name !== 'Paris' is in the notparis class. You can use javascript to initialize these categories (How do I add a class to a given element?) when you fetch the JSON table, and then showing/hiding can use for (tr of document.getElementsByClassName('notparis')) tr.visible = !parisOnly. If these categories overlap (i.e. you want notparis and notcoldweather), you could probably record which classes are currently visible or hidden and use document.getElementsByClassName('notparis notcoldweather') (this may be a bit more complex, but should maintain performance).
Also, changing the items directly instead of using an intermediate list might be more performant.

Tabulator range slider to update filter

I am currently using a range slider and am stuck as I cannot easily clear/update the previous values filters. This is the code that takes in values from the slider and set the filters on the table.
function priceFilter(values){
table.setFilter([
{field:"price",type:">=", value: values[0]},
{field:"price",type:"<=", value: values[1]},
]);
}
function gallonFilter(values){
table.setFilter([
{field:"gallons",type:">=", value: values[0]},
{field:"gallons",type:"<=", value: values[1]},
]);
}
Here is a JSfiddle of what I currently have that does not work as intended. This is due to setFilter wiping all previous filters. So only one slider is working at a time.
Here is one "solution" I have found that does the correct behavior, but looks awkward as the table reloads twice on a slide (once starting, once ending). This works by removing the previous filter at the start of the slide, then adding new filters at the end.
How can I update the values in these filters that does not cause the table to reload, or at least make it not as visible?
The setFilter function is designed to replace all existing filters. If you simply want to add another filter to existing filters then you should us the addFilter function
table.addFilter("age", ">", 22);
Full details on how to manipulate filters can be found in the Filter Management Documentation

Prevent multiple select element from automatically sorting the value assigned to it basis the order of the indexes in the options

I am using the select2 plugin to convert a multiple select html element to a more presentable format. Also I don't think my question is very much dependent on the plugin.
What the plugin does internally is -
this.select.val(val);
where this.select points to the hidden multiple select element.
On feeding the function above a val of say - 2,4,0 ,
the value stored as confirmed when I do an alert(this.select.val()) is 0,2,4 , i.e. with automatic unwanted sorting according to the order of the options in the select element.. :/
DEMO - http://jsfiddle.net/rohanxx/DYpU8/ (thanks to Mark)
Is there a way to preserve the sort order after feeding in the value to my select element?
Thanks.
This is a very good question. I think this is more to do with the multiselect html element, rather than select2.
If you have a normal multiselect, there is no "order" sort of speak. You just have a list in the original order, with either each item selected or not.
I'm almost 100% sure there is a better way of doing this than the below, but for a workaround it should do just fine.
End result:
JavaScript code
// 'data' brings the unordered list, while 'val' does not
var data = $('#e1').select2('data');
// Push each item into an array
var finalResult = [];
for( item in $('#e1').select2('data') ) {
finalResult.push(data[item].id);
};
// Display the result with a comma
alert( finalResult.join(',') );
JSFiddle:
http://jsfiddle.net/DYpU8/4/
A little late for an answer but I actually found a way of doing this.
Keep in mine that this method will hide the options that are already selected, because for my use case it looked better, plus it needs to be that way in order the choices to be in the order the user made them.
$('.my-multi-select').select2('Your Options').on("select2:select", function (e) {
$('[data-option-id="' + e.params.data.id + '"]').insertBefore(_this.find('option:not(:selected):eq(0)'));
}).on("select2:open", function () {
_this.append(_this.find('option:not(:selected)').sort(function (a, b) {
return +a.getAttribute('data-sort-order') - +b.getAttribute('data-sort-order');
}));
});
And for the styles
.select2-results__option[aria-selected=true]{
display:none !important;
}
You will want to make sure you know how the jQuery .sort() function works for you to be able to modify this for your own needs.
Basically what this is doing is when you select an option, it gets hidden and then placed at the bottom of the other selected options, which are before the unselected options. And when you open the drop down, it sorts all of the unselected options by their pre-determined sort order.

How to update ZK Grid values from jQuery

I have three Tabs and in each tab, I have a Grid.
The data for each Grid is coming from a database, so I am using rowRenderer to populate the Grids. The following code is common for all three Grids:
<grid id="myGrid1" width="950px" sizedByContent="true" rowRenderer="com.example.renderer.MyRowRenderer">
The rows are constructed from Doublebox objects. The data is populated successfully.
The Problem:
I need to handle multiple-cell editing on the client side. The editing is done via mouse-clicking on a particular cell and entering a value.
As example let's say that the user edits first cell on the first row and the value should be
propagated to all other cells on the same row and in all three Grids (so also the two Grids which the user currently does not see, because they are in tabpanes).
I am using jQuery to do this value propagation and it works OK.
I am passing the jQuery as follows:
doublebox.setWidgetListener(Events.ON_CHANGING, jQuerySelectors);
doublebox.setWidgetListener(Events.ON_CHANGE, jQuerySelectors);
This makes it possible to change the value in 1 cell and the change is instantly (visually) seen in all other cells filtered by jQuery selectors.
The problem is that the value is visually distributed to all the cells, but when I try to save the Grid data back to the database, the background values are the old ones.
I am assuming that ZK-Grid component is not aware that jQuery changed all the cell values. Nevertheless if I manually click on a cell that already has the NEW value (enter/leave/change focus) when I save the grid the NEW value is correct in that particular cell. Maybe that's a hint how can I resolve this.
Code of how I extract the Grid values:
Grid tGrid = (Grid) event.getTarget().getFellow("myGrid1");
ListModel model = tGrid.getModel();
MyCustomRow tRow = (MyCustomRow)model.getElementAt(i);
The model for my Grid is a List of MyCustomRow:
myGrid1.setModel(new ListModelList(List<MyCustomRow> populatedList));
I have a couple of assumptions, but whatever I have tried, hasn't worked. I have in mind that jQuery events and ZK-Events are different and probably isolated in different contexts. (Although I have tried to fire events from jQuery and so on..)
Do you have any suggestions? As a whole is my approach correct or there's another way to do this? Thanks for your time in advance!
Your problem is exactly what you are expecting.
Zk has it's own event system and do not care about your jq,
cos it's jq and zk don't observ the DOM.
The ways to solve your problem.
Use the "ZK-Way":
Simply listen at server-side and chage things there.
I am not sure if not selected Tabs
are updateable, but I am sure you could update the Grid
components on the select event of the Tab.
Fire an zk-event your self:
All you need to know, is written in the zk doc.
Basically, you collect your data at client side, send
an Event to the server via zAu.send() extract the
data from the json object at serverside and update your Grids
I would prefer the first one, cos it's less work and there should not be
a notable difference in traffic.
I post the solution we came up with:
This is the javascript attached to each Doublebox in the Z-Grid
//getting the value of the clicked cell
var currVal = jq(this).val();
//getting the next cell (on the right of the clicked cell)
objCells = jq(this).parents('td').next().find('.z-doublebox');
// if there's a next cell (returned array has length) - set the value and
// fire ZK onChange Event
if (objCells.length) {
zk.Widget.$(jq(objCells).attr('id')).setValue(currVal);
zk.Widget.$(jq(objCells).attr('id')).fireOnChange();
} else { //otherwise we assume this is the last cell of the current tab
//So we get the current row, because we want to edit the cells in the same row in the next tabs
var currRow = jq(this).parents('tr').prevAll().length;
//finding the next cell, on the same row in the hidden tab and applying the same logic
objCellsHiddenTabs = jq(this).parents('.z-tabpanel').next().find('.z-row:eq(' + currRow + ')').find('.z-doublebox');
if (objCellsHiddenTabs.length) {
zk.Widget.$(jq(objCellsHiddenTabs).attr('id')).setValue(currVal);
zk.Widget.$(jq(objCellsHiddenTabs).attr('id')).fireOnChange();
}
}
The java code in the RowRenderer class looks something like this:
...
if (someBean != null) {
binder.bindBean("tBean", someBean);
Doublebox box = new Doublebox();
setDefaultStyle(box);
row.appendChild(box);
binder.addBinding(box, "value", "tBean.someSetter");
...
private void setDefaultStyle(Doublebox box) {
box.setFormat("#.00");
box.setConstraint("no negative,no empty");
box.setWidth("50px");
String customJS = ""; //the JS above
//this is used to visually see that you're editing multiple cells at once
String customJSNoFireOnChange = "jq(this).parents('td').nextAll().find('.z-doublebox').val(jq(this).val());";
box.setWidgetListener(Events.ON_CHANGING, customJSNoFireOnChange);
box.setWidgetListener(Events.ON_CHANGE, customJS);
}
What is interesting to notice is that ZK optimizes this fireOnChange Events and send only 1 ajax request to the server containing the updates to the necessary cells.

Categories

Resources