rails link_to - pass hash of values to controller action - ruby-on-rails

I have retrieved some records based on condition in a hash - #special_products. Now I want to pass the hash to a non-restful action(:special)/ of the same controller so that I can view the products.
I've tried this but how can link_to pass hash and how should the value be retrieved in action: special? which is in the same products_controller?Many thanks.
products_controller.rb
def show
#special_products = Product.by_company
end
show.html.erb
<%= link_to "Special Products", special_path(:anchor => "#{#special_products}") %>

If you're hitting the show action of the Products controller, you should be showing a product.
If you want to show a product in a special way, use the same show action, but render a different view for it.
def show
#product = Product.find(params[:id])
render #product.special? ? 'special_show' : 'show'
end
If you want to list some products in a different way (a filtered collection), you want to use an index. E.g. the products#index action.
def index
#products = Products.not_special
#special_products = Products.way_special
end
# app/views/products/index.html.erb
Special Products:
<%= #special_products.pluck(:name).to_sentence %>
Finally, note that the parameters you pass to link_to end up in the linked URL, which means that your example link_to is going to render something like #<Array []> in the href attribute.

Related

Rails use the same endpoint instead of two new

In my app I need to display purchased Book in one page and planned_purchase Book in other page. The view will be the same so my question is - do I need to create new controller method and routes to display both or can I use e.g. Index and somehow display two different values depending on request?
current code below:
class BooksController < ApplicationController
before_action :fetch_conversation
def index
#planned = Book.planned_purchase
#purchased = Book.purchased
end
end
class Book < ApplicationRecord
scope :purchased, -> { where(purchased: true) }
scope :planned_purchase, -> { where(purchased: false) }
end
As I can understand: you can do this thing using a single controller GET action.
So, you've this BooksController and index action, which I assume can be accessible via books_path.
You can modify the index method, as follows to accept a new parameter by which you can filter the books:
def index
case params[:filter]
when 'purchased'
#records = Book.purchased
when 'planned_purchase'
#records = Book.planned_purchase
else
#records = Book.all
end
end
Now, you have a view page books/index.html.erb for this index action. Let's break this into 2 separate partials.
In books/index.html.erb:
<% if params[:filter] == 'purchased' %>
<%= render "partial_for_parchased" %>
<% elsif params[:filter] == 'planned_purchase' %>
<%= render "partial_for_planned_parchased" %>
<% end %>
Inside those partials you can modify the view based on the category.
Now, to get those two different page, you need to define 2 separate urls:
<%= link_to 'Purchased', books_path(filter: 'purchased') %>
<%= link_to 'Planned Purchased', books_path(filter: 'planned_purchase') %>
As your, def index, is a GET method and not depending on the strong parameters, so you don't need to add filter in your params.required(:book).permit(...)
Hope I covered all the areas!
I think the answer should be pretty simple and straight.
You can just pass a parameter to the index method and filter records inside it and return them.
def index
case params[:filter]
when 'purchased'
#records = Book.purchased
when 'planned_purchase'
#records = Book.planned_purchase
else
# get all records or throw an error
end

Routing to different instances of Index using a Helper Method

I have a controller called BookingsController with a bookings#index action. Inside the index action, there are 2 instance variables, #pending_bookings and #approved_bookings, which query Booking objects by their status.
def index
#pending_bookings = Booking.where(host_id:#user.id,
status:'pending')
#approved_bookings = Booking.where(host_id:#user.id,
status:'approved')
end
I want to route the user to a different instance of index depending on the link they click. Basically bookings_path(#pending_bookings) should route the user to the index page displaying all pending_bookings, adversely, bookings_path(#approved_bookings) should route the user to the index page displaying all approved_bookings.
In my view, I have 2 links that should direct the user to each path respectively.
<%= link_to 'Pending Reservations', bookings_path(#pending_bookings)%>
<%= link_to 'Approved Reservations', bookings_path(#approved_bookings)%> `
The index.html.erb file:
<%= booking_index_helper_path %>
contains an embedded helper method that should recognize the path the user clicks and render the proper Booking objects.
Here's the (flawed) logic for recognizing the path the user chooses and rendering the necessary objects:
pages_helper.rb:
def booking_index_helper_path
if bookings_path(#pending_bookings)
render #pending_bookings
elsif bookings_path(#approved_bookings)
render #approved_bookings
else bookings_path(#total_bookings)
#total_bookings
end
end
I put a binding.pry in the helper method to confirm it is being hit (it is). For some reason when I click the link to direct me to the proper objects, however, the first condition is always satisfied. What is a better way to write this conditional to recognize the path the user chooses?
It seems like you're going about this in a more complicated way than you need to. Why not just have an index like:
def index
#Rails autoescapes this string so no fear of sql injection using user supplied strings
#bookings = Booking.where(host_id:#user.id, status: "#{params[:status]}")
end
Then use a link like:
<%= link_to 'Pending Reservations', bookings_path(status: 'pending')%>
<%= link_to 'Approved Reservations', bookings_path(status: 'approved')%> `
Now your view can just handle #bookings and not concern itself with the types of #bookings as that is done by the logic in your controller. This is the bare minimum but you should get in the habit of adding error messages etc. to your controllers so consider doing:
def index
if params[:status].present?
#Rails autoescapes this string so no fear of sql injection using user supplied strings
#bookings = Booking.where(host_id:#user.id, status: "#{params[:status]}")
flash[:success] = "#{params[:status].titleize} Bookings loaded."
redirect_to whatever_path
else
flash[:error] = "Something went wrong"
redirect_to some_path
end
end

How to filter by params and user input in Rails

I am trying to display only the rows that belong to certain states in my application. I can do it the long way in Javascript, but I would prefer to better understand Rails and queries in the controller. I want to take the users to another page and then show them only that the companies in that state. It would be great to not have to link them to another page. Does anyone know how to do this?
Here is what I have in my controller
def vendors
#vendors = Collective.where(sort: 'Vendor').all
#vendors = #vendors.where(params[:state])
end
My route
get '/vendors/:state', to: 'collectives#vendors'
Then I use the stereotypical method to print a table in a html.erb file.
<% #vendors.each do |company| %>
<tr>
<td><%= company.name %></td>
<td><%= company.state %></td>
etc...
Should your controller code change the where as follows:
def vendors
#vendors = Collective.where(sort: 'Vendor').all
#vendors = #vendors.where(state: params[:state])
end
or better:
def vendors
#vendors = Collective.where(sort: 'Vendor', state: params[:state])
end
Using sessions instead of url params.
This is more or less what you can do, sorry if it is not completly working for your case, just to give an idea.
# view collectives/index (or whatever you have)
<%= form_tag (controller: :collectives, action: :set_status_filter, method: :post) do %>
<%= select_tag(:session_status_filter, options_for_select(#your_list_of_options_for_the_filter)) %>
<%= submit_tag "Set filter" %>
<% end %>
# collectives controller
def index # or whatever, this is the page containing the form and the list to show
#vendors = Collective.where(sort: 'Vendor').all
if session[:session_status_filter] == # etcetera
then #vendors = #vendors.where(state: session[:session_status_filter]) # for example
else # another option just in case, etcetera
end
end
def set_status_filter # this action is called by the form
session[:session_status_filter] = params[:session_status_filter]
respond_to do |format|
format.html { redirect_to *** the view where the form is placed ***, notice: 'the filter is set to: ....' + session[:session_status_filter] } # after the session variable is set the redirects goes to index which uses the session to filter records
end
end
params[:session_status_filter] is passed by the form to collectives#set_status_filter. The value is used to set the session variables. After that the action collectives#set_status_filter redirects to the index, or whatever page you placed the form and the list to show.

Only getting the index for the current user

I have a pretty basic app. I've managed to install devise. The idea is to have users who have created challenges (1 to many relationship).
I want the logged in user to be able to see all the challenges they have created.
I order to do this am I correct in thinking that I can pass the current user id as a parameter to just get the challenges of the current user as follows (assuming the view is set up correctly)
<%= link_to challenges_path(user_id: current_user.id), class: 'expandable' %>
challenges controller
def index
#challenges = Challenge.all
render :layout => false
end
If this is the default behaviour you want for the index of the challenges, then you can simply change your controller action directly, and no need to modify your link_to to add user_id
challenges controller
def index
#challenges = Challenge.where(user: current_user)
end
Now if you want to change the behavior only if the user_id GET param is set, you can keep your link_to like this and modify your controller this way
def index
#challenges = params[:user_id] ? Challenge.where(user: current_user) : Challenge.all
end

form_tag action not working in rails

I have this form in my application.html.erb.
<%= form_tag(:action=>"index", :controller=>"posts") %>
<p>
// code here
</p>
I dont understand why is this getting directed to posts->create instead of posts->index?
Thanks.
Basically, Rails observes and obeys "RESTful" web service architecture. With REST and Rails, there are seven different ways to interact with a server regarding a resource. With your current code, specifying the form's action as index doesn't make sense: Rails' form helpers can either POST, PUT or DELETE.
If you wanted to create a post, then redirect to the index, you can do so in the applicable controller action:
class PostsController < ApplicationController
...
def create
#post = Post.new
respond_to do |format|
if #post.save
format.html { redirect_to(:action => 'index') }
end
end
While your form would look like:
<% form_for #post do |f| %>
# put whatever fields necessary to create the post here
<% end %>
You seem to be a little mixed up with respect to the uses for each action. Here's a quick summary of typical RESTful usage:
Index -> view a list of items
New/Edit -> form where items are added or edited
Create/update -> controller action where items are created/updated
The reason your routes file is not taking you to index is because index is not an action where posts are typically created or updated. The best way is to go RESTful. Unless you have a very unusual situation, the best way to set your system up is probably a little like this:
# routes.rb
resources :posts
# application.html.erb (or better: posts/_form.html.erb).
<% form_for #post do |f| %>
<% end %>
# posts controller, whichever action you want to use
def new
#post = Post.new
end
By putting the form in a partial called form you can access it in new, edit, or wherever else you need to manipulate a post in your system.

Resources