Rails - changing displayed URL from the controller - html

I have a page that has a form with one select list and a button.
<%= form_tag("/restaurant", method: "get") do %>
<%= collection_select(:company_name, nil, Food.distinct_company_names, :company_name, :company_name) %>
<%= submit_tag("Search One", :name => nil, :class=>"btn btn-search") %>
<% end %>
This works fine to pass the parameters to my restaurant page, but I don't want the URL to display like this-
http://localhost:3000/restaurant?utf8=%E2%9C%93&company_name%5B%5D=McDonalds
I want it to display like this (my routes are setup to allow it)-
http://localhost:3000/restaurant/McDonalds
Here's my current solution in the controller...
def restaurant
if params[:company_name]
#company_name = params[:company_name][0]
redirect_to("/restaurant/"+#company_name)
else
#avoid redirect loop
#company_name ||= params[:id]
end
end
I'm thinking there's an easier or more elegant way to do this, though. Can someone point me in the right direction?

You can use javascript to change the url when the select changes value. No need for the form.
<%= collection_select :company, :name, Food.distinct_company_names, :company_name, :company_name %>
<script>
$('#company_name').on('change', function() {
if ($(this).val().length) {
window.location = '/restaurant/' + $(this).val()
}
})
</script>

Instead of GET method you can use POST method to submit the form. Here you have to use restaurant controller and the action name is mcdonalds
<%= form_tag("/restaurant/mcdonalds", method: "post") do %>
<%= collection_select(:company_name, nil, Food.distinct_company_names, :company_name, :company_name) %>
<%= submit_tag("Search One", :name => nil, :class=>"btn btn-search") %>
in the routes.rb
post '/restaurant/mcdonalds'

Related

Can you forward params as an ActionController::Parameters object in a forms hidden_field?

Users apply filters in an index page and I need the filters' params to do some background jobs.
I can access the params after the filters are applied as you normally would, but when I try to forward the request.params to the controller action where I launch the background jobs, the params passed in the form's hidden_field no longer is an ActionController::Parameters object but a String instead, which I can't access as I would normally do by it's key.
form:
<%= form_tag admin_users_on_demand_mass_emails_path, method: :get do %>
<%= simple_fields_for :on_demand_email do |f| %>
<%= hidden_field_tag :filters, params[:q].to_unsafe_h %>
<%= f.input :email,
as: :radio_buttons,
collection: options_for_on_demand_emails %>
<% end %>
<%= submit_tag "Enviar emails", class: "btn btn-primary" %>
<% end %>
params before form send:
<ActionController::Parameters {"q"=><ActionController::Parameters {"fname_or_lname_or_email_or_tax_id_number_or_legal_name_cont"=>"", "extra_params"=><ActionController::Parameters {"invoice_status"=>""} permitted: false>, "subscription_payment_type_eq"=>"", "address_province_eq"=>"", "subscription_status_eq"=>"cancelled", "created_at_gteq"=>"", "created_at_lteq"=>""} permitted: false>, "subscription_status_eq"=>"{}", "commit"=>"Filter", "controller"=>"admin/users", "action"=>"index"} permitted: false>
becomes:
{"filters"=>
"{\"fname_or_lname_or_email_or_tax_id_number_or_legal_name_cont\"=>\"\", \"extra_params\"=>{\"invoice_status\"=>\"\"}, \"subscription_payment_type_eq\"=>\"\", \"address_province_eq\"=>\"\", \"subscription_status_eq\"=>\"cancelled\", \"created_at_gteq\"=>\"\", \"created_at_lteq\"=>\"\"}",
"on_demand_email"=>"[FILTERED]",
"commit"=>"Send emails"}
I've tried many different things such as passing request.params.to_unsafe_h (same result) and params.require(:q).permit(params[:q].keys) which results in an error in the view when :q is not present due to the require.
Is there any way to accomplish this or rebuild the params hash after it's been passed as a string?
You can use JSON.parse() to convert them into hash like below,
{"filters"=>
JSON.parse("{\"fname_or_lname_or_email_or_tax_id_number_or_legal_name_cont\"=>\"\", \"extra_params\"=>{\"invoice_status\"=>\"\"}, \"subscription_payment_type_eq\"=>\"\", \"address_province_eq\"=>\"\", \"subscription_status_eq\"=>\"cancelled\", \"created_at_gteq\"=>\"\", \"created_at_lteq\"=>\"\"}"),
"on_demand_email"=>"[FILTERED]",
"commit"=>"Send emails"}
In the end I did params.permit(q: params.dig(:q)&.keys

Multiple radio buttons getting selected in Rails app

I have an app where Question model has_many relationship with Option. I also have a button to add options while creating a question. Every question has only one correct answer. So when I create a question and click on Add Option button, new option is created but the new radio button associated with it has different name. In fact the name of radio button is of the form question[options_attributes][i][is_answer] where i is id. As far as I know radio buttons should have the same name to work as a collection or group. So how can I make it work as a group even if I create any number of options for a single question?
html.erb
<%= form_for #question do |form| %>
<div class="field">
<%= form.label :body %>
<%= form.text_area :body %>
</div>
<%= form.fields_for :options, question.options.each do |a| %>
<div class="field">
<%= a.label :options %>
<%= a.text_area :body %>
<%= a.radio_button :is_answer, "options" %>
<%= a.check_box :_destroy %>
<%= a.label :_destroy, 'delete' %>
</div>
<% end %>
<%= form.submit 'Add option', :name => "add_option" %>
<%= form.submit 'Delete options', :name => "remove_option" %>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
controller.rb
class QuestionsController < ApplicationController
def new
#question = Question.new
#question.options.build
end
def create
#question = Question.new(question_params)
#question.user = current_user
if params[:add_option]
#question.options.build
else
respond_to do |format|
if #question.save
format.html { redirect_to #question, notice: 'Question was successfully created.' and return }
format.json { render :show, status: :created, location: #question }
else
format.html { render :new }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
render :action => 'new'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_question
#question = Question.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def question_params
params.require(:question).permit(:body, options_attributes: [:id, :body, :question_id, :created_at, :updated_at, :is_answer])
end
end
There are two options:
Using JavaScript on the client-side to uncheck the radio buttons.
Using radio buttons with the same name. It this case you will have to change the way you pass the :is_answer parameter and manually assign the value in options_attributes.
Method 1 details:
See this question radio different names - only check one
Method 2 details:
Instead of passing :is_answer parameter for each option you can pass a single parameter for the question having chosen answer id as the value. Lets name it "answer_id". We want this parameter to be in the params[question]
hash in the controller, so the whole name will be "question[answer_id]". Although radio buttons are generated for each option, only the chosen one will be sent to the server as they all have the same name.
<%= form.fields_for :options, question.options.each do |a| %>
<div class="field">
<%= a.label :options %>
<%= a.text_area :body %>
<%= radio_button_tag "question[answer_id]", a.object.id, a.object.is_answer? %>
<%= a.check_box :_destroy %>
<%= a.label :_destroy, 'delete' %>
</div>
<% end %>
https://apidock.com/rails/v4.2.7/ActionView/Helpers/FormTagHelper/radio_button_tag
In the controller you will have to manually assign the option's is_answer parameter based on the answer_id value.
def question_params
result = params.require(:question).permit(:body, :answer_id, options_attributes: [:id, :body, :question_id])
answer_id = result.delete(:answer_id)
result[:options_attributes].values.each do |option_attrs|
option_attrs[:is_answer] = option_attrs[:question_id] == answer_id
end
result
end
If you need further details please let me know. I will update the answer to give more information.

Getting a form get parameter

I am having trouble reading the business_id parameter from a form get request.
http://localhost:3000/clients?utf8=%E2%9C%93&client%5Bbusiness_id%5D=toyota&commit=Save+Client
With: params[:business_id]
Why doesn't that work?
Details:
html form:
<%= form_for :client, url:clients_path, method: :get do |f| %>
<%= f.label :business_id %><br>
<%= f.text_field :business_id %>
<%= f.submit%>
<% end %>
The form hits the create controller and redirects back to the original page:
def create
#clients = Client.all
redirect_to controller: 'clients'
end
On the original page reads the url string in the index controller with:
def index
#clients = Client.all
#filters = params[:business_id]
end
#filters comes back as blank unless I hard code a value.
You need to use params[:client][:business_id] as business_id is inside client hash.

Controller and action within link_to image_tag don't execute controller code

My link_to looks like this:
<%= link_to image_tag(user_likes_selection.page_picture, :image_id =>
user_likes_selection.id, :controller => :preferences_controller,
:action => :checked_average_with_profile) %>
My controller, preferences_controller, has a method called checked_average_with_profile, which, as far as I can tell, is not being called when I click the image.
The html code that is generated from the link_to is
<img>
<a href="/preferences"><img action="checked_average_with_profile" alt="Soul_surfer_film"
controller="preferences_controller" height="70%" image_id="3254"
src="/assets/soul_surfer_film.jpg" width="70%" /></a>
</img>
Why isn't the controller code executed when the image is clicked?
in cases like these, it's easier to read the code if you use the block form of link_to
<%= link_to { :image_id => user_likes_selection.id, :controller => :preferences, :action => :checked_average_with_profile } do %>
<%= image_tag(user_likes_selection.page_picture %>
<% end %>
in your routes, you can also pass an as option so you can use a named route. assuming your routes looks like
match '/preferences/checked_average_with_profile/:image_id' => 'preferences#checked_average_with_profile', as: :check_average_profile
you can simplify your link using
link_to image_tag(user_likes_selection.page_picture), check_average_profile_path(user_likes_selection.id)
Here is how i do in my code.
<%=link_to(image_tag(user_likes_selection.page_picture), check_average_profile_path(user_likes_selection.id)) %>
Try:
<%= link_to image_tag(user_likes_selection.page_picture), url_for({:controller => 'preferences_controller', :action => 'checked_average_with_profile', :image_id => user_likes_selection.id}) %>
Put your paren after user_likes_selection.id, not at the end. You're mixing image tag properties with your link_to properties.
Try:
<%= link_to image_tag(user_likes_selection.page_picture, :image_id =>
user_likes_selection.id), {:controller => :preferences,
:action => :checked_average_with_profile} %>
Finally solved my problem by adding a collection with my action in resources:
resources :preferences do
collection do
get 'save_new_scores_to_profile'
get 'checked_average_with_profile'
end
end
Then, I modified my view code so that I could pass the image_id variable along to the controller.
<%= link_to image_tag(user_likes_selection.page_picture,
checked_average_with_profile_preferences_path(:image_id =>
user_likes_selection.id) %>
In my controller, I made sure to grab the image_id with params and put a redirect_to at the end:
def checked_average_with_profile
params[:image_id]
redirect_to preferences_url
end
If you have this problem, the key parts are passing the id (whatever that may be) within parenthesis of the controller path you specify and using a COLLECTION instead of a MEMBER in your routing file.

Placing link at top

How do I place a link at the top of my page when the URL that it is pointing to is not determined until later down the page. In this example, I want to move Create and Edit Scenario links to the top of the page, but as you can see Edit Scenario depends on knowing the #scenario_id first.
<%= will_paginate #scens, :next_label => 'Older', :prev_label => 'Newer' %>
<div class="box">
<% for scenario in #scens %>
<% #created = scenario.created_at %>
<% #updated = scenario.updated_at %>
<% #scenario_id = scenario.id %>
<% if scenario.scenario_image.exists? %>
<%= scenario_image_tag(scenario) %>
<% end %>
<%= simple_format(scenario.description) %>
<% end %>
</div>
<% if session[:role_kind] == "controller" %>
<p>
<%= button_to "Create new scenario", :action => "create" %>
<% if #scens.size > 0 %>
<%= button_to "Edit scenario", :action => "edit", :id => #scenario_id %>
<% end %>
</p>
You can add the link at the top but you will need to programmatically access it later and then assign the URL to it. That needs some kind of reference or look-up capability, I'm thinking client-side javascript but that's as I don't know Ruby.
Alternatively you could create the link later when you have the URL and place the link at the top using CSS positioning. The actual position of all the DOM elements on the page need not match the order in which they are rendered.
One way to do this is to use a helper:
In your helper.rb file:
def stack_example(scens, &block)
html = 'Scenario Details'
edit_link = 'Edit Link'
yield html, edit_link
end
Then in your partial you could have something like:
<% stack_example(#scens) do |html, edit_link| %>
<%= edit_link %><br>
<%= html %>
<% end %>
Should output the following:
Edit Link
Scenario Details
I don't get it. Why do you create model in the view layer? Why wouldn't you create the model variables in the controller? Sth like:
class your_controller
def your_method
#scenario_id = ...
end
end
I think that your problem lays in the invalid MVC usage. Don't you think that all the #member #variables should be initialized before the view starts to render?