I have a form which contains one drop down, check box and four buttons. When any of the action is performed (check/uncheck or drop down selection or button click), it has to trigger a service call and the below section should be updated. There may be a chance that the multiple actions can be performed one after other immediately. If this is the case, http call should be triggered only after last activity.
Would be nice if there is any workable idea for this. I feel the timeout can helpful to wait for user to complete all activities (waiting for certain time after each activity) and call http service.
I guess thats what you need:
var timeoutPromise;
var delayInMs = 2000;
$scope.$watch("your_form_scope", function(newValue, oldValue) {
$timeout.cancel(timeoutPromise); //does nothing, if timeout alrdy done
timeoutPromise = $timeout(function(){ //Set timeout
//your code
},delayInMs);
});
Related
I have ASP Web Forms Web application, which is used for searching in my database. I have page which contains gridview and also text input field for filtering results.
Input field has onkeyup event which run postback by JavaScript for refreshing the gridview from codebehind.
And also in codebehind I have method, which does saving state last filter request, and if request do not equal previously filter request, then I run new query to database.
My problem - onkeyup event on text input field may generate multiple postbacks. If it first postback, then server will execute the query to database. But following postbacks do not execute and overlap first results. And therefore my gridview stay old state.
<script type="text/javascript">
function DoUpdateGridView() {
var timeout = null;
clearTimeout(timeout);
timeout = setTimeout(function () {
Sys.WebForms.PageRequestManager.getInstance()._doPostBack('<%=GUIGridUpdatePanel.ClientID%>', '<%=GUIGridUpdatePanel.UniqueID%>');
}, 1000);
};
</script>
How you may see I set delay for execute JavaScript, but this only Delays execution of the code but does not interrupt.
I think i may make cache first results, and return it.
But I need know all solutions for this problem.
Сan there be any way to perform only the latest event from the JavaScript?
But then somebody may send multiple request and my web application may fall (DDOS).
You can't interrup what is already happening on the server, but there is an issue with your "delay" code. You need to persist the timeout variable between calls.
Try:
<script type="text/javascript">
//New Position for timeout variable
var timeout = null;
function DoUpdateGridView() {
//This will now clear the existing timneout function
clearTimeout(timeout);
timeout = setTimeout(function () {
//For debug purposes only
console.log("About to postback");
Sys.WebForms.PageRequestManager.getInstance()._doPostBack('<%=GUIGridUpdatePanel.ClientID%>', '<%=GUIGridUpdatePanel.UniqueID%>');
}, 1000);
};
</script>
When two (or more) postbacks are made from the same client at the same time, they send the same ViewState data to the server as part of each request. Control state is part of ViewState. Assuming Session is being handled in the default manner, these requests are processed by the server serially.
Let's say the server alters controls while handling the first request. Then the second request is processed. But since both postbacks were generated at the same time, the second request has the same ViewState (and thus the same control state) as the first request, so the state of the controls that the server sees is the state before the first request was processed, and not the state after the first request was processed!
The simplest solution is to store the pieces of state you are using to determine whether to change the controls in the Session, which by default is stored in memory on the server, thus making the two requests distinguishable.
I have this situation, i'm building a shopping cart, and i need to make a http request each time a product is added or its quantity is changed.
I have one button to add 1 unit of a product to the cart, it supposed that each unit added should make the http request, but i want to avoid to make to many calls at once... So i wanted to set a timeout for the request, but if within 3 secs the user clicks again, then i should cancel the last request with old data and then set a new timeout fot the request with the new data.
So i came up with this solution
function doRequest(){
clearTimeout(state.request);
state.request = setTimeout(updateCartService, 180);
}
The function is inside another function that retrieves the state object (A collection of variables and data persistent through all the application). The updateCarteService function contains the http request.
However all the requests are still being made, if i click 5 times the button that triggers doRequest, the call i being excecuted 5 times.
Have any idea of what could be wrong or pherhaps a better aproach to achieve my goal?
If it helps i'm bulding the site using vueJs, vuex (flux) and this code is inside an action.
setTimeout takes milliseconds, so you set timout for 180ms, it is too short, try 3000 for example. Here a small jsfiddle example.
https://jsfiddle.net/ShinShil/5fw2c63o/
var timeout;
$('#click').click(function() {
clearTimeout(timeout);
timeout = setTimeout(setText, 3000);
});
function setText() {
$('#text').append('text');
}
I've read this and am trying to implement similar functionality
"If you’re using toggle buttons, e.g.: to turn something on and off and it hits an API endpoint, be sure to wrap that interaction in a timer so you’re not calling that endpoint multiple times if the user decides to tap the control multiple times in quick succession. Wait a second, then hit the endpoint."
I have a settings page with a series of toggle switches. Rather than do an API call at the change of each and every toggle I want to delay the API call that passes the value of the scope to my server. I want to delay it by X seconds. However, by doing this
$scope.settingsChange = function () {
$timeout(function () {
//save_notifications
console.log('called');
// Trigger API call
}, 3000);
};
All that's happening its just delaying the call by 3000ms. I still get the console log appear 3 times if I toggle something 3 times.
I'm fully aware this is because I've implemented a timeout function. I'm not sure what to implement to get the functionality the poster alludes to in my quote above.
Any ideas much appreciated.
Thanks,
Note: my template looks like this
<ion-toggle ng-repeat="item in settingsData"
ng-model="item.checked"
ng-checked="item.checked"
ng-change="settingsChange()">
{{ item.text }}
</ion-toggle>
once you initiate the API call, block any other calls from being made.
$scope.settingsChange = function () {
if ($scope.processingSettingChange) return;
$scope.processingSettingChange = true;
$timeout(function () {
//save_notifications
console.log('called');
// Trigger API call
$scope.processingSettingChange = false;
}, 3000);
};
you might also want to disable the UI while the API is processing the request. You should be able to using the same $scope variable and ng-disabled
I have a series of checkboxes and want to make an $http request when the user checks a checkbox. However, since the http request takes some time to complete and the user may want to check a few different boxes at a time, I want to delay sending the http request until the user has stopped checking boxes.
I have tried using $timeout to set a delay before sending the $http request, but this doesn't really queue the selections before sending.
var nodeList = [];
$scope.checkNode = function(checkedNode, sendRequestFn) {
nodeList.push(checkedNode);
$timeout(function() {
sendRequestFn(nodeList);
nodeList = []; // Sent the list, so reset this.
}, 500);
}
Clear your timeout each time a button is clicked. With your implementation you are starting a new timeout each click.
var nodeList = [], waitBeforeRequest;
$scope.checkNode = function(checkedNode, sendRequestFn) {
// if we are waiting, reset the timer
if (waitBeforeRequest) $timeout.cancel(waitBeforeRequest);
// wait for any more clicks before sending
waitBeforeRequest = $timeout(function () {
sendRequestFn(nodeList);
nodeList = [];
}, 500)
}
You need to know when the user is done with the form and is ready to submit their checkbox changes. So, you should tie the http request to a submit button or button click of some kind, and the user will click on this button when done, triggering the update.
If you don't want to tie the http update to a button click, then I think tying the event to a timeout is your second best option, since there's no way to tell when a user is done with input.
I think what you're after is called a debounce function. You can write your own, but it is available in the underscore library, that is usually fine to include in the page along with Angular. Example usage would be:
var nodeList = [];
var debouncedSend = _.debounce(function(sendRequestFn) {
sendRequestFn(nodeList);
nodeList = [];
}, 500);
$scope.checkNode = function(checkedNode, sendRequestFn) {
nodeList.push(checkedNode);
debouncedSend(sendRequestFn);
};
The debouncedSend would be called outside of Angular, so you might need to wrap the function in $apply, as in:
var debouncedSend = _.debounce(function(sendRequestFn) {
$scope.$apply(function() {
sendRequestFn(nodeList);
nodeList = [];
});
}, 500);
My users are presented a basically a stripped down version of a spreadsheet. There are textboxes in each row in the grid. When they change a value in a textbox, I'm performing validation on their input, updating the collection that's driving the grid, and redrawing the subtotals on the page. This is all handled by the OnChange event of each textbox.
When they click the Save button, I'm using the button's OnClick event to perform some final validation on the amounts, and then send their entire input to a web service, saving it.
At least, that's what happens if they tab through the form to the Submit button.
The problem is, if they enter a value, then immediately click the save button, SaveForm() starts executing before UserInputChanged() completes -- a race condition. My code does not use setTimeout, but I'm using it to simulate the sluggish UserInputChanged validation code:
<script>
var amount = null;
var currentControl = null;
function UserInputChanged(control) {
currentControl = control;
// use setTimeout to simulate slow validation code
setTimeout(ValidateAmount, 100);
}
function SaveForm() {
// call web service to save value
document.getElementById("SavedAmount").innerHTML = amount;
}
function ValidateAmount() {
// various validationey functions here
amount = currentControl.value; // save value to collection
document.getElementById("Subtotal").innerHTML = amount;
}
</script>
Amount: <input type="text" onchange="UserInputChanged(this)">
Subtotal: <span id="Subtotal"></span>
<button onclick="SaveForm()">Save</button>
Saved amount: <span id="SavedAmount"></span>
I don't think I can speed up the validation code -- it's pretty lightweight, but apparently, slow enough that code tries to call the web service before the validation is complete.
On my machine, ~95ms is the magic number between whether the validation code executes before the save code begins. This may be higher or lower depending on the users' computer speed.
Does anyone have any ideas how to handle this condition? A coworker suggested using a semaphore while the validation code is running and a busy loop in the save code to wait until the semaphore unlocks - but I'd like to avoid using any sort of busy loop in my code.
Use the semaphore (let's call it StillNeedsValidating). if the SaveForm function sees the StillNeedsValidating semaphore is up, have it activate a second semaphore of its own (which I'll call FormNeedsSaving here) and return. When the validation function finishes, if the FormNeedsSaving semaphore is up, it calls the SaveForm function on its own.
In jankcode;
function UserInputChanged(control) {
StillNeedsValidating = true;
// do validation
StillNeedsValidating = false;
if (FormNeedsSaving) saveForm();
}
function SaveForm() {
if (StillNeedsValidating) { FormNeedsSaving=true; return; }
// call web service to save value
FormNeedsSaving = false;
}
Disable the save button during validation.
Set it to disabled as the first thing validation does, and re-enable it as it finishes.
e.g.
function UserInputChanged(control) {
// --> disable button here --<
currentControl = control;
// use setTimeout to simulate slow validation code (production code does not use setTimeout)
setTimeout("ValidateAmount()", 100);
}
and
function ValidateAmount() {
// various validationey functions here
amount = currentControl.value; // save value to collection
document.getElementById("Subtotal").innerHTML = amount; // update subtotals
// --> enable button here if validation passes --<
}
You'll have to adjust when you remove the setTimeout and make the validation one function, but unless your users have superhuman reflexes, you should be good to go.
I think the timeout is causing your problem... if that's going to be plain code (no asynchronous AJAX calls, timeouts etc) then I don't think that SaveForm will be executed before UserInputChanged completes.
A semaphore or mutex is probably the best way to go, but instead of a busy loop, just use a setTimeout() to simulate a thread sleep. Like this:
busy = false;
function UserInputChanged(control) {
busy = true;
currentControl = control;
// use setTimeout to simulate slow validation code (production code does not use setTimeout)
setTimeout("ValidateAmount()", 100);
}
function SaveForm() {
if(busy)
{
setTimeout("SaveForm()", 10);
return;
}
// call web service to save value
document.getElementById("SavedAmount").innerHTML = amount;
}
function ValidateAmount() {
// various validationey functions here
amount = currentControl.value; // save value to collection
document.getElementById("Subtotal").innerHTML = amount; // update subtotals
busy = false;
}
You could set up a recurring function that monitors the state of the entire grid and raises an event that indicates whether the entire grid is valid or not.
Your 'submit form' button would then enable or disable itself based on that status.
Oh I see a similar response now - that works too, of course.
When working with async data sources you can certainly have race conditions because the JavaScript process thread continues to execute directives that may depend on the data which has not yet returned from the remote data source. That's why we have callback functions.
In your example, the call to the validation code needs to have a callback function that can do something when validation returns.
However, when making something with complicated logic or trying to troubleshoot or enhance an existing series of callbacks, you can go nuts.
That's the reason I created the proto-q library: http://code.google.com/p/proto-q/
Check it out if you do a lot of this type of work.
You don't have a race condition, race conditions can not happen in javascript since javascript is single threaded, so 2 threads can not be interfering with each other.
The example that you give is not a very good example. The setTimeout call will put the called function in a queue in the javascript engine, and run it later. If at that point you click the save button, the setTimeout function will not be called until AFTER the save is completely finished.
What is probably happening in your javascript is that the onClick event is called by the javascript engine before the onChange event is called.
As a hint, keep in mind that javascript is single threaded, unless you use a javascript debugger (firebug, microsoft screipt debugger). Those programs intercept the thread and pause it. From that point on other threads (either via events, setTimeout calls or XMLHttp handlers) can then run, making it seem that javascript can run multiple threads at the same time.