ruby on rails autosave forms - ruby-on-rails

I have a pretty extensive form on one of my rails sites and I was wondering if its possible to dynamically save the form for every onchange input. What I'm trying to prevent is users taking the time to fill the form out and then lose all their changes because of a connectivity issue or something stupid like that...
Any suggestions? Basically, I don't want the form to have to be submitted at all. I just want the form to save like a preference would in an Mac OS X (no apply or save button it just saves).
using rails 3...thanks!

The strain that would put on your server and DB would be several orders of magnitude higher than a more traditional approach. I also agree with Kyle that I would be very confused about the lack of submit button. At the very least, you'll need to notify users each time data is sent to the server and saved successfully, otherwise they'll have no idea why you aren't asking them to save.
Also, think about all the overhead. With every keystroke the user will have to initiate a connection, send their HTTP headers, cookies, the contents of the form, etc.
Have you considered an autosave feature instead? Maybe save the form every 2 minutes if changes have been made, and then put a submit button on the form as well? I think it would save you a great deal of pain, but get you almost the same benefit.

You could attach an ajax event to each input losing focus that would call the Controllers update method.
Most users would be surprised by this behavior though because it isn't the expected behavior of a web site.

If you use AJAX to call update, then here are a few things that might help: jQuery's serialize() function can help gather the form data into a post request without having to call the forms submit action. Using save(:validate => false) will bypass validation if you are saving drafts and want to skip validation until the final save.

Related

Synchronous update of form data

This is basically a simple question, but I haven't found a good answer here or with Google.
We have a Ruby on Rails app with a form that needs to update a section when the user changes the selected option in a pulldown menu. The complication is that this update will be from the database - it can't realistically store and hide all the possible data when it loads. The obvious solution is an Ajax call, but the problem is that Ajax is asynchronous, and our update is inherently synchronous. We need to ensure that the user gets the updated info before submitting or doing any other work on the form.
I know one can (but shouldn't) specify that an Ajax call be synchronous, but is there a better approach to this? I hate to do a separate post and reload every time the user selects a different option in one field, but I'm not sure what the best practice is for a situation like this.
Update: I tried switching to using an XMLHttpRequest object in JavaScript, and it may work (I'm figthing with Rails routing issues right now), but it gives me a warning that the synchronous option for this object is deprecated. Is there really no accepted way to get synchronous communication between a web page and server?

Rails Nested Forms: Error when using back button and saving after deleting a record

I've noticed a problem with nested forms and i'm not sure how to solve it. My forms are working fine normally, this isn't really a question about how to get them to work. This question is more of a 'what should I do in this scenario'.
When I use a nested form via fields_for it works great. When I add in the javascript to be able to add and remove fields everything works great. I can add, save, remove, save, no problems. However If you edit an object with a nested form then delete one of the nested objects (via sending the {'_delete' => true} parameter with the object), then you use the back button and save that form again without deleting the same fields you will get an error.
Your browser caches the field that should no longer be there, then Rails tries to find the old object via the 'id' element in the hash and it fails, rightfully so. Is there anyway I can prevent this? Do I need to just manually inspect the hash to make sure the element is still there? Is there some way I can force a browser refresh or something? Thanks for your time.
Yes and no. Your application has very little control over browser behavior. If the browser sends information to your application that no longer has relevance the best you can do server-side is validate against that and present the user with a clean error message (or silently drop the invalid record ids, but that could be very confusing to the user.)
The alternative is to try and get the browser to stop caching the page in question, you may have seen this before in bank or other sensitive applications where you hit your back button and you're presented with a warning "this page has expired". You could use meta tags or http headers to set the expiry date in the past or use pragma: no-cache.

How to store user preferences? Cookie becomes bigger

My application (Asp.Net MVC) has great interaction with the user interface (jQuery/js). For example, setting various searches charts, moving the gadgets on the screen and more .. I of course want to keep all data for each user. So that data will be available from any page in the Dumaine and the user will accepts his preferences.
Now I keep all data in a cookie because it did not seem logical asynchronous access to the server each time the user changes something and thet happens a lot.When the user logout from the application I save the cookie to the database.
The Q is how to save the settings back to the db - from the client to the server.
because the are a lot of interactin that I want to record.
example scanrios: closing widget,moving widget,resizing menues, ordering columens..
I want to record that actions. if I will fire ajax saving rutine for each action
ןt will be too cumbersome. Maybe I have no choice..
Maybe I should run an asynchronous saving all of a certain interval seconds.
The problem is the cookie becomes very large. The thought that this huge cookie is attached to each server request makes me feel that my attitude is wrong.
Another problem cookies have size limit. It varies from your browser but I definitely have been close to the border - my cookie easily become 4kb
Is there another solution?
Without knowing your code, have you considered storing the users preferences in a/your database. A UserPreference table with columns for various settings is a possibility.
You could update it via AJAX/JSON if you had a 'Save Preferences' option, or just update it on postback.
EDIT 1: After thinking about it, I think having an explicit 'save preferences' button would be beneficial and practical.
Somewhere on your page, where the use edits the things that generate the cookie, put an button called save, then hook up a jQuery click handler. On click, build a CSV string or another method of storing the preferences for posting back to the server, then use $.post to send it back to an action method in a controller.
Once there, store it in the database somehow (up to you exactly how), then return a JSON array with a success attribute, to denote whether the preference storing was successful.
When the page is loading, get the preferences out of the database and perform you manipulation.
Another solution would be to store the user preferences into the session and write some server side logic (like action filter) that would write those preferences as JSON encoded string on each page (in a script tag towards the end of the markup) making them available to client scripts.

What methods are available to stop multiple postbacks of a form in ASP.NET MVC?

A common web problem is where a user clicks the submit button of a form multiple times so the server processes the form more than once. This can also happen when a user hits the back button having submitted a form and so it gets processed again.
What is the best way of stopping this from happening in ASP.NET MVC?
Possibilities as I see it are:
Disable the button after submit - this gets round the multiple clicks but not the navigation
Have the receiving action redirect immediately - browsers seem to leave these redirects out of the history
Place a unique token in the session and on the form - if they match process the form - if not clear the form for a fresh submit
Are there more?
Are there some specific implementations of any of these?
I can see the third option being implemented as an ActionFilter with a HtmlHelper extension in a similar manner to the anti-forgery stuff.
Looking forward to hearing from you MVC'ers out there.
Often people overlook the most conventional way to handle this which is to use nonce keys.
You can use PRG as others have mentioned but the downside with PRG is that it doesn't solve the double-click problem, it requires an extra trip to the server for the redirect, and since the last step is a GET request you don't have direct access to the data that was just posted (though it could be passed as a query param or maintained on the server side).
I like the Javascript solution because it works most of the time.
Nonce keys however, work all the time. The nonce key is a random unique GUID generated by the server (also saved in the database) and embedded in the form. When the user POSTs the form, the nonce key also gets posted. As soon as a POST comes in to the server, the server verifies the nonce key exists in its database. If it does, the server deletes the key from the database and processes the form. Consequently if the user POSTs twice, the second POST won't be processed because the nonce key was deleted after processing the first POST.
The nonce key has an added advantage in that it brings additional security by preventing replay attacks (a man in the middle sniffs your HTTP request and then replays it to the server which treats it as a legitimate).
You should always return a redirect as the HTTP response to a POST. This will prevent the POST from occuring again when the user navigates back and forth with the Forward/Back buttons in the browser.
If you are worried about users double-clicking your submit buttons, just have a small script disable them immediately on submit.
You might want to look at the Post-Redirect-Get (PRG) pattern:
This really isn't MVC specific, but the pattern we follow on our web pages is that actions are performed with AJAX calls, rather than full page POSTs. So navigating to a url never performs an action, just displays the form. The AJAX call won't be in the history
Along with the disabling of buttons, you can add a transparent div over the entire web page so that clicking does nothing. We do this at my work and add a little friendly label saying processing request..
The most elegant solution I found was to use ActionFilters:
Blog post is here

Preventing double HTTP POST

I have made a little app for signing up for an event. User input their data and click "sign me in".
Now sometimes people are double in the database, the exact same data that got inserted 2 times very quickly after each other. This can only mean someone clicked the button twice, which caused two posts to happen.
This is common web problem, as credit card apps and forum apps often say: "Clicking once is enough!".
I guess you could solve it by checking for the exact same data to see if the post is unique, but I wonder if there are other methods.
This ofcourse does not count for ASP.NET webforms, because POST doesn't matter as much.
While JavaScript solutions can disable the submit button after it has been clicked, this will have no effect on those people who have JavaScript disabled. You should always make things work correctly without JavaScript before adding it in, otherwise there's no point as users will still be able to bypass the checks by just disabling JavaScript.
If the page where the form appears is dynamically generated, you can add a hidden field which contains some sort of sequence number, a hash, or anything unique. Then you have some server-side validation that will check if a request with that unique value has already come in. When the user submits the form, the unique value is checked against a list of "used" values. If it exists in the list, it's a dupe request and can be discarded. If it doesn't exist, then add it to the list and process as normal. As long as you make sure the value is unique, this guarantees the same form cannot be submitted twice.
Of course, if the page the form is on is not dynamically generated, then you'll need to do it the hard way on the server-side to check that the same information has not already been submitted.
Most of the answers so far have been client-side. On the server-side, you can generate a hidden field with a GUID when you first produce the form, and then record that GUID as a submitted form when the post is received. Check it before doing any more processing.
Whenever a page is requested from the server , generate a unique requestToken , save it in server side,mark status as NOT Processed and pass it along with the current requested page. Now whenever a page submit happens , get the requestToken from the "POST"ed data and check the status and save the data or take alternate action.
Most of the banking applications use this technique to prevent double "POST"ing.So this is a time proven & reliable way of preventing double submissions.
A user-side solution is to disable the submission button via Javascript after the first click.
It has drawbacks, but I see it often used on e-commerce websites.
But, it won't never replace a real server-side validation.
Client side techniques are useful, but you may want to couple it with some server side techniques.
One way to do this is to include a unique token in the form (e.g. a GUID or similar), so that when you come to process the form you can check to see whether the token has already been used, preventing a double submission.
In your case, if you have a table with event visitors, you might include this token as a column.
A client-only solution won't be enough, as stated in many of the answers here. You need to go with a server-side fail-safe.
An often overlooked reason that disabling the submit button doesn't work is, the user can simply refresh the submit target (and click OK on the "are you sure you want to resubmit the POST data?" dialog). Or even, some browsers may implicitly reload the submitted page when you try to save the page to disk (for example, you're trying to save a hard-copy of an order confirmation).
Almost no one has js disabled.
Think about coding your e-commerce website for the 70 year old woman who double clicks every link and button.
All you want to do is add a javascript to prevent her clicking "Order Now" twice.
Yes - check this at the server side too "be defensive" - but don't code for that case. But for the sake of a better UI do it on the client side too.
Here are some scripts that I found:
//
// prevent double-click on submit
//
jQuery('input[type=submit]').click(function(){
if(jQuery.data(this, 'clicked')){
return false;
}
else{
jQuery.data(this, 'clicked', true);
return true;
}
});
and
// Find ALL <form> tags on your page
$('form').submit(function(){
// On submit disable its submit button
$('input[type=submit]', this).attr('disabled', 'disabled');
});
None of the solutions address a load-balance server.
If you have some load balancer, send a UUID (or any type of unique number) to the server to store and read again will not work well if the server is not aware of other servers, because each request could be processed by a different server in a stateless environment. These servers need to read/write to the same place.
If you have multiple servers you will need to have some shared cache (like a Redis) among the servers to read/write the unique value in the same place (what could be an over-engineering solution, but works).
Client side alteration is a common technique:
Disable submit button
Change the screen to a "please wait" screen
If the form was modal, changing the screen back to their usual process (this has the benefit of making things look really slick)
But it's not perfect. It all relies on JS being available and if that's not the case, without back-end duplication detection, you'll get duplicates still.
So my advice is to develop some sort of detection behind the scenes and then improve your form to stop people with JS being able to double-submit.
You can track the number of times the form's been submitted and compare it to the number of unique visits to the page with the form on it in the session.
Beside the many good techniques already mentioned, another simple server-side method, that has the drawback of requiring a session, is to have a session variable that is switched off on the first submit.

Resources