I am using AG Grid React. I have a thousand row of data. I am trying to select a range of rows based upon their indices.
gridOptions.api.forEachNode(node => {
if (node.childIndex >= startIndex && node.childIndex < endIndex) {
node.setSelected(true)
}
});
This is turning out to be very sow and typically takes 30 seconds on the UI. Seems like setSelected triggers multiple render cycles. What is the correct way of doing it?
The problem is - setSelected(newValue) dispatches events. When we do it in a loop for a thousand items(say) - there are a thousand events, a thousand requests queued for asynchronous update in React which can be accounted for all the delay.
I fixed it using another version of setSelected - setSelected(newValue, clearSelection, suppressFinishActions). Unfortunately, this is not written in the official documentation.
The idea is to use this version for all but the last selection, so that all the event dispatches are supressed and use the normal selection we have been using forever to select the last element so that it also triggers necessary events for onRowSelected, onSelectionChanged, etc. to work normally.
this.api.forEachNodeAfterFilter(node => {
if (node.childIndex >= startIndex && node.childIndex < endIndex) {
selectedNodes.push(node);
}
});
if (selectedNodes.length > 0) {
// setSelected re-renders every time so use suppressFinishActions except last one
for (let i = 0; i < selectedNodes.length - 1; i++) {
selectedNodes[i].setSelected(true, false, true);
}
selectedNodes[selectedNodes.length - 1].setSelected(true);
}
You can try to use pagination to load the chunk of data thane the whole data.
Pagination Docs
Make sure that you don't have subscribed to any events that may cause your code to be slow, e.g. onRowSelected, onSelectionChanged. These get triggered for each
node.setSelected(true)
If you do have any of these subscribed events, one possible way to solve this, is to unsubscribe before the loop and then re-subscribe after the loop.
Also, depending on your use case you may want to use
forEachNodeAfterFilter(node)
in order to only loop through visible nodes instead of
forEachNode(node)
Related
I have these two functions that should add and remove the item on the list. The adding one should also calculate the total sum of all items, but it doesn't work quite exactly as it should. Here is the adding function:
Future getTotal(item) async {
int counter = 0;
counter += int.parse(item);
totalPrice.add(counter);
_totalPrice.forEach((element) => counter += element);
print(totalPrice);
print(counter);
return totalPrice;
}
It first declares counter (which is sum basically), then it should onTap calculate the total sum of all items, but I get this problem, where it adds items properly, but the counter adds one extra item on the first tap, like the counter is added twice in the beginning, I suspect this code is the issue:
counter += int.parse(item);
totalPrice.add(counter);
Here is the issue in pictures:
Now for the delete function
I have this simple delete function, but it doesn't seem to remove any item onTap:
deleteSumItem(item) {
_totalPrice.remove(item);
notifyListeners();
print(totalPrice);
}
It should work together with the getTotal(item) function, removing the particular item from it, but as I mentioned, it doesn't seem to do anything, and returns full list each tap.
I hope I was clear, I never seem to figure out the simple issues. Thank you!
You are adding the element twice here: _totalPrice.forEach((element) => counter += element);
with regards to removing, you are using (mainly) totalPrice when adding but _totalPrice when removing. See the difference?
I am utilizing cellChanged.node.setDataValue(fieldChanged, oldValue) inside of the (cellValueChanged) event emitter, and I'm having trouble figuring out how to call a function once the setDataValue function has finished executing. I need to do this to do a check to see if a user has the permission to update a cell.
Here is the full code that checks:
if(this.showPaywallNotification) {
// Okay, so the budget is above what we allow HOWEVER...
if(budget > BUDGET_AMOUNT) {
this.showPaywallNotification = false;
cellChanged.node.setDataValue(fieldChanged, oldValue)
// Note: This timeout is in place to prevent the infinite updating bug
// This is problematic because if the user changes the cells fast enough, they can get around the paywall. If I change the timeout to be smaller, the resulting change triggers the update, which ends up creating an infinite loop of updates.
setTimeout(() => {
this.showPaywallNotification = true;
}, 230)
}
}
Is there a way I can replace my setTimeout() function with something better that can always ensure the user can't get around my paywall by just updating the cell faster than the timeout can execute?
You don't have to do polling. setDataValue is a not an async function.
Also, onCellValueChanged won't get called again if you call node.setDataValue.
Have a look at this plunk: Cell Editing - Revert to old value. Try updating any Age value to negative.
onCellValueChanged($event) {
if ($event.colDef.field === 'age' && $event.newValue < 0) {
// debugger
$event.node.setDataValue('age', $event.oldValue);
console.log('value reverted');
}
}
Let me know if something is not clear, or this is not sufficient.
I want to set a minimum and a maximum zoom level in my map.
My first idea was to listen to 'zoomstart' events, but the org.gwtopenmaps.openlayers.client.Map class doesn't implement any listener with such event type. Then I tried to listen to 'zoomend' events. My idea was to check the zoomlevel after the zoom event and if it is higher/lower than a threshold value than i zoom to that threshold value. Example code:
#Override
public void onMapZoom(MapZoomEvent eventObject) {
if (eventObject.getSource().getZoom() > 18) {
eventObject.getSource().zoomTo(18);
}
}
But i found, the zoomTo event doesn't fire in this case. Has anybody got a solution to this problem?
Great idea Imreking.
I have added this to the GWT-Openlayers library.
So if you download the latest version from github now you can do :
map.setMinMaxZoomLevel(6, 8);
And you no longer need some javascript method in your own code.
I actually also added a showcase but having difficulties uploading it to our website.
Uploading the new showcase has now succeeded.
See http://demo.gwt-openlayers.org/gwt_ol_showcase/GwtOpenLayersShowcase.html?example=Min%20max%20zoom%20example to see an example of newly added Map.setMinMaxZoomLevel(minZoom, maxZoom).
I don't think this is possible in OpenLayers (normal and GWT).
According to me two solutions are available.
Option 1
This is ugly for the user. As he sees the map getting zoomed, and just after this going back to the previous zoomlevel.
The Timer is needed to give OL the chance to animate the zoom.
map.addMapZoomListener(new MapZoomListener()
{
#Override
public void onMapZoom(final MapZoomEvent eventObject)
{
Timer t = new Timer()
{
#Override
public void run()
{
if (eventObject.getSource().getZoom() > 15)
{
map.zoomTo(15);
}
else if (eventObject.getSource().getZoom() < 10)
{
map.zoomTo(10);
}
}
};
t.schedule(500);
}
});
Option 2
Don't use the zoom default zoom control but create your own zoom buttons (using normal GWT), and putting these on top of the map. If you want you can style these buttons in the same way as the normal buttons. The trick 'create in normal GWT, and make it look like OL' is a trick I use a lot (for example to create a much more advanced layer switcher).
Note : I am one of the developers of GWT-OpenLayers, if you want I can add an example to our showcase displaying how to do 'Option 2'.
Knarf, thank you for your reply. I tried the 'Option 1' and it worked, but i found another solution which is maybe more acceptable for the users.
My solution is:
map.isValidZoomLevel = function(zoomLevel) {
return ((zoomLevel != null) &&
(zoomLevel >= minZoomLevel) &&
(zoomLevel <= maxZoomLevel) &&
(zoomLevel < this.getNumZoomLevels()));
}
I overrode the isValidZoomLevel method. The minZoomLevel and maxZoomLevel variables were set when the application started. I don't like calling javascript from GWT code, but here i didn't have any other opportunity.
Is their an event available after DOM manipulation in Sencha Touch has succeeded?
I want to measure the time it takes to render a list with 1000 elements.
Therefor, a timer is started when the list is initialized and stoppend when the list is painted like so:
listeners: {
initialize: function () {
start = new Date();
var store = Ext.getStore('Songs');
for (var i = 1; i <= 1000; i++) {
store.add({id: i});
}
},
painted: function () {
stop = new Date();
Ext.Msg("Timer", stop - start);
}
}
The painted event is triggerd before DOM manipulation so the 1000 listitems are not visible when the rentertime pops up.
Is there an other event that is triggerd after DOM has been manipulated and the list is updated?
Or is there an alternative method to measure the time it takes to do this?
Greets,
Sander Van Loock
Unfortunately you are not answering, therefore I can only guess:
You are adding each item seperately to the store. In Sencha Touch this will eat up time. Better to create an array of items and add them at once.
If you are adding items and the update of the store takes too long, stop sorting of the store.
If you are interested in the list you better work with before and after events. Something like before updatedata and after updatadata. Or you could add start to the first itemTpl.
But again. Painting of a list which is infinite will not take any real time. So the DOM part really is not what you are looking for.
If you are using the dataview, this might be different.
I'm attempting to fade-in new elements in a reactive {{#each}} of the comments posted by users.
I have a code sample at https://gist.github.com/3119147 of a very simple comments section (textarea and new comment insert code not included, but it's very boilerplate.). Included is a snippet of CSS where I give .comment.fresh { opacity: 0; }, and then in my script, I have:
Template.individual_comment.postedago_str = function() {
var id = this._id;
Meteor.defer(function() {
$('#commentid_'+id+'.fresh').animate({'opacity':'1'}, function() {
$(this).removeClass('fresh');
});
});
return new Date(this.time).toString();
};
Which seems like a terrible place to execute an animation. My thinking is that each time a new comment is rendered, it will need to call all my Template.individual_comment.* functions, so that's why my animation defers from one of those. However, Meteor is calling Template.individual_comment.postedago_str() each time a different collection (Likes) is inserted to. This means I click the Like button, and my whole list of comments flashes white and fades back in (very annoying!).
I read the Meteor documentation and tried to figure out how to better slice up my templates so only chunks will update, and I added id="" attributes everywhere that seemed reasonable.. still this bug. Anyone know what's going on?
TIA!
As a workaround, you could wrap an {{if}} block around the fresh class on individual comments, that would check the comment's creation time and only add the fresh class in the first place if the comment is actually recent. Something like:
<div class="comment{{#if isActuallyFresh}} fresh{{/if}}" id="commentid_{{_id}}">
And then define the isActuallyFresh function:
Template.individual_comment.isActuallyFresh = function() {
if ((new Date().getTime() - this.time) < 300000) // less than 5 minutes old
return true;
else
return false;