I have a Index page which shows data for current_user only, and I made another page which shows the same data fields and same format as of index page but not for current_user but after querying for some other column, in this case is it possible to reuse the code of index and using some condition in the controller to pass the object variable different in each case ?
Thanks in advance
Suppose the controller is FoosController and the action of index page for current_user is #bar, then your controller may look like this
class FoosController < ApplicationController
def bar
#user = current_user
end
To reuse the template, you can make a separate partial template say app/views/foos/_index.html.erb.
Then, in app/views/foos/bar.html.erb
<%= render partial: '_index', locals: { user: #user }
To reuse the partial template in another action, you just need to call the partial in another template, feed it with different instance variable.
You bet - #user = params[:user_id] ? User.find( params[:user_id] ) : current_user
Related
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
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
I'm trying to build a profile page that displays posts sent only to the requested user, and allows the visitor to write a post of their own. Because this simplified example should have two distinct controllers: users and posts, I made partials for each post action to render within the user's show action.
Directory structure for my views directory looks like this:
- posts
- _index.html.erb
- _new.html.erb
- users
- show.html.erb
... (etc.)
Section that displays these partials within the user's show.html.erb:
<section>
<h3>Posts:</h3>
<%= render '/posts/new', :post => Post.new %>
<%= render '/posts/index', :posts => Post.where(target_id: params[:id]) %>
</section>
I eventually found out that you could pass variables into the partial in this render line, and though this works, it's very messy and probably doesn't follow the best practices.
Ideally, I'd want these partials to be connected with the posts controller so I can write more complex database queries in a place that isn't the view:
class PostsController < ApplicationController
def new
#post = Post.new
end
def index
#posts = Post.where(target_id: params[:id])
end
def create
#post = Post.new(post_params)
#post.user_id = current_user.id
#post.target_id = params[:post][:target_id]
if #post.save
redirect_to :back, notice: 'You published a post!'
else
render new
end
end
private
def post_params
params.require(:post).permit(:body)
end
end
Currently, I haven't found a way of doing this. I know this is a newb question, but thanks for any help in advance.
You are attempting to treat your controllers like models: doing the post work in post controller and the user work in user controller. But controllers are task-oriented, not model-oriented.
Since you want posts info in your user form, it's typical to gather it in the user controller. E.g.
class UsersController < ApplicationController
def show
...
#posts = Post.where(user_id: user.id)
end
end
That #posts instance variable is visible in the show template and any partials it calls. But many coders prefer to send it explicitly through render arguments, as more functional:
<%= render '/posts/post_list', posts: #posts %>
For one thing it's easier to refactor when you can see at a glance all of the partial's dependencies.
I agree somewhat with #Mori's advice. As he said, you are trying to put too much logic into the controller. I think this was a result of you trying to get it out of the view, which is the right idea, but you want business logic to be in the model.
Also, those index and new actions for PostsController are never going to be called. When you are calling the render posts/new for example, that is rendering the view, not the controller action. So, those controller actions have no reason to exist.
I would implement the fix in perhaps a different way than Mori described. It's a recommended practice to try and pass as few instance variables from the controller to the view as possible (see 3rd bullet in the linked section).
Since it's really the show action of the UsersController we are talking about here, I as someone trying to understand your code would assume the instance variable you are passing to the show view is something like #user.
You may want to use an includes method when instantiating the #user object. The includes statement will allow you to load the additional models you will need to instantiate using the minimum number of queries possible (preventing an N+1 query situation). You probably don't want to load every single one if there are thousands of matching posts, so I put an arbitrary limit of 10 on that.
UsersController
def show
#user = User.find(params[:id]).includes(:received_posts).limit(10)
end
#....
View
<section>
<h3>Posts:</h3>
<% unless #user.id == current_user.id %>
<%= render 'posts/form', post: Post.new(user_id: #user.id) %>
<% end %>
<%= render #user.received_posts %>
</section>
Putting the partial for a new post instead as a view called posts/form will allow you to reuse that form if you want to render an edit action (form_for knows which action to use on submit by calling the passed model's persisted? method).
Note that this code assumes the User model has the second relationship with posts set up to be called received_posts, but you can change it to whatever reflects the reality. By passing the received_posts collection to the render method, Rails is smart enough to know that if you want to render a collection of Post models to look for a posts/_post partial and render one for each Post. It's a little cleaner looking IMO. Just make sure to move your posts/show code into that. posts/show implies this is its own action and not something used as a partial for something else.
I am not able to share views among multiple controllers in Ruby ob Rails application.
Please help.
you can even use partials or explicitly render another template in controller's action.eg: -
def UsersController < ApplicationController
def index
#records = User.all
render '/products/index' # instead of user's index i rendered product's index template
end
end
You can use this in the view you are trying to render the other view
abc - The name of the folder in which the other view exists (views/abc/view_name.html.erb)
xyz - The name of the view
<%= render :template "abc/xyz" %>
I want to have 2 kinds of views for the same posts in my rails application. For instance - in one where a logged in user can update and edit the post, and in the other any user can just view it and comment on it or select it.
How should I go about this? Do I need a separate class? I know I need a separate view for each, but how about the model and the controller?
1.case: your views are going to have similar content, but only the signed in users will have extra options like editing.
You should use a partial view and in your main view you should write something like this:
<% if signed_in? %>
<%= render 'edit_form' %>
<% end %>
Remember, the name of the partial should always start with a underscore, so your partial in this case would be called _edit_form.html.erb or _edit_form.html.haml, depending on what you are using.
2.case: depending on if the user is signed in or not, you want to render completely different views, then you should handle it in your controller:
def show
if signed_in?
render 'show_with_edit'
else
render 'show_without_edit`
end
end
And your files would be named show_with_edit.html.erb and show_without_edit.html.erb
Also, if your view for a signed in user was called show then you could just do this:
def show
render 'show_without_edit' unless signed_in?
end
3.case: if you want to change basically EVERYTHING depending if the user is signed in or not, you could create some custom methods and call them inside your original action like this:
def show
if singed_in?
show_signed_in
else
show_not_signed_in
end
end
private
def show_signed_in
# declaring some instance variables to use in the view..
render 'some_view'
end
def show_not_signed_in
# declaring some other instance variables to use in the view..
render 'some_other_view'
end