Rails parameters contain backslashes - ruby-on-rails

I am currently working with button_tag to create a remote styled answer submission quiz. When pressing this button, instead of posting the new record, it is throwing an error.
ActiveRecord::RecordNotFound (Couldn't find Answer without an ID):
When looking at the server logs I see it is trying to work with these params when trying to post Parameters: {"{\"answer_id\":59}"=>nil, "id"=>"15"}
What I am looking, or expecting to see is this.
Parameters: {"answer_id"=>"59", "id"=>"15"}
Here is the button_tag I am using.
<% #question.answers.each do |answer| %>
<%= button_tag "#{answer.answer.titleize}", class: 'btn btn-block btn-lg btn-primary', data: {
remote: true,
method: :post,
url: answer_question_path(#question),
params: { answer_id: answer.id }
} %>
<% end %>
Here is my response controller which is responsible for submitting the POST request.
class ResponsesController < ApplicationController
def answer
question = Question.find(params[:id])
answer = question.answers.find(params[:answer_id])
response = question.responses.find_or_initialize_by(user: current_user)
if response.update(answer: answer)
head :ok
else
puts 'Something went wrong chief'
end
end
private
def responses_params
params.require(:response).permit(:user_id, :question_id, :answer_id)
end
end
I have tried using to_json on the parameter with no success and have not been able to find any solution elsewhere on SO or other forums. Any ideas?

This seems to be an issue with button_tag in the feature I am using it for.
button_tag creates a button element that defines a submit button, resetbutton or a generic button which can be used in JavaScript. button_tag is also an action view helper but is defined as a FormTagHelper.
button_to generates a form containing a single button that submits to the URL created by the set of options. button_to is a UrlHelper while button_tag is a ViewHelper.
Below is the button_tag code I was using which was creating the issue described above. Using button_tag fixed my parameters issue and also looks a bit cleaner. I hope this helps anybody else having issues with button_tag in the future.
<%= button_tag "#{answer.answer.titleize}", class: 'btn btn-block btn-lg btn-primary', data: {
remote: true,
method: :post,
url: answer_question_path(#question),
params: { answer_id: answer.id }
} %>
<%= button_to "#{answer.answer.titleize}",
answer_question_path(#question),
class: 'btn btn-block btn-lg btn-primary',
params: { answer_id: answer.id },
remote: true %>

Related

Rails 'remote:true' ignored when params provided

In a Rails (5.2.4.2) app
I am using button_to + remote: true, and this is working as expected -> ajax call fired, all is OK.
But then I add 'params' to the button_to. Params are added to the form as hidden input (as expected) but when I click on the button, the request made is not remote, and all the page content is updated.
Problematic: button_to + remote + params -> this code seems to ignore the remote:true, although I see data-remote="true" in the form tag
<%= button_to ent_path(ent),
{remote: true,
method: :patch,
class:"btn btn-primary",
params: {ent: {active_a: false}}
} do
%>
<span>TEEEST</span>
<% end %>
Working as expected:
<%= button_to ent_path(ent),
{remote: true,
method: :patch,
class:"btn btn-primary",
} do
%>
<span>TEEEST</span>
<% end %>
So my purpose is to have the button_to remote and update the ent record, changing the active_a attribute depending on some logic
You want to pass any extra parameters via the link path, like this
<%= button_to ent_path(ent, active_a: false),
{remote: true,
method: :patch,
class:"btn btn-primary",
} do
%>
<span>TEEEST</span>
<% end %>
Turned out the remote:true was working properly. But there was a redirect_back in the controller that was executed in case of some custom logic, so that appeared as if the button is working without remote:true

How to make Rails 5.2 form submit remotely?

In my view, using Slim:
= form_with url: update_pn_price_path(#price)
.form-group
= label_tag :part_number
= text_field_tag :part_number, #price.part_number, required: true
.form-group
=> submit_tag t(:save), class: 'btn btn-primary btn-sm'
= link_to 'Cancel', '', class: 'btn btn-link btn-sm', data: { cancel_in_place: true }
This always submits using a standard submit, not remotely. Log shows:
Processing by PricesController#update_pn as HTML
What am I doing wrong? Why won't it submit using JS?
I'm surprised to see that your view compiles. There are a few problems that I can spot.
You start indenting content as though it would be in the form. However you didn't open a form tag or provided a block to the form_with method.
= form_with url: update_pn_price_path(#price)
.form-group
Should at least be changed to:
= form_with url: update_pn_price_path(#price) do
.form-group
Since a variable is provided to a block, the preferred way is to capture it in a variable (form in the code below). If you're not planning to use it you should still capture it in an underscored variable to indicate that it is there, but not being used (_form).
= form_with url: update_pn_price_path(#price) do |form|
.form-group
Since you're using a form builder, the preferred way of creating elements is through the form builder interface. Since you're not creating the submit tag through the form builder this might be another reason why the submit isn't done remotely.
I would at least replace:
=> submit_tag t(:save), class: 'btn btn-primary btn-sm'
With:
= form.submit t('save'), class: 'btn btn-primary btn-sm'
But preferably update the whole form to use the form builder. The result should look something like this:
= form_with model: #price, url: update_pn_price_path(#price) do |form|
.form-group
= form.label :part_number
= form.text_field :part_number, required: true
.form-group
= form.submit t('save'), class: 'btn btn-primary btn-sm'
= link_to 'Cancel', '', class: 'btn btn-link btn-sm', data: {cancel_in_place: true}
For more/other form builder methods see the FormBuilder and FormHelper documentation.
Here's what solved the issue. Rookie mistake, but posting here in case someone else runs into it.
My form shown above was inside another form. You can submit such an 'inner' form, but not remotely. The form was being inserted via an AJAX call, so I changed that to insert to the end of the HTML body, and then positioned using CSS. That put it outside the containing form and made things work as expected.

Creating a PUT request in Ruby on Rails?

I want to be able to use a PUT request to edit the title of a song I uploaded in my Ruby on Rails application.
def update
#sound_byte = SoundByte.find(params[:id]) #Error here
#sound_byte.update!(sound_byte_params)
flash[:success] = "The soundbyte title was changed."
redirect_to sound_byte_path
end
private
def sound_byte_params
params.require(:sound_byte).permit(:mpeg, :mpeg_file_name)
end
I end up getting an error like this:
Couldn't find SoundByte with 'id'=song_name
Any ideas of how to fix this issue? I am using the Paperclip gem to enable the audio/mpeg file uploads.
EDIT: Here is my views code
<%= link_to "Edit", sound_byte_path(sound_byte.mpeg_file_name), class: "btn btn-primary btn-lg btn-xlarge", :method => :put %>
In the view page, you pass string sound_byte.mpeg_file_name as params, but in your controller, you use id #sound_byte = SoundByte.find(params[:id]).
Try this
<%= link_to "Edit", sound_byte_path(sound_byte.id), class: "btn btn-primary btn-lg btn-xlarge", :method => :put %>

How can I use multiple HTTP methods in simple_form?

I have a view like this:
= simple_form_for #fixed_number, url: polymorphic_path([:manage, #numberable, :fixed_number]), method: :put do |f|
= f.input :number
= f.submit 'Ok', class: "btn btn-success"
= f.button :button, "Delete", class: "btn btn-danger", method: :delete
Obviously I want that Delete button to send the DELETE method. It doesn't work - it uses the PUT specified for the whole form. Can I have one form that changes method for a particular button?
I found a solution. I don't know if it's the best one. In particular it relies on UJS and CSS. I replaced the delete button with a styled link:
= link_to "Delete", f.options[:url], method: :delete, class: "btn btn-danger"

Conditionally add a class to link_to in Rails

I want to conditionally add the class "hidden" to a Rails link tag, depending on if "accepted == true".
If I weren't using a rails link_to I could do <a href="#" class="foo bar <%= "hidden" if accepted == true %>" >. How can I accomplish this inside a link_to?
<%= link_to "Accept Friend Request",
"#",
class: "btn btn-success btn-sm btn-block requestSent ???hidden???",
disabled: true %>
If you use interpolation with #{}, anything you put between it is run as plain old Ruby code.
In this example you could add a conditional class in the string like this:
<%= link_to "Accept Friend Request",
"#",
class: "btn btn-success btn-sm btn-block requestSent #{'hidden' if accepted}",
disabled: true %>
Just note that you should use single quotes around the class name 'hidden'.
Also note that when a variable represents a boolean value (true or false), you don't need to explicitly say if accepted == true. You can simply say if accepted.
You can do it outside the link_to:
<% css_class = accepted ? "hidden" : "" %>
<%= link_to "Accept Friend Request",
"#",
class: "btn btn-success btn-sm btn-block requestSent #{css_class}",
disabled: true %>
You can use a helper to build up the link as well:
def accept_friend_request_link
classes = [:btn, :and_friends]
if accepted
classes << :hidden
end
link_to 'Accept Friend Request', '#', class: classes, disabled: true
end
I posted a similar answer to this question.
A cleaner solution
The standard approach requires putting logic into the views and using string interpolation or moving things into a separate helper.
Here's an updated approach that avoids any of that:
<%= link_to "Accept Friend Request",
"#",
class: class_string("btn btn-success btn-sm ban-block requestSent" => true, hidden: accepted),
disabled: true %>
class_string method
The class_string helper takes a hash with key/value pairs consisting of CSS class name strings and boolean values. The result of the method is a string of classes where the boolean value evaluated to true.
Sample Usage
class_names("foo bar" => true, baz: false, buzz: some_truthy_variable)
# => "foo bar baz"
Inspired by React
This technique is inspired by an add-on called classNames (formerly known as classSet) from Facebook’s React front-end framework.
Using in your Rails projects
As of now, the class_names function does not exist in Rails, but this article shows you how to add or implement it into your projects.
I've override the link_to to expect a class_if parameter, check it out:
def link_to(options = {}, html_options = {})
if html_options.is_a?(Hash) && html_options.key?(:class_if)
html_options[:class] <<
(" #{html_options[:class_if][1].strip}") if html_options[:class_if][0]
html_options.delete :class_if
end
super(options, html_options)
end
And usage:
<%= link_to(my_path, class: "class1", class_if: [true_or_false?, 'class2']) %>
I've just override one method, but a good refactor is to override all of described signatures here: http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
UPDATE
Another solution is:
module ApplicationHelper
def active_link_to(name = nil, options = nil, html_options = nil, &block)
html_options ||= { class: '' }
html_options[:class].concat('is-active') if options.match(controller_name)
link_to(name, options, html_options, &block)
end
end
And usage:
<%= active_link_to(my_path, 'My path') %>
This way, I got an "active status" even when is a custom route like: "my_path/help", "my_path/two".

Resources