I'm writing a web app containing posts and comments. Since there are many places I need to display a bunch of posts with their comments, I'm thinking about reusing the code. But I'm not sure if it is correct to use a partial _posts.html.erb that displays each post in #posts, or implement it directly through the show action in posts controller, and render this action when necessary in other views. Anyone has any idea?
For this use case, partials are your best bet.
As you said, there are many places I need to display a bunch of posts with their comments. A main premise of Rails is Don't Repeat Yourself. It is far more tidy (and programmatically sound) to retrieve #posts in your various controller actions and then render those posts/comments using partials in your views. Otherwise, you'd be rendering the show action within other views – views aren't really meant to render out actions, but the other way around, rather.
Yes, according to DRY principle your are thinking in a perfect manner, you should create a partial.
A good practice is instead of using #posts, you should pass posts as a locals like:
<%= render 'posts', posts: #posts %>
in this way you can use this partial from anywhere with providing just posts as locals without any dependency on #posts instance variable.
Related
I'm still fairly new to rails, but I've been trying to figure it out by building a bunch of simple projects like blogs, forums, clones over the weekend.
One of the blogs that I'm working on is a typical blog with users(devise), posts, and comments. I've been following a tutorial for this.
The thing that I've been really confuse on is setting up the comments and rendering it.
The tutorial teaches me to make a partial for comment in the comment view named _comment.html.erb and then another for form _form.html.erb . I fully understand that to call the form i just do render 'comments/form' but for the comment.html.erb i need to render #post.comments how come it's not 'comments/comment'? Why is it comments with the S?
I've tried reading up on render and plurization on rails, but most of them just talk about the model.
There are two convention over configuration things going on here that may be causing you some confusion but are meant to save time.
Rails will automagically find the right partial if it follows naming conventions, meaning if your model is Comment that the view partial is located in app/views/comments/_comment.html.erb. That means you don't have to specify the location of the partial when calling render, but instead can just pass the model object directly and Rails figures out that you want it to render the partial and finds it on its own.
The reason it's comments plural here is that you are rendering all of the comments as a collection, not just a single comment. It's a convenience feature to allow the developer to simply tell Rails to render a collection and it will automagically know to use the corresponding partial. It's identical to typing:
#post.comments.each do |comment|
render 'comments/comment`, object: comment
end
Note how the above code is calling render directly on a model object so we don't have to bother specifying the location of the partial (again, assuming you followed the convention when naming and locating things). If we named the partial something else, then we'd have to specify the location like your other examples.
I've got an index page where I've decided to render both Usernames, Posts and other informative sources.
Since Devise takes care of the user creations and login sessions-managing I am aware of needing to have multiple controllers.
PostsController, DashboardController and RandomController. The plan is to make them be rendered on different parts of the page, with different sizes and on different places.
Let's say I've got Post.find(:all => :order => "created_at DESC") in the PostsController. I want to render that someplace on the page.
I want to render Usernames right under the according Post which belongs_to them, and I want to render some still "Random" Controller action which will post news on some random place on the page.
So a recap:
The PostsController finds and renders some part of the index page, and the DeviseControllers I guess renders the username belonging to the according one, and the Random Controller renders some random thing from news or something on the middle of the page. Amongst all of them all of this is happening on another controller called DashBoard.
So all these controller actions are supposed to be rendered on a own DashBoard controller index view.
Any tips, or good ideas on how to accomplish this. I know about partial rendering, but I don't know how to achieve that.
It seems we're talking about a view rendered by Dashboard#index. So all you need to do is define all the info on the page as instance variables under the index action.
Probably the bit of info that's missing for you is that controllers are free to use data from any of your models (and even other classes that don't inherit from ActiveRecord::Base).
For instance, in Dashboard#index, you can have:
#posts = Post.order("created_at DESC").all
Which is referring to a different model than the Dashboard model (assuming one exists).
I am using Ruby on Rails 3.2.2 and, since my system implementation, I would like to generate different outputs (in views) and / or to retrieve different records (in controllers) depending on the "access-er" user authorization (for instance, the authorization could depend on if the "access-ed" user is or not is the current "access-er" user).
How can I handle the situation? That is, for example in order to handle if the user is or not the current user and so to display different content, should I implement two view files and / or controller actions for each case, or should I use if else statements directly in the view file and / or in the controller action?
A cleaner approach I would suggest is to create different roles for users and group them. So for that particular group you can have a separate view files and controllers. One advantage of this approach is it will be very easy to read the code and we can easily understand the code. We get more control on the page easily without having to worry about other users. We can even avoid the need for many filters. But if there is only two type of users then it could be managed easily with if else statement, so choosing the right method will depends on the problem too.
I usually use if else statements right in the views, and render partials if there is a lot of markup between the statements to keep things clean.
That is a style choice though so if you are finding you are making massive conditionals on every page, it could be that you need to rethink your organization in the controller or even make a new resource. For simple things like adding an 'edit' or 'delete' button if the user is an admin, I would use conditionals in the view.
For .html.erb markup, you can do normal if/else blocks like this:
<% if <condition> %>
<%= render 'partial_name' %>
<% else %>
<p>Some other content</p>
<% end %>
Where condition is your condition, like for example current_user? #user
In my current project I have a couple instances where I have a re-usable form that exists inside a rails partial. This form submits to a specific controller via ajax (:remote => true). The controller does some stuff and then returns back the appropriate js.erb to modify the page via javascript.
This works fine for when I have a single view. But the problem seems to happen when this re-usable partial exists on multiple views. In view 1 I might want to issue a completely different set of javascript commands then in view 2.
As a concrete example, say I have a comments controller that has the normal CRUD operations.
I now have partial called _comments_box.erb. This _comments_box.erb contains the ability to submit a comment via a simple line:
- form_for comment, :url => post_comments_path(post), :remote => true do |f|
This submits to a comments_controller.rb create method which looks somethings like this:
def create
... do some stuff, like create a new comments model
respond_to do |format|
# will respond with create.js.erb
format.js
end
end
The create.js.erb in turn adds a comment to the view, perhaps doing a bunch of other updates to the DOM.
Say I render the _comments_box.erb within a view called post_summary.erb. Now I have another view, post_detail.erb that requires the same _comments_box.erb. However the post_detail.erb requires me to update completely different divs on the DOM in response to a new comment.
I need to create a different JS response for each instantiation. So I can either:
Create an alternate controller method, say create_2. Pass in some parameter to the _comments_box.erb from post_detail.erb to the _comments_box.erb partial so it knows which controller method to fire. This will allow me to have a separate file _create_2.js.erb that will allow me to manipulate the post_detail.erb view independently.
Forget about using js.erb altogether and just use plain old AJAX and get back JSON, and handle the javascript manipulation completely on the client-side.
It seems option 1 allows me to continue to use the UJS supported by Rails which is nice. But also means I probably will be adding a lot of duplicate code everywhere which is annoying. Is there a way for me to do this elegantly while continuing to use UJS?
That's exactly the purpose of Apotomo: http://apotomo.de/
Here is it's own description:
Apotomo is a true MVC widget framework
for Rails. Widgets are based on Cells
and provide reuseable view components.
Having bubbling events, they know when
and how to update themselves via AJAX!
Working with Apotomo widgets almost
feels like developing GUI components –
in a Rails environment.
Have a try, it's great.
I'd not recommend using UJS for frontend apps: server shouldn't take care of client side business. I agree it's useful and clean but it lacks performance and thus should be kept for backend stuff (RJS will move into a gem, see here: http://weblog.rubyonrails.org/2011/4/21/jquery-new-default).
That said, back to the solutions you expose:
1) I think you won't need an extra controller, you'd just have to pass additional params in order to know from where to query came from. A hidden_field could do the trick. With this info, render the good js.erb file
format.js { if condition
render "create.js.erb"
else
render "create_2.js.erb"
end
}
2) I'd go for it and return json but you'll face the same problem: knowing from where the request comes from.
A better solution (than using a hidden_field) might be to check the request.referer in your controller action. This way you leverage the fact that each context has a unique URL, and don't have to explicitly specify another unique value when rendering your widget partial.
I have two controllers for two respective models, by example, photos and categories. index and show methods are very similar in each controller, and the views are identical. What is the best method for share the view by the two models?
I've though two options:
Use a helper. In the helper will put the code for the view, and will call the helper from each view (photos/views and categories/views)
Use a partial in each views. I think it's a more clean solution, but I see huge DRY's in my mind when going to code this solution.
So, I have two controllers from two models, each one at and exposes a #photo object (photos controller with all the photos, and categories controller with just the selected categorie's photos) and I need one view to show both.
I'm looking for an elegant solution for this, complaining REST and DRY principes. Any idea?
Thanks in advance.
I have a similar situation with one of my projects. All the delete views for most controllers are styled the same way, display the same confirmation boxes, and simply renders a predictable display of whatever object is being deleted.
The solution was quite simple and elegant in my opinion. Simply put, what we (the developers) did was create a new directory in app/views called shared and put shared views in there. These could be full template files or just partials.
I would suggest using a shared template (in neither categories nor photos view directories, but rather in the shared directory) and rendering it manually from the view.
e.g. have a method as such in both controllers and a file app/views/shared/photo.html.erb:
def show
#photo = Photo.first # ... or whatever here
render :template => 'shared/photo'
end
This should successfully render the shared template. It is the DRYest route and doesn't have the feeling of pollution you get when using a more-or-less empty view in each controller's view directory just to include a shared partial, as I understand your question is suggesting.
About the first answer:
If the partial must be rendered from a view:
<%= render :partial => "shared/photo" %>
and the partial must be in app/views/shared/_photo.html.erb
I'd use an helper because they are shared among views. For the HTML part, I'd use partials so it would be a mix of both ways.
This looks like a good use case for the Cells gem.
#bjeanes If all your delete views are the same, you can create views/default/delete.html.erb and all the delete actions will use it.
That's what i'm doing: Most of my views are on default, and i create specific ones only when needed
Update: Ok, this post is from 2009, anyway, i will keep my comment here in case someone gets here from Google like i did.