Merge link and select params - ruby-on-rails

In my case i have link with params and select with params
If I filter #posts with select (:per_page) and then with (:pub) params everything is ok, because I used params.merge in link.
But now if I want to use first link and then select, it does not work, because I don't know where I should write params.merge in my select.
Code from my controller:
def index
#posts = Post
#posts = #posts.published unless params[:pub]
#posts = #posts.where(:published => params[:pub]) if params[:pub]
#posts = #posts.page(params[:page]).per(params[:per_page] || 5)
end
code from view
<%= select_tag :per_page, options_for_select(%w(1 2 3), params[:per_page].to_i), :onchange => "if(this.value){window.location='?per_page='+this.value;}" %>
<%= link_to "unpubl", params.merge(:pub => :f) %>
<%= link_to "publ", params.merge(:pub => :t) %>

If I understood correctly you are trying to create some kind of HTML filters to pass to your controller right ?
If this is the case, you should build your HTML filters using a form. This way you would not have to deal with "merging" your options into the params hash. The form will take care of that for you.
Example:
<%= form_tag posts_path, method: :get %>
<%= select_tag :per_page, options_for_select(%w(1 2 3), params[:per_page].to_i) %>
<%= check_box_tag 'published', 'true' %>
<%= submit_tag 'Submit' %>
<% end %>
Then in your controller:
params[:published]
params[:per_page]
will hold the corresponding values.
More information about form_tag here

Related

Rails 5: Two search form_tags on the same page

I'm trying to implement a two search form_tag on a the same page, each search form is placed inside dynamic bootstrap tabs. The first one which is working is basic a search form with one field. The second one which is not working has two fields, one is the same search method as the first and the other I'm trying to get the address from the other_location field and via params[:other_location].
With the current setup the other_location field form the second form does not appear!
Both of the forms are inside partials and I am rendering them inside two dynamic bootstrap tabs like this:
<%= render 'pages/search' %>
<%= render 'pages/search_other' %>
<%= form_tag search_items_path, :method => "get" do %>
<%= text_field_tag :search, params[:search], autofocus: true,
class: "search-query search_size",
placeholder: "Enter product to search" %>
<%= submit_tag "Search", name: nil, :style => "display: none;" %>
<%end%>
<%= form_for :search_other_path, :method => "get" do |form| %>
<%= form.text_field :search, autofocus: true,
class: "search-query search_size",
placeholder: "Enter keyword to search" %>
<% form.fields_for :other_location_path, :method => "get" do |f| %>
<%= f.text_field :other_location, class: "search-query search_size",
placeholder: "Enter address to search" %>
<%= form.submit "Search", name: nil, :style => "display: none;" %>
<%end%>
<%end%>
model
def self.search(search)
return where("0=1") if search !~ /\w{4}/
where("lower(title) LIKE lower(:term)", term: "%#{search}%")
end
routes.rb
get 'search' => 'pages#search', as: 'search_posts'
get 'search' => 'pages#search_other', as: 'search_other'
get 'search' => 'pages#other_location', as: 'other_location'
controller:
def search_other
if params[:search]
#posts = Post.near(other_location,10).search(params[:search]).page(params[:page])
else
#posts = []
end
end
def other_location
other_location = params[:other_location]
if params[:other_location]
Geocoder.search(params[:other_location])
end
end
def search
if params[:search]
#posts = Post.near(action,10).search(params[:search]).page(params[:page])
else
#posts = []
end
end
On your route file:
get 'search/other' => 'pages#search_other', as: 'search_other'
get 'search' => 'pages#search_other', as: 'search_other_items'
both GET requests are going to your pages_controller.rb #search_other method. So even if you have the two form_tags sending the data to different paths (search_other_path, and search_other_items_path) it would be going to the same controler method - which is redundant.
On your actual HTML you have two form tags:
<%= form_tag search_items_path, :method => "get" do %>
and
<%= form_tag search_other_items_path, :method => "get" do %>
You have not mentioned search_items_path in your routes, so I have no idea where that's pointing to. Likely its a proper controller that works since you mentioned the first form was the only one working.
Now, your mentioned controller only has a search method. So to start you are looking at the wrong controller. You should be looking at the controller methods being referenced by the form's action.
In this case, the second form is sending it's request to search_other_items_path which according to your routes, its pointing to pages_controller.rb -> #search_other method.
You should edit your question to include code that is actually relevant. Maybe then I can actually help.

How to do the calculation without any models in Rails?

I need to get an integer(#integer) from the form in my root_path, do multiplication (#integer*45) and display the result on the same page. How can I do it without any models in my application?
Please, share your best practice. Thank you!
I was trying to do next:
CalculatorsController
def calculation
#integer = params[:integer]
#result = #integer*45
end
def result
end
root.rb
root :to => 'calculators#result'
resources :calculators, :collection=>{:result => :get, :calculation => :post}
calculators/result.html.erb
<% form_tag root_path, :html => {:method => :post} do %>
<%= label_tag 'integer' %>
<%= text_field_tag :integer %>
<div><%= submit_tag 'OK' %></div>
<% end %>
I'll do it with ajax, so there is no need for page refresh:
First, update the routes, for your example you only need two routes, one get (or root) and one post.
routes.rb:
Rails.application.routes.draw do
root 'calculators#result'
post 'calculators/calculation'
end
Next, update your view:
Change the url in your form_tag where the data will be sent (to calculation action instead of result).
Add remote: true option to enable ajax.
Add a tag where you will display your result.
result.html.erb:
<% form_tag calculators_calculation_url, remote: true do %>
<%= label_tag 'integer' %>
<%= text_field_tag :integer %>
<div><%= submit_tag 'OK' %></div>
<% end %>
<div id="total"></div>
And create a view for calculation action, but since you are using ajax, you will create it as js.erb and include the required javascript (or jQuery) to update your view (i'm using jQuery in the example).
calculation.js.erb:
$('#total').html('<%= #result %>')
Now when you click submit, your form will be sent to calculation action and will update the div with #result.
Just add the field to your form...
<% form_tag root_path, :html => {:method => :post} do %>
<%= label_tag 'integer' %>
<%= text_field_tag(:integer, #integer) %>
<% if #result.present? %>
<br>
Result is: <%= #result %>
<br/>
<% end %>
<div><%= submit_tag 'OK' %></div>
<% end %>
And then render result in your calculate...
def calculation
#integer = params[:integer].to_i
#result = #integer*45
render :result
end
Your result view (result.html.erb) is getting its data from the result method, not calculation. Update your controller as follows:
def calculation
#integer = params[:integer]
end
def result
#result = #integer*45
end
You then need a tag to display your result in the view, something like:
<p> <%= #result %> </p>

How do I pass params through an AJAX form to create.js while retaining the params?

I have some comments that have a vote score, and I filter them by recent and popular using a toggle. When I click popular, it passes sort: popular as params, and resorts the comment list based on that. Then when a user posts another comment using AJAX, i'm trying to keep the comments sorted by popular (instead of reverting to the default recent) by passing the sort params through the comment form to the controller, and on to create.js, where it sorts the comments based on the params pass in. This works once, but as soon as I post 2 comments, it reverts back to sorting by recent, because i'm not able to continue passing the sort parms.
My sort toggle:
<% if params[:sort] == 'popular' %>
sorted by <%= link_to("Recent", video_path(video), remote: true, class: 'gray-link') %> |
<%= link_to("POPULAR", video_path(video, sort: 'popular'), remote: true, class: 'gray-link') %>
<% else %>
sorted by <%= link_to("RECENT", video_path(video), remote: true, class: 'gray-link') %> |
<%= link_to("Popular", video_path(video, sort: 'popular'), remote: true, class: 'gray-link') %>
<% end %>
The comments form, where I pass my params as a hidden field called :sort - note, they are nested comments, hence the form_for syntax, but it should be irrelevant here.
<%= form_for [#video, #comment, , :html => {:class => 'form_height'}], :remote => true, method: :post, url: video_comments_path(#video.id) do |f| %>
<div id="comment-form-errors">
<%= render :partial => "/videos/comment_form_errors" %>
</div>
<%= f.hidden_field :parent_id, :value => parent_id %>
<%= f.hidden_field :sort, value: params[:sort] %>
<%= f.text_area :post, placeholder: 'Comment', id: 'comment-box' %>
<% if parent_id != nil %>
<%= f.submit "Reply" %>
<% else %>
<%= f.submit %>
<% end %>
<% end %>
Then in the controller I create a variable #sort to collect the sort params from the comment form:
def create
#comment = #video.comments.build(comment_params)
#comment.user = current_user
#sort = params[:comment][:sort]
respond_to do |format|
if #comment.save
format.html { redirect_to video_path(#video.id), notice: "You said something. Let's hope it didn't suck." }
format.js { }
else
format.html { render 'videos/show', alert: "There was an error." }
format.js {}
end
end
end
Then I order the comments based on whether the popular sort params was passed:
<% if #comment.errors.present? %>
$('#comment-form-errors').html("<%= escape_javascript(render(:partial => '/videos/comment_form_errors')) %>");
<% else %>
<% if #sort == 'popular' %>
$('#comment-list').html("<%= j nested_comments (#video.comments).arrange(:order => 'cached_weighted_score DESC') %>");
<% else %>
$('#comment-list').html("<%= j nested_comments (#video.comments).arrange(:order => 'created_at DESC') %>");
<% end %>
$('#review-form-errors').html('');
$('textarea#comment-box').val('');
$('#comment-counter').text("<%= pluralize(#video.comments.count, 'comment') %>");
$('.error-explanation').text('');
<% end %>
HERE'S THE PROBLEM: The first time around my server logs show params being sent as: Parameters: {"utf8"=>"✓", "comment"=>{"parent_id"=>"321", "sort"=>"popular", "post"=>"c"}, "commit"=>"Reply", "video_id"=>"283"} - which is great, I post a comment and the comments, if sorted by popular, stay sorted by popular.
But if I post a second consecutive comment, it's not passing the sort params: Parameters: {"utf8"=>"✓", "comment"=>{"parent_id"=>"322", "sort"=>"", "post"=>"d"}, "commit"=>"Reply", "video_id"=>"283"} - my comments reverts to sorting by the default, recent.
I don't know where to go from here. I feel like i'm missing something small, but no amount of searching around has found the solution. Can anyone point me in the right direction, I would really appreciate any ideas on how to get this working, or if need be, how to rework my solution if there's a better way to do this.
THANKS!
EDIT: With MravAtomski's help I was able to get this working. I changed my comment form to:
<% if params[:comment] %>
<%= f.hidden_field :sort, value: params[:comment][:sort] %>
<% else %>
<%= f.hidden_field :sort, value: params[:sort] %>
<% end %>
And in my comment controller create action I added:
if params[:sort]
#sort = params[:sort]
elsif params[:comment][:sort]
#sort = params[:comment][:sort]
end
My guess is that after the first form submit the 'sort' hidden field looses it's value so it does not get sent it in next submit.
<%= f.hidden_field :sort, value: params[:sort] %>
maybe it should be
<%= f.hidden_field :sort, value: params[:comment][:sort] %>
because a comment object is being used in the form_for.
Also this is probably not the case but check if it is doing ajax requests and not html requests. If it's doing html requsests then it probably loses the 'sort' hidden value due to page refresh.
EDIT
so when page loads it uses params[:sort] then after create submit it uses params[:comment][:sort], maybe something like this can be used as quick solution:
<%= f.hidden_field :sort, value: params[:sort] || params[:comment][:sort] %>
Other solution would be to add a data attribute to one of tags that is not changed via create ajax request. i.e. form tag. add a data-sort attribute to form tag used to create comments:
<%= form_for [#video, #comment, , :html => {:class => 'form_height ..., :data-sort => params[:sort] do |f| %>
When comment is created don't change the data-attribute, it should only be set when user changes sorting and send it as ajax request parameter every time sort value is needed.

How to use form_tag to update params

I have been struggling with a problem in Rails for a couple of days and still could not find the solution. Could you help me with that?
Problem: I have a search box that puts a :search_string entry in the params structure. I use a form_tag for that and it works fine.
<% form_tag :controller=> 'items', :action => 'find' do %>
<%= text_field_tag :search_string, params[:search_string] %>
<% end %>
The problem is when I want to add and update other params key-value (in another view), for instance :start_date, to filter the search_string result. Here is the code snipped that I use in the view:
<% form_tag :controller=> "items", :action => "find", :params => params do %>
<%= hidden_field_tag :date_start, '2010-04-01' %>
<%= submit_tag 'April' %>
<% end %>
<% form_tag :controller=> "items", :action => "find", :params => params do %>
<%= hidden_field_tag :date_start, '2010-03-01' %>
<%= submit_tag 'March' %>
<% end %>
When I first click on "April" submit button, then the params is correctly passed to the controller (i.e. there is a params[:start_date]='April'). However when I try to click "March" button afterwards, the params[:start_date] is not updated. I definitely think this is a stupid newbie mistake, but I cannot figure out how to properly use the form_tag. Could you tell me if I am doing something work? Otherwise, could you advise me which is the best way to update the params using form_tag's ? Thank you very much in advance.
Miquel
What you may want to do is instead force-merge the parameters, something along the lines of:
<% form_tag :controller=> "items", :action => "find", :params => params.merge(:date_start => '2010-03-01') do %>
<%= submit_tag 'March' %>
<% end %>
There is a chance you're inadvertently submitting two of the same parameter and the first of them is getting picked, but since the "first" is not clearly defined, you may get inconsistent results.
Have a look in your log file to see what parameters are received from the two forms.

Rails: Using form (collection select) to call show-action

A model named 'book' with attributes 'name' and 'id' is given. How can i use this collection select to call the show-action of a certain book? The one code mentioned below returns the following error message:
Couldn't find Book with ID=book_id
<% form_tag(book_path(:book_id)), :method => :get do %>
<p>
<%= label(:book, :id, 'Show Book:') %>
<%= #books = Books.find(:all, :order => :name)
collection_select(:book, :id, #books, :id, :name)
%>
</p>
<p>
<%= submit_tag 'Go' %>
</p>
<% end %>
book_path is generated once only, for the form tag itself. It won't be updated whenever your selection changes.
When you submit that form, it's going to request the following URL:
/books/book_id?book[id]=5
Since your book_path thinks book_id is the ID number you wanted, it tries to look that up. You could do what you want you by changing the code in your controller from:
#book = Book.find(params[:id])
to:
#book = Book.find(params[:book][:id])
But it kind of smells bad so be warned.
You can create a new route that is not based on the id, like
get 'books/show' # put this above your "resources :books"
and change your form to
<% form_tag books_show_path, :method => :get %>

Resources