Event after DOM manipulation in Sencha Touch - dom

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.

Related

Angular Ag Grid - Has anyone figured out a way to wait for a cell node update to happen and THEN fire a function, like a callback?

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.

Row selection using forEachNode very slow

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)

jsTree Node Expand/Collapse

I ran into the excellent jstree jQuery UI plug in this morning. In a word - great! It is easy to use, easy to style & does what it says on the box. The one thing I have not yet been able to figure out is this - in my app I want to ensure that only one node is expanded at any given time. i.e. when the user clicks on the + button and expands a node, any previously expanded node should silently be collapsed. I need to do this in part to prevent the container div for a rather lengthy tree view from creating an ugly scrollbar on overflow and also to avoid "choice overload" for the user.
I imagine that there is some way of doing this but the good but rather terse jstree documentation has not helped me to identify the right way to do this. I would much appreciate any help.
jsTree is great but its documentation is rather dense. I eventually figured it out so here is the solution for anyone running into this thread.
Firstly, you need to bind the open_node event to the tree in question. Something along the lines of
$("tree").jstree({"themes":objTheme,"plugins":arrPlugins,"core":objCore}).
bind("open_node.jstree",function(event,data){closeOld(data)});
i.e. you configure the treeview instance and then bind the open_node event. Here I am calling the closeOld function to do the job I require - close any other node that might be open. The function goes like so
function closeOld(data)
{
var nn = data.rslt.obj;
var thisLvl = nn;
var levels = new Array();
var iex = 0;
while (-1 != thisLvl)
{
levels.push(thisLvl);
thisLvl = data.inst._get_parent(thisLvl);
iex++;
}
if (0 < ignoreExp)
{
ignoreExp--;
return;
}
$("#divElements").jstree("close_all");
ignoreExp = iex;
var len = levels.length - 1;
for (var i=len;i >=0;i--) $('#divElements').jstree('open_node',levels[i]);
}
This will correctly handle the folding of all other nodes irrespective of the nesting level of the node that has just been expanded.
A brief explanation of the steps involved
First we step back up the treeview until we reach a top level node (-1 in jstree speak) making sure that we record every ancestor node encountered in the process in the array levels
Next we collapse all the nodes in the treeview
We are now going to re-expand all of the nodees in the levels array. Whilst doing so we do not want this code to execute again. To stop that from happening we set the global ignoreEx variable to the number of nodes in levels
Finally, we step through the nodes in levels and expand each one of them
The above answer will construct tree again and again.
The below code will open the node and collapse which are already opened and it does not construct tree again.
.bind("open_node.jstree",function(event,data){
closeOld(data);
});
and closeOld function contains:
function closeOld(data)
{
if($.inArray(data.node.id, myArray)==-1){
myArray.push(data.node.id);
if(myArray.length!=1){
var arr =data.node.id+","+data.node.parents;
var res = arr.split(",");
var parentArray = new Array();
var len = myArray.length-1;
for (i = 0; i < res.length; i++) {
parentArray.push(res[i]);
}
for (var i=len;i >=0;i--){
var index = $.inArray(myArray[i], parentArray);
if(index==-1){
if(data.node.id!=myArray[i]){
$('#jstree').jstree('close_node',myArray[i]);
delete myArray[i];
}
}
}
}
}
Yet another example for jstree 3.3.2.
It uses underscore lib, feel free to adapt solution to jquery or vanillla js.
$(function () {
var tree = $('#tree');
tree.on('before_open.jstree', function (e, data) {
var remained_ids = _.union(data.node.id, data.node.parents);
var $tree = $(this);
_.each(
$tree
.jstree()
.get_json($tree, {flat: true}),
function (n) {
if (
n.state.opened &&
_.indexOf(remained_ids, n.id) == -1
) {
grid.jstree('close_node', n.id);
}
}
);
});
tree.jstree();
});
I achieved that by just using the event "before_open" and close all nodes, my tree had just one level tho, not sure if thats what you need.
$('#dtree').on('before_open.jstree', function(e, data){
$("#dtree").jstree("close_all");
});

Meteor: Elements from CollectionA re-rendering when I insert to CollectionB

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;

Dropping onto an event (as opposed to the calendar) - how to identify event?

I have successfully implemented dragging of a jquery-ui element onto my fullCalendar. The problem is that what I want to drop onto is not the calendar itself but a specific event displayed on the calendar in order to add the dropped item to the event. The missing piece is how to identify the event that was under the mouse when I dropped.
drop: function (date, allDay, jsEvent, ui)
{
var event = ???;
event.description += ui.helper.data("filters").text;
$('#calendar').fullCalendar('updateEvent', event);
}
I've discovered the solution. Basically you have to add "droppable" to the event element. I do this by catching the "eventRender" (I assume this is a good spot)...
eventRender: function (event, element)
{
// store the ID for later...
$(element).data('id', event.id);
element.droppable({
drop: function (event, ui)
{
// get the ID I stored above...
var rowID = $(this).data('id');
I just implemented this - thankyou for your solution!
I'm using it in combination with drop - I need to be able to drop events either onto another event or onto a date.
In my case adding event.stopPropogation(); in element.droppable drop is necessary to stop the date drop function from also triggering.