What is the right way of creating a nested view in rails - ruby-on-rails

I have blog model. Blog has_many comments. I have created all the CRUD related to the blog. The comments doesnt have a page on its own. On the blog page, there could be text area and on the entering the comment, it would be saved thru ajax. But normally when a new page is created a new object is sent from the controller, so should i create a comment object and send it thru Blog's new action like this
def new
#comment = Comment.new
#blog = Blog.new
end
Or should i just access the comment objects present in the blog while creating the view
<form_remote_for #blog.comments>
Which is the right way of doing this? Is there any better solution

Its preferred to have initialization of new comment in controller action. But its rather a guideline or practice I follow rather than the rule.
There is no form_remote_for tag. If its rails 2, tag is remote_form_for, similar thing in rails 3 would be:
form_for [#blog, #comment], :remote => true do |f|

Related

How to get 1st nested object into a 2nd level nested object controller?

I have a Character model that has a show page. On the show page, I have a loop of comments that are dynamically generated via a partial. In that comments partial, I have another partial for votes, which contains voting buttons. Naturally, I want to allow votes on comments.
I am unsure how to get the comment object into the votes controller (or VotesController module, depending on the implementation) for creating a vote.
Getting the character object id to the votes controller is simple enough, since the actual view is the character show page, but obtaining a specific comment's id that is genrated from a partial, by clicking a vote button in a partial that is nested in the comments partial is causing me to draw a blank for the syntax of accessing that comment.
(I am using acts_as_votable for votes, and acts_as_commentable for comments.)
app/views/characters/show.html.haml
= render partial: 'comments/comment', collection: #comments, as: :comment
app/views/comments/_form.html.haml
.comment{ :id => "comment-#{comment.id}" }
%hr
= render partial: 'votes/vote_comment'
%h4
#comment body
app/views/votes/_vote_comment.html.haml
.vote-comment-buttons
= link_to image_tag("upvote.png"), votes_upvote_path(), method: :post, remote: true
= link_to image_tag("downvote.png"), votes_downvote_path(), method: :post, remote: true
app/controllers/votes.html.haml
VotesController < ApplicationController
def upvote
# Need the specific comment or comment id whose vote button was clicked.
end
def downvote
# Need the specific comment or comment id whose vote button was clicked.
end
end
Well, here are the basic tips:
You can not pass ruby objects through HTTP, but you can pass id and type of them to build them in your controller.
Even when you type something like comment_path(comment), only id of that comment is passed to your action. That is easily checked by observing your action code (it should contain something like Comment.find(params[:id])).
Passing any desired amout of additional parameters can be done with just providing them to your route helpers, like that: some_voting_path(commentable_id: 14, commentable_type: 'character').
You can access that params inside of your action with params['commentable_type'] or whatever values you pass with your URL. In case you follow passing id and type approach, you should be able to do some metaprogramming:
def upvote_method
model = params[:commentable_type].camelize.constantize # => e.g., Post
object = model.find(params[:commentable_id]) # => post object
# here goes your inner logics
end
Beware that in case you send your request using GET method, these params are gonna be shown in your browser URL. However, you should not use GET for your purpose here, as voting changes the state of objects in your database.

Creating multiple forms without flooding the controller with new objects

How do I create multiple forms without having to create the object in the controller/load them by ajax?
Say you have a Forum model that has many posts, and each post has many tags. You are looking at a list of posts at forms_controller#index.
Say you want to allow the user to tag posts from the same template. One way is to load the form via a remote link when needed.
# forums/index.html
= link_to "Add Tag", new_post_tag_path, remote: true
# tags_controller.rb
def new
#tag = #post.tags.build
respond_to do |format|
format.js
end
end
# tags/new.js.erb
<%= j render("form") %>
But that requires going to the server and back again to render the form.
Another way is to create the object when looping over the posts.
- #posts.each do |post|
= form_for #post.tags.build do
That doesn't work very well if you want to create several tags at the same time.
Is there another way to do this, perhaps with JS, without having to go to the server/or create the object as in the second approach?
Ryan Bates has done a great screencast on this subject. http://railscasts.com/episodes/197-nested-model-form-part-2 Hopefully this should provide you with what you need.

Create a link to a defined method?

As my first Rails app, I'm trying to put together a simple blog application where users can vote on posts. I generated the Blogpost scaffold with a integer column (entitled "upvote") for keeping track of the vote count.
In the Blogpost model, I created a function:
def self.voteup
blogpost.upvote += 1
end
On the Blogpost index view, I'd like to create a link that does something like:
link_to "Vote up" self.voteup
But this doesn't seem to work. Is it possible to create a link to a method? If not, can you point me in the right direction to accomplish this?
What you are trying to do goes against the MVC design principles. You should do the upvoting inside a controller action. You should probably create a controller action called upvote. And pass in the post id to it. Inside the controller action you can retrive the post with the passed in ID and upvote it.
if you need serious voting in your rails app you can take a look at these gems
I assume that you need to increment upvote column in blogspots table. Redirection to a method is controllers job and we can give links to controller methods only. You can create a method in Blogposts controller like this:
def upvote_blog
blogpost = Blogpost.find(params[:id])
blogpost.upvote += 1
blogpost.save
redirect_to blogpost_path
end
In your index page,
<% #blogposts.each do |blogpost| %>
...
<%= link_to "Vote up", :action => upvote_blog, :id => blogpost.id %>
...
<% end %>
You can not map Model method to link_to in view. you can create an action in controller to access the Model method and map it using link_to, also if the action is other than CRUD, then you should define a route for the same in route.rb

Should my edit and new actions re-use the same view? (editing a post)

I have this setup in my routes:
namespace :admin do
resources :posts
end
so in my admin/posts_controller.rb I have my new, create and edit actions.
I want to re-use my new and edit view page somehow, b/c the page has allot of custom javascript etc. for the form and I don't want to repeat myself.
How can I do this?
i.e. for the edit page, I have to pre-populate the form fields and for the new page it is to be empty.
For a new page, it should post to the 'create' action, and for the edit I am thinking it should post to a different 'update' action (which is a PUT request as per my rake routes)?
Rails is pretty clever, a form like
<% form_for post do |f| %>
<% end %>
will post to the create action if post.new_record? == true and to the update action otherwise.
So, you can put the form in a partial, and render it inside your new/edit views, which will probably have different headings and copy.
Alternatively you can just have a single view and perform your own logic based on post.new_record? - but I'd advise against this because you'll end up with an unnecessarily complex view.

new/edit actions on show page :: best practice question

i am designing a dashboard page that summarizes data from multiple models, all on one page. and i also want to include forms on that page for creating new items.
after some experimenting, i realized i needed some advice as to the best practice for this situation... here are the 2 methods i was attempting
1) using nested field_fors...
form_for #job do |j|
j.fields_for :invoice do |i|
i.fields_for :charges do |c|
c.text_field :amount
or 2)
form_for #job.invoice.charges.build do |c|
c.text_field :amount
with the nested approach, the problem is now i have a new object which throws a wrench in my show view when it iterates through existing objects. suddenly trying to render all the created_at times fails since the new object hasn't been saved yet.
the problem with approach 2) is either i post each form to its own controller to handle it, but if it doesnt validate, i need to render the show view... which means i have to provide each controller i have a form for with all the instance variables and methods etc to render the page.
and i havent quite figured out how the error handling works. sometimes my form is marked with fieldWitherrors class, and sometimes it doesnt. i have been trying out both approaches but am having trouble figuring it out.
can anyone offer advice on the direction i should go here?
If new charge is all you need, then the latter approach is the way. Note that these do basically the same:
form_for #job.invoice.charges.build do |c|
and
#charge = Charge.new(:invoice_id => #job.invoice.id)
form_for #charge do |c|
Using this second case you don't connect the new charge with the job until it's saved and if you move the first line with #charge=... to a controller, you can re-render the form when validations fail.

Resources