Autosave input box's to database during pause in typing? - javascript

I have a large form on my website that I want to be able to autosave to a database as the user is filling it out. Almost identical to how google drive works when typing a document.
I am trying not to have a function that runs every X seconds but rather a function that runs when the user has taken a break in typing. So, if the user has not typed in 1 hour but is still on the page, it doesn't keep pushing save requests.
This is all I have so far which is a basic javascript form submit.
$("#page1Form").submit(function(event){
event.preventDefault();
$changesSaved.text("Saving...");
var url = "/backend/forms/page1-POST.php";
$.ajax({
type: "POST",
url: url,
data: $("#page1Form").serialize(),
success: function(data) { $changesSaved.text(data); }
});
return false;
});

Debounce the textarea change.
Demo: jsFiddle
Put your ajax call in the saveToDB() function. These event names('input propertychange change') will trigger on any form element change such as radio buttons, inputs, etc.
var timeoutId;
$('#the-textarea').on('input propertychange change', function() {
console.log('Textarea Change');
clearTimeout(timeoutId);
timeoutId = setTimeout(function() {
// Runs 1 second (1000 ms) after the last change
saveToDB();
}, 1000);
});
function saveToDB()
{
console.log('Saving to the db');
}
Here is a full demo showing you how to debounce a full form and use ajax to send the data and then return the status (Saving, Saved, etc).
Demo full form and ajax: jsFiddle

I know that this question is old, but I would like to include a code that I like the most. I found it here:
http://codetunnel.io/how-to-implement-autosave-in-your-web-app/
Here is the code:
var $status = $('#status'),
$commentBox = $('#commentBox'),
timeoutId;
$commentBox.keypress(function () {
$status.attr('class', 'pending').text('changes pending');
// If a timer was already started, clear it.
if (timeoutId) clearTimeout(timeoutId);
// Set timer that will save comment when it fires.
timeoutId = setTimeout(function () {
// Make ajax call to save data.
$status.attr('class', 'saved').text('changes saved');
}, 750);
});
It saves after the user stops writing for more than 750 milliseconds.
It also has a status letting the user know that the changes have been saved or not.

Try Sisyphus.js https://github.com/simsalabim/sisyphus. It persists the form data in the browser's local storage and is robust against tabs closing, browser crashes, etc...

Related

CRM 2013: Force refresh a form after save

We have a requirement to refresh the form after saving (to ensure that some hide/show logic works as expected based on a field value).
Currently the form does not automatically refresh after saving the record.
I have gone through some articles and found this reference:
http://msdn.microsoft.com/en-us/library/dn481607%28v=crm.6%29.aspx
When I try to do either of the below, it results in an infinite loop and throws a 'Callstack exceeded Max limit' error.
OnSave(context)
{
//my logic
...
...
Xrm.Page.data.save.then(SuccessOnSave, ErrorOnSave);
}
function SuccessOnSave()
{
//force refresh
Xrm.Page.data.refresh();
}
function ErrorOnSave()
{
//do nothing
}
OnSave(context)
{
...
...
//force refresh
Xrm.Page.data.refresh(true).then(SuccessOnSave, ErrorOnSave);
}
function SuccessOnSave()
{
//do nothing
}
function ErrorOnSave()
{
//do nothing
}
Can someone please explain me how to use the refresh or save method to do a force refresh of the form ??
Rajesh
I use to achieve it with following code
Xrm.Page.data.save().then(
function () {
Xrm.Page.data.entity.attributes.forEach(function (attribute, index) {
attribute.setSubmitMode("never");
});
Xrm.Utility.openEntityForm(Xrm.Page.data.entity.getEntityName(), Xrm.Page.data.entity.getId());
},
function (errorCode, message) {
}
);
For me this is what solved the purpose (CRM 2015)
// Save the current record to prevent messages about unsaved changes
Xrm.Page.data.entity.save();
setTimeout(function () {
// Call the Open Entity Form method and pass through the current entity name and ID to force CRM to reload the record
Xrm.Utility.openEntityForm(Xrm.Page.data.entity.getEntityName(), Xrm.Page.data.entity.getId());
}, 3000);
If you want to do a hard refresh on the form data, you will likely want to do a location reload. What I've done in the past is put the refresh logic in a function that is called when the Form is loaded (after being saved). The tricky part about this is that the function can get called if the form is auto-saved in CRM 2013. You also want to take into account that you don't want to refresh the form on the first load, since this would result in an infinite reloading loop. Here's an example:
var formLoaded = false;
function formLoad() {
if (formLoaded) {
window.location = location.href;
}
formLoaded = true;
}
You have attached the OnSave() method to OnSave event. So, logically if you call save again within the same event, the calls goes recursively.
From MSDN
Xrm.Page.data.refresh(save).then(successCallback, errorCallback);
Parameter: save - A Boolean value to indicate if data should be saved
after it is refreshed.
So, you will have to pass 'false' to this method (You just need to refresh, no save is required)
As I couldn't find the complete code for this written in a 'generically' reusable way, here goes:
var triggeredSave = false;
//Attach the OnSave Form event to this OnSave function
//and select passing of context as the first parameter.
//Could instead be attached programmatically using the code:
//Xrm.Page.data.entity.addOnSave(OnSave);
function OnSave(context) {
var eventArgs = context.getEventArgs();
var preventedAutoSave = false;
//Preventing auto-save is optional; remove or comment this line if not required.
preventedAutoSave = PreventAutoSave(eventArgs);
//In order to setup an after save event function, explicitly
//invoke the save method with callback options.
//As this is already executing within the OnSave event, use Boolean,
//triggeredSave, to prevent an infinite save loop.
if (!preventedAutoSave && !triggeredSave) {
triggeredSave = true;
Xrm.Page.data.save().then(
function () {
//As the form does not automatically reload after a save,
//set the save controlling Boolean, triggeredSave, back to
//false to allow 'callback hookup' in any subsequent save.
triggeredSave = false;
OnSuccessfulSave();
},
function (errorCode, message) {
triggeredSave = false;
//OPTIONAL TODO: Response to failed save.
});
}
}
function PreventAutoSave(eventArgs) {
if (eventArgs.getSaveMode() == 70 || eventArgs.getSaveMode() == 2) {
eventArgs.preventDefault();
return true;
}
return false;
}
//Function OnSuccessfulSave is invoked AFTER a save has been committed.
function OnSuccessfulSave() {
//It seems CRM doesn't always clear the IsFormDirty state
//by the point callback is executed, so do it explicitly.
Xrm.Page.data.setFormDirty(false);
//TODO: WHATEVER POST SAVE PROCESSING YOU REQUIRE
//e.g. reload the form as per pre CRM 2013 behaviour.
ReloadForm(false);
//One scenario this post save event is useful for is retriggering
//Business Rules utilising a field which is not submitted during save.
//For example, if you implement a Current User field populated on Form
//Load, this can be used for user comparison Business Rules but you
//may not wish to persist this field and hence you may set its submit
//mode to 'never'.
//CRM's internal retriggering of Business Rules post save doesn't
//consider changes in fields not submitted so rules utilising them may
//not be automatically re-evaluated or may be re-evaluated incorrectly.
}
function ReloadForm(preventSavePrompt) {
if (preventSavePrompt) {
Xrm.Page.data.entity.attributes.forEach(function (attribute, index) {
attribute.setSubmitMode("never");
});
Xrm.Page.data.setFormDirty(false);
}
Xrm.Utility.openEntityForm(Xrm.Page.data.entity.getEntityName(),
Xrm.Page.data.entity.getId());
//Another way of trying Form reload is:
//window.location.reload(true);
}
Use Mscrm.Utilities.reloadPage();
I found this post helpful in demonstrating the difference between Xrm.Page.data.refresh() and Xrm.Utility.openEntityForm(entityName, id).
TL;DR - if you want the screen to repaint, consider using Xrm.Utility.openEntityForm(entityName, id).
You can achieve by placing Modified On field on the form. And set visible by default property to false.
Use below JS to refresh the form
function refreshCRMFORM()
{
setTimeout(function () {
// Call the Open Entity Form method and pass through the current entity name and ID to force CRM to reload the record
Xrm.Utility.openEntityForm(Xrm.Page.data.entity.getEntityName(), Xrm.Page.data.entity.getId());
}, 1000);
}
Create On Change event on Modified on field and provide above function name.

How to prevent Autoclick Spam of upvote/downvote button?

I have a upvote button, which the user can press and a vote will be registered. On clicking this button again, the vote gets cancelled.
So each press of this button is doing a DataBase Write. If an autoclicker is used on this button continuously, continuous DB calls will happen. I want to prevent this.
What can I do?
PS. I am sending an ajax query to the backend(running Django) when the upvote button is clicked.
You'd really want to check this on the server, so it doesn't matter if someone disables javascript.
If you're using PHP one option is to set a time limit between votes.. eg only allow one vote per minute.
So on every vote, store the time of the vote in a session variable, then ignore subsequent votes if it is within the time limit:
//on vote
$now=date('U');
if(!isset($_SESSION['lastvote']) || $now - $_SESSION['lastvote']<LIMIT){
$_SESSION['lastvote']=$now;
// do database call
}else{
//throw error
}
Probably the easiest approach is to disable the buttons while a single operation is running.
To obtain this, assuming you're using a JQuery ajax request to call the "upvote" / "downvote" method you simply have to:
$("#upvoteButton").click(function)
{
$("#upvoteButton").attr("disabled");
$.ajax({
url: 'url/upvote.extension',
success: function(data) {
$("#upvoteButton").removeAttr("disabled");
}
});
}
This way a single request is send & resolved per single tab / window.
If you trust that your users accept cookies, you could store the click together with the object id and a timestamp in the session, and test whether the user clicked the vote button during the last n seconds.
You also could disable the vote button on the web page with javascript, and enable it again after n seconds to revoke the vote.
Both suggestions are dependent on users who use a javascript enabled or cookie enabled browser.
If you have to take bots into consideration who just send the url repeatetly to the server, you need to take a different approach - e.g. check whether the current ip address has called the url in the last n seconds and raise a 403 Forbidden http error or something.
If you can use setTimeout you can try something like this:
var toggleVote = (function () {
var voted = false,timeout;
return function () {
if( timeout )
clearTimeout(timeout)
voted = !voted
timeout = setTimeout(function() {
console.log("Ajax call - vote : " + voted)
},400)
}
})()
document.getElementById("vote").addEventListener("click",toggleVote)
Heres an example on JSBin
Try mashing the "Click me" div
Easy way:
<button id="upvote" onclick="upvote(this)">Up</button>
<script>
var upvote = function(x){
$(x).removeAttr('onclick'); //if you click this button again, it will do nothing
//call ajax to do something here...
//...............
//after finishing, add attribute onclick to the button
$(x).attr('onclick', 'upvote(this)')
};
</script>

keyup(function()) ajax request delay - jQuery

I have a jQuery Ajax request, that I want to call with text input, and so I nested it inside keyup(function(). This works fine.
$("#text_box").keyup(function() {
//AJAX REQUEST
});
But this behaves buggy sometimes. When I input some text very fast, I am getting results for input word with some last letters of the original input word omitted (may be some fault with browser). I want the ajax request to be sent when there is no input activity for a second, I mean, if I input text very fast and rest for a second (means I made the input). How can I do this?
It sounds as if you get results from a previous ajax call. Use a timer with setTimeout and clearTimeout.
var timer = null;
$("#text_box").keyup(function() {
if(timer) {
clearTimeout(timer);
}
timer = setTimeout(someFunction, someDelay);
});
Where someFunction is a function which does your ajax call and someDelay is the delay you want to wait before doing the call, after the user has typed, in ms.
As you are already using jQuery you could use the debounce plugin from Ben Aleman.
Example from the page
// Bind the not-at-all debounced handler to the keyup event.
$('input.text').keyup( text_1 );
// Bind the debounced handler to the keyup event.
$('input.text').keyup( $.debounce( 250, text_2 ) ); // This is the line you want!
omg. for somebody who will search in 2014...
function sendAjax() {
setTimeout(
function() {
$.ajax({
url: "url.php",
type: "POST",
data: data,
success: function(data) {
$("#result").html(data);
}
});
}, 2000);
}
<input onkeyup="function()">

time an ajax request

Is there any way to time how long a jquery ajax request has been going on? sometimes searches take too long and it would be nice to add a jquery abort() button if the search takes over, say, 5 seconds. Any way I can do this!
On the other end of the ajax request is a php file that makes a postgresql request.
Much thanks for any ideas!
Take a look at the timeout option (http://api.jquery.com/jQuery.ajax/). You can set it on a particular call, or globally with $.ajaxSetup().
To have the abort button appear after 5 seconds, add a setTimeout function after your call to send. Once the AJAX command is complete, you can add code to clear the timeout and remove the abort button if it exists.
var timeOutID = 0;
$.ajax({
url: 'ajax/test.html',
success: function(data) {
clearTimeOut(timeOutID);
// Remove the abort button if it exists.
}
});
timeOutID = setTimeout(function() {
// Add the abort button here.
}, 5000);
This way the abort button will never appear if AJAX returns quick enough.
Usually, I'll set a timeout once the request is sent that will trigger after 10 seconds or so and then fallback on something else to make sure it still happens (for example, form submission).
So set a variable to false, var failed = false; and do the request
At the same time that the request starts, set a timeout:
setTimeout(function() {
failed = true;
$("#form").submit();
return false;
}, 10000);
In the return function of the ajax call, check to see if the failed variable has been set to true, and if it has, don't actually do whatever it was originally trying, otherwise it could mess something up, or confuse the user if something else is happening (since these things usually happen on slower internet connections, if the next step appears while a new page is loading, they might try to interact and then the page will change).
$.post("ajaxcall.php", {'etc': "etc"},
function(returned) {
if (failed != true) {
//do whatever with returned variable
}
});
var timer = 0,
XHR = $.ajax({
url: 'ajax/mypage.html',
beforeSend: function() {
timer=setTimeout(showAbort, 5000);
}
});
function showAbort() {
$('<input type="button" value="Abort" id="abort_button"/>').appendTo('#some_parent');
$('#abort_button').on('click', function() {
XHR.abort(); //abort the Ajax call
});
}
XHR.always(function() { //fires on both fail and done
clearTimeout(timer);
if ($('#abort_button').length) {
$('#abort_button').remove(); //remove button if exists
}
});

Change and Keyup Event Handler in JQuery

I'm using JQuery, CakePHP, and Mysql in my application.
I'm having a code like below in which instructions are a Textbox that when I type it will be shown in the Display panel.
$(".TextFieldSettings #instructions").keyup(function (){
instr=$(".TextFieldSettings #instructions").val();
$("#displayPanel .fieldInstructions"+counter).html(instr).show();
});//Text field instructions keyup
This Code Works well.
Edit:
If I change the value in the Textbox instructions, the keyup value must be shown in the Display Panel. Mean while I need the final changed value in the instr to insert into the database.
How can I do so?
You can delay your operation :
$(".TextFieldSettings #instructions").keyup(function (){
setTimeout(function () {
var instr=$(".TextFieldSettings #instructions").val();
$("#displayPanel .fieldInstructions"+counter).html(instr).show();
}, 1);
});//Text field instructions keyup
That way, when the function is executed, the input value has been updated with the keypress.
Depending on how much content your instructions is holding/how frequently keypressing, i think updating the database every keyup is a bit overkill. Maybe update the database every 5 seconds or something? with each keypress triggering/resetting a setTimeout function.
Also, you can replace your call to pull out the text value with just $(this);
$(".TextFieldSettings #instructions").keyup(function (){
var instr = $(this).val();
$("#displayPanel .fieldInstructions"+counter).html(instr).show();
// Update database
$.ajax({
method: 'POST',
url: 'path/to/a/script_file/executing_the_sql.ext',
data: { 'str': escape(instr.replace(/\n/g,'<br />')) },
success: function(){},
error: function(e){ alert(e.responseText); }
});
});//Text field instructions keyup

Categories

Resources