I would like to display a table which have several thousand rows with complex formatting (color, font, border, etc. done on the ASP.Net Core server).
Initially, I generated an html copy of all the data (stored in a SQL Server database), but realised it wasn't optimal since the generated html data accounted for more than 50MB.
No, I only generate about 200 rows; 100 visible and 50 hidden above and below (cache). I would like to freely scroll the tablen, but when there are only 25 hidden rows above or below, fetch new rows from the controller which are then prepend or append to the table. Basically, I want to give enough room so I can can populate the table when I'm scrolling through the "hidden" (cache) rows.
Everything seems to work well, but I believe I need to use a web-worker to run the function in a background thread which add new rows to the table independently of the table being scrolled.
Below is a excerpt of the code :
I use a debunce function to only catch the lastest position of the mouse scroll.
The scroll function basically only checks whether there are enough hidden rows (cache) above or below the table. If it reaches the threshold, it either prepends (scroll upwards) or appends (scroll downwards) rows obtained from the controller.
The main issue is that I can't scroll the table when the new rows are being fetch as the page freezes. It only takes about 1 to 2 seconds to populate to new (scrollable) rows but it isn't smooth.
Could anyone help me improve the code? (general ideas) I also read that there are already existing libraries but can't really get my head around them..
$('#fields-table > tbody').on('wheel', _.debounce(async function (event) {
await scroll(); // Probably change it to a web-worker or promise?
}
async function scroll() {
var threshold = 200; // Corresponds to approximatively 50 rows (above and below).
var above = $('#fields-table').scrollTop();
var below = $('#fields-table > tbody').height() - $('#fields-table').height() - above;
// Gets the scroll delta based on the table heights.
var delta = 0
if (above < threshold) delta = above - threshold; // Scrolls upwards.
if (below < threshold) delta = threshold - below; // Scrolls downwards.
await addCacheRows(delta); // Prepends (delta < 0) or appends (delta > 0) or appends rows obtained via the fetch API.
}
Your problem is unlikely to resolve with a web worker. Without seeing more code I cannot tell for sure, but I suspect your code to generate new rows is not sufficiently efficient. Remember:
Use DocumentFragment to create the HTML, do not immediately append it to the main DOM tree row by row. Appending elements to a document triggers some recalculations.
Unless this is a LOT of data or requires lots of work serverside, you can immediately start preloading next/previous rows. Keep the promise object and only await it once you need them, that's the simplest way to go around it
Use passive scroll event listener - Firefox even shows a console warning whenever you do not do that
There is no way generating 200 rows of table data should take seconds. Since you use JQuery anyway (really, in 2022?), note that there are plugins for this. I don't remember what I used, but it worked perfect and scrolled smooth with much more data than what you have.
Thank you for your help. I realise it won't be as straightforward as I initally thought (I made some tests with WPF virtualization as well).
Regarding the time it takes to generate the extra rows, I believe it mostly comes from the server. Sure, I can probably load new rows independently of the threshold.
I've never heard about DocumentFragment, but that something I should definitely consider.
Related
I need an algorithm to tell a sprite to end as soon as the text finishes appearing, basically I need to make a cutscene which describes the beginning of a story within the game, but since I'm using gamemaker, I don't know how to do it, can someone help me?
For cutscenes and automated image sequences you usually have some sort of variables, that handle the states and counters for displaying sprite's sub-images.
But firstly I like to use mainly two main time-related variables in my controller object usually named sys (counter and counterTime):
c = 0; // and for stepEvent: c += 1; // so it goes +1 per step
ct = 0; // and for stepEvent: ct+= 1 / room_speed; // so it goes +1 per second
During the cutscene you might want to stop everything else that moves:
is_cutscene = false; // objects might not be able to move when this is true
// you still have to implement that by yourself
// player can't move if is_cutscene, nothing happens!!
(except cutscene buttons etc...)
So now when the player gets to a 'cutscene'y object and triggers some cutscene_1 = true eg. you can have the image/text/sound/animation object come to life/create/active... That object might have in the create event:
duration = 6; // 6 seconds the scene lasts
start_time = sys.ct // and in Step: if sys.ct > (start_time + duration)
// then -> cutscene advance/destroy
Ofcourse - this is super simple and now you could only say implement:
walk close to a pop-up object
show an image for 6 seconds
it dissappears
And it might not be big trouble to implement it... BUT THIS may also be the foundation for more advanced multi-step sequence and cuts.
Whenever I try to test a Qualtrics survey in preview mode, Qualtrics.SurveyEngine.addOnload will be called twice. This is not a problem for conditional events (as in most of the examples), but a large problem for unconditional code a timed page change (this will be triggered twice as well). See the following snippet:
Qualtrics.SurveyEngine.addOnload(function()
{
$('NextButton') && $('NextButton').hide();
var that = this;
var timeOutInterval=1000+Math.trunc(Math.random()*10000);
alert(timeOutInterval); //for Testing only
var myVar;
myVar = setTimeout(function(){ that.clickNextButton();}, timeOutInterval);
});
If I launch the survey, this will lead to a page change after 1-11 seconds. If I preview the survey, this change will happen as well, followed by a second change. The alert will be shown twice as well.
Does anyone have a solution, how this functionality could be tested in preview mode?
I've run into Survey Preview issues with JFE as well. There are ways to get around JFE mode and preview in non-JFE mode.
If only care about a specific set of questions in a block and don't care about the survey flow, the easiest solution is to use View Block. It does not use JFE. Go to the Block drop down and choose View Block.
If you need to preview the whole survey, there are tricks to 'break' JFE and force it to non-JFE mode. These tricks seem to be a moving target as Qualtrics makes changes. The best one (easiest) I've found that is working for me today on my Qualtrics account (notice all the qualifiers) is to add an end of survey object to the survey flow, click custom, and check the "Override Survey Options" box.
If that doesn't work, I've found that once a survey gets over a certain size, it doesn't use JFE mode anymore. I don't know what the limit is, but if you add a bunch of fake questions after your end of survey you can trick it that way as well.
Qualtrics links jQuery as of current writing (albeit the shorthand $ is reserved for the prototype.js library).
Following should skip execution of addOnload javascript in the mobile preview:
Qualtrics.SurveyEngine.addOnload(function()
{
if(jQuery(this.questionContainer).parents('.MobilePreviewFrame').length)
{
console.log('Mobile Preview - skipping rest of addOnload');
return true;
};
console.log("Running addOnload()");
// The rest of your code. Log statements can obviously be removed
});
Hope this is helpful
It seems Qualtrics now defaults to JFE mode for live surveys as well. We have been able to resolve this by adding the query string &Q_JFE=0 to the end of our survey URLs, like so:
https://uleidenss.eu.qualtrics.com/SE/?SID=SV_123432434343&Q_JFE=0
This had the additional benefit of solving our issue with JFE mode breaking several of our long time running Qualtrics JQuery experiments.
I use dojo.grid.datagrid
If the DataGrid contains lots of rows(example 200) and to scroll, it does not work exit from edit mode cell. Do know what the problem is?
upd: Or maybe someone knows how to use dgrid / gridx in xPages, becouse i found next big bug - encoding after save rest service :(
I just did some testing and I believe I'm seeing the same thing. It seems to be fine to edit and save as I move through the grid. I can scroll down as needed and save changes. However, when I scroll back up and put a cell in edit mode, it doesn't save the changes -- it immediately reverts to the original value. And sometimes it just leaves the cell in edit mode.
I would agree that it seems to be an issue with memory management. If I set the rowsPerPage to a number that will keep all rows in memory, it appears (with very limited testing) that I can scroll up and down and make changes and they're all saved.
I don't have a solution at the moment, but what I would suggest in lieu of a perfect solution is to find a way to set rowsPerPage to a number greater than the amount of rows that will be displayed in the grid. If there's too much data for that to be feasible, then the approach I would take is to provide filtering on the grid to keep the maximum number of rows displayed much lower and then it won't be as much of a performance hit to set the rowsPerPage to a sufficient amount.
If I come across a better solution, I'll come back and post it here.
Yeah! I solved it! :) Error in FileStore.js(extlib). Add this on onClientLoad. I change "!!error code"
restViewItemFileService._processResponse = function(requestObject, data) {
this._items.splice(0, this._items.length); // !!error code -> this.close();
this._start = requestObject.start;
//TODO: clear identity?
dojo.forEach(data.items, function(entry, idx) {
var item = {storeRef:this, attributes:entry};
var id = item.attributes[this._identity]
var pending = this._pendings[id]
if(pending) {
for (var s in pending.modAttrs[s]) {
this.item.attributes[s] = pending.modAttrs[s]
}
}
this._byIdentity[id] = item;
this._items.push(item);
}, this);
this._topLevelEntries = data['#toplevelentries'];
this.onData(requestObject, data);
this._finishResponse(requestObject);
}
I've got a ASP.net page that create an excel file using eeplus library (http://epplus.codeplex.com/). my problem is that I create a really big file. It take times to be done and showed to the client. I search a way to show a kind of progressbar of the creation.
for the moment, the client click and wait til the file is created, so I cant really showed something.
What you're trying to do is actually pretty complicated. You might just want to show a spinning wheel gif or something and save yourself the headache.
However, if you're feeling adventurous, read on.
To use a progress bar, you need to create a way to measure how much % complete your task is. This usually involves some kind of incrementer that gets measured against the total number of rows/column/whatever that you're creating. I don't know what language you're using, so here's some pseudo to help you out:
var totalRows = 100;
var processedRows = 0;
var progress = 0;
while( processedRows < totalRows ){
Process_A_Row();
processRows++;
progress = processedRows / totalRows;
StoreProgressSomewhereForPolling();
}
So there's your basic mechanism for tracking progress. On the client, you'll need to set up a way to poll the value of the progress variable. This gets messy because you'll quickly learn that you need a way to isolate the progress variable for every individual request. Its up to you how to implement this -- there are lots of ways to do it. One solution I saw stored the progress value in a static dictionary keyed by username so that it could be easily polled by the client with webmethods.
A quick solution would be to use the Ajax.NET UpdateProgress control. Just display a GIF that spins while the server is processing.
THE GOAL I WANT TO ACHEIVE:
Control AE timelines using ONE EXPRESSION LAYER (much like using Actionscript) to trigger frequently used comps such as blinking, walking, flying etc... for cartoon animation.
I want animate a the blinking of a cartoon character. (and other actions, explained below) Rather than "re posting" the comp or key frames movements every time I want a blink or a particular action, I want to create a script where I can trigger the Blink comp to play. Is this possible? (Sidenote: A random blink through entire movie would be nice) but I still want to know how to do this for the reasons below.
Ideally: I would like to create an "Expressions layer" in the main comp to TRIGGER other comps to play. At certain points I would like to add triggers to call frequently used comps that contain actions like.. Blinking, Walking, Flying, Look Left and Right etc...
IT WOULD BE AMAZING IF somehow we could trigger other comps to begin, repeat, stop, maybe reverse, and do this all from one Main Comp using an expression layer.
WHY DO IT THIS WAY?
Why not just paste a comp in the spot you want it to play every time you want such action? Well in after effects if you wanted a "blink comp" to play 40 times in two minutes you would have to create 40 layers, or pate the key frames on that comp 40 times. Wouldn't it be awesome to trigger or call it from one one layer when you wanted it from one expressions layer?
We do something like this in Flash using Actionscript all the time. It would be awesome if there was a method out there to achieve this effect. This would be an OUTSTANDING tutorial and I believe it would be very popular if someone did it. It could be used for a MULTITUDE of amazing effects and could save a ton of time for everyone. Heck, help me figure this out and perhaps I will make a tutorial.
Thank you all ye "overflowing Stackers" who contribute! :)
I found the answer and that is...
IT'S NOT POSSIBLE.
After Effects expressions can not control other timelines. Unfortunately you have to put an expression on each layer you want to affect.
The next best solution, and to achieve something close to what I was asking can be found on this link: motionscript.com/design-guide/marker-sync.html
We can only hope that Adobe will someday give the power to expressions like they did with action-script.
HOPEFULLY SOON! Anyone reading this who works for Adobe please plead our case. Thanks
Part 1: Reference other layers in pre-Comps
Simply replace "thisComp" with "comp("ComName")"
To reference Effect-Controllers between compositions, follow the below formula:
comp("ComName").layer("LayerWithExpression").effect("EffectControlerName")("EffectControllerType")
More In-depth Answer: Adobe's Docs - Skip to the Layer Sub-objects part
As I understand the Adobe documentation, only Layers can be accessed,
not footage. What this means is that you will need to create your
expression link utilizing a pre-Comp. Footage can not access this so
that also means no nulls, adjustment layers, etc.
As an added bonus, if you use the essential graphics panel, you can put all the controllers in one pre-comp, but have the controls available no matter which comp you are in. Just select it in the Essential-Graphics dropdown.
Part 2: Start/End based on other layers within pre-comps:
Regarding the next part where you want the expressions to activate based on other compositions, I recommend using the in-out Point expression.
inPoint | Return type: Number. Returns the In point of the layer, in seconds.
outPoint | Return type: Number. Returns the Out point of the layer, in seconds.
If you utilize the start time overrides you can pull this from:
startTime | Return type: Number. Returns the start time of the layer, in seconds.
Alternate Option:
I would recommend avoiding this as the keyframes are basically referenced as an index, so things can get messed up if you add one ahead of a keyframe you were already using - def incorporate some error handling.
Refer to the Key attributes and methods (expression reference) Here
Part 3: Interpolation & Time Reversal
You can time reverse the layer in the rightclick->time, otherwise this is all interpolation expressions like loop out etc - you can loopOut("FOO") a pre-comp if you not only cut it correctly, but also enable time remapping.
then use this to loop those keyframes;
try{ timeStart = thisProperty.key(1).time; duration = thisProperty.key(thisProperty.numKeys).time-timeStart; pingPong =
false; //change to true value if you want to loop animationn back &
forth quant=Math.floor((time-timeStart)/duration);
if(quant<0) quant = 0
if(quant%2 == 1 && pingPong == true){
t = 2*timeStart+ (quant+1)*duration - time;
}
else{
t = time-quant*duration;
}
}
catch(err){
t = time;
}
thisProperty.valueAtTime(t)