disable button if user has previously performed that action - ruby-on-rails

Tried to figure this out for a while without being able to crack the nut.
What I am trying to do is this.
I have a user model, it has_many answers. I would like to restrict the ability for one user to answer the same answer more than once. I've been able to block this in the model, however not in the view.
I would like the submit button to see if the current_user.id is present in the current answer id (the one it's thinking about filling out), if so disable the submit button saying "already applied".
My answers#new
<%= form_for #answer do |f| %>
<%= f.hidden_field :application_id, value: #application.id %>
<% if #application.question_2.length && #application.question_3.length >= 1 %>
<p>Question 1: <%= #application.question_1 %></p>
<%= f.text_area :answer_1 %><br/>
<p>Question 2: <%= #application.question_2 %></p>
<%= f.text_area :answer_2 %><br/>
<p>Question 3: <%= #application.question_3 %></p>
<%= f.text_area :answer_3 %>
<% elsif #application.question_2.length >= 1 %>
<p>Question 1: <%= #application.question_1 %></p>
<%= f.text_area :answer_1 %><br/>
<p>Question 2: <%= #application.question_2 %></p>
<%= f.text_area :answer_2 %><br/>
<% else %>
<p>Question 1: <%= #application.question_1 %></p>
<%= f.text_area :answer_1 %><br/>
<% end %>
<%= f.submit "Submit answers", data: { disable_with: "Please wait..." } %>
<% end %>

I see a couple different pieces of this. When I write similar functionality, I think about the following questions:
What should happen on page load / reload?
Should anything happen without page reload (ie. via AJAX / JS)?
Should the action be disabled server-side, client-side, or both?
First of all, if the user has already submitted an answer by the time the page loads, the template should display the button differently. Usually it makes sense to not display a form at all in that case, but if you want the form with the button disabled, the following code comes to mind (see html example):
<% if #user_has_answered %>
<%= f.submit "Submit answers", disabled: true %>
<% else %>
<%= ... normal button ... %>
<% end %>
If you're concerned about preventing the user from clicking the button twice when they submit the form from the page, some simple Jquery can easily handle that, or you can use disable_with as in your example twice. This has nothing to do with the state of the database; it merely sets up a Javascript listener that disables the button as soon as it's clicked (and changes its text) to prevent the user from accidentally double-submitting when pages load slowly.
You mentioned that you have prevented double answers in the model. How does the controller handle this? If a user somehow manages to fill out the form a second time and submit it (perhaps Javascript is disabled and they had multiple tabs open on the same page? If it's possible, users do it), what do you want to happen to the data they've submitted? Show them a rejection message? Preserve their data on the same page? This functionality should be 80% automatic in Rails, if you want it, as long as you handle the request and rejection in the same way that form validation errors are handled.
I'm happy to be more specific if you share more detail about what you're looking for or having trouble with.

Assuming that Answer belongs_to Application, you could do...
<% if current_user.answers.collect(&:application).include?(#application) %>
<%= f.submit "already applied", disabled: true %>
<% else %>
<%= f.submit "Submit answers", data: { disable_with: "Please wait..." } %>
<% end %>

Related

Rails require radio button from a collection

I need a way for my form to not be sent if the user didn't bother to select any radio buttons.
I'd like to to that within the view and the controller, not in the model (the data shouldn't even be sent)
<%= form_tag("/bookings/new", method: "get") do %>
<% #flights.each do |flight| %>
<%= radio_button_tag :flight_id, flight.id %>
<% end %>
<%= submit_tag "book now" %>
<% end %>
edit, to clarify
normally I'd do
<%= f.text_field :name, required: true %>
but, as I have many radio buttons and I only need one for the form to work, I don't know how to implement it
You can set validation in the model to see the presence of checkbox if javascript is disabled. This is a more robust method.
validates :flight_id, :acceptance => true
Docs here - http://guides.rubyonrails.org/active_record_validations.html#acceptance
Edit
function validateCheckBox() {
var x = document.getElementById("flight_id").checked;
if(!x) {alert("Not checked")}
}
<%= submit_tag "book now" , :onclick => "validateCheckBox();" %>
<%= f.text_field :name, required: true %>
This still works perfectly for radio buttons, and it's okay if it ends up on all radio items. The form will still only require one input.
I just tested it on my Rails 6 app.

Create object,not to save before the end of the filling

Data collection for filling the order is going to table with 3 pages (go to each at the touch of a button). Stored in the database they need only after you press the last button. How to store data from the first to the last page?
1 page
`<%= form_for(#orders) do |f| %>
<%= f.text_field :city,placeholder: "Город"%>
<%= f.submit "Далее", class: "btn" %>
<% end %>`
2 page
`<%= form_for(#orders) do |f| %>
<%= f.text_field :time, placeholder: "Время" %>
<%= f.submit "Далее", class: "btn" %>
<% end %>`
3 page
`<%= form_for(#orders) do |f| %>
<%= f.text_field :count, placeholder: "Количество" %>
<%= f.text_field :phone,placeholder: "Телефон для связи"%>
<%= f.submit "Заказать", class: "btn" %>
<% end %>`
While you could use the session or some other alternative storage mechanism actually saving the order in the database in the first step and performing the following steps as atomic update operations might be a much better solution.
The advantages are less complex logic and being able to avoid tricky chicken vs egg situations like for example how to route to a resource that does not have an id.
class Order < ActiveRecord::Base
enum status: [:draft, :confirmed, :processed, :shipped] # etc...
end
In this example you would use a bitmask column on the database - it should be set to zero by default an in the last step you would change the state of the order from draft to confirmed.
There are several gems such as wicked that are used to create wizards (step by step forms).
Yet another alternative is to use javascript to split a complex form into steps in the form of tabs or "slides" while the backend still processes it as a single operation.

How to send one or the other attribute in the form

I have a form for which I am using checkboxes (Not using radio buttons for my purpose). The problem I run into is when I submit a form, I get an error saying params is missing or value is empty:checkup. I am trying to use hidden filed but get the same error. HOw to have an option of sending only one if selected?
def checkup_params
params.require(:checkup).permit(:eye, :heart)
end
my form:
<%= form_for(#checkup) do |f| %>
<%= hidden_field_tag "checkup[eye]", nil %>
<%= check_box_tag :eye, "eye" %>
<%= hidden_field_tag "checkup[heart]", nil %>
<%= check_box_tag :heart, "heart" %>
<%= f.submit %>
<% end %>
I suggest reading this guide http://guides.rubyonrails.org/form_helpers.html#dealing-with-model-objects
Most specifically section 2.2. You really shouldn't be using tag helpers here.

Edit a post without leaving the page

I am trying to edit a micropost on the page without leaving the physical page.
In my app i have a page the renders all of a user's microposts using the partial below:
microposts/_micropost:
<%= render 'shared/edit_micropost', object: micropost %>
<%= micropost.title %>
<%= micropost.content %>
<%= micropost.url %>
<%= raw "Tags: #{micropost.tag_list.map {|t| link_to t.capitalize, tag_path(t)}}" %>
shared/edit_micropost:
<%= link_to "edit", object, remote: true %>
<%= form_for object do |object| %>
<%= object.text_field :title %>
<%= object.text_area :content %>
<%= object.text_field :url %>
<%= object.text_field :tag_list %>
<%= object.submit "Update", class: "btn btn-mini" %>
<% end %>
When I click "edit" I would like the form to come up so that the title, content, url, and tag_list of the specific micropost is editable.
Right now when I click "edit" I get No route matches [GET] "/microposts/452" I'm not sure how to specify a working path in my link_to. I assume I have to move the form_for to a JS file?
I'm new to programming and would really appreciate some help, thanks.
There is a gem maybe you want to try it, 'Best in Place' is a jQuery based AJAX Inplace-Editor
Also there is a screencast for it by Ryan Bates
A straightforward way to do this would be to go ahead and render the form on the page, but hide it with javascript. Then when the user clicks the button, show it again. This way the form is also available to users that don't have js enabled.
Then it's just a matter of setting the remote: true option in the form to get it to submit via ajax and use an ajax callback to notify the user of success or failure (if you want). Again, this approach will still allow non-js users to submit the form with a normal request, while users with js enabled will get the slick ajax functionality.

Rails Save Info if checkbox is checked

How can I make it so that my info only gets stored if the checkbox is checked
<% #extra.each do |extra| %>
<%= f.fields_for :purchaseds do |builder| %>
<div class="label-field">
<%= builder.label :name, extra.name %>
<p><%= extra.description %></p>
</div>
<div class="text-field">
$<%= extra.price %>
<%= builder.check_box :purchased %>
</div>
#I WOULD LIKE THIS TO ONLY GET SAVED IF THE CHECK BOX FOR PURCHASED IS CHECKED
<%= builder.hidden_field :name, :value => extra.name %>
<%= builder.hidden_field :description, :value => extra.description %>
<%= builder.hidden_field :price, :value => extra.price %>
<% end %>
<% end %>
My client asked to be able to add extra services himself, and then users could be able to choose if they want to purchase them as accessories to their order. So what I did was I made a table called Extra (for extra services) and another table called Purchased. Purchased belongs to Order and is a nested attribute.
Well two ways. I will give you just the high level of it but if you would like me to expand, please let me know!
Option 1
Add a submit button to the form builder and have the checkbox data sent to the controller as well. There, check to see if it's set to true. If so, save the data. If not, don't save it.
Option 2
Add a jQuery function that, when the checkbox is set to true, it fires off an AJAX request to the controller to save the data.
It depends on how you want the user to be able to use this form.

Resources