Hi I have a working "blog module" on my page.
From the start. My routes looks like this:
root 'static_pages#home'
resources :announcements, only: [:new,:create,:update,:edit,:destroy,:show] do
resources :comments, only: [:create, :destroy]
end
My controller actions looks like this:
static_pages_controllers.rb
def home
#announcements = Announcement.page(params[:page]).order('id DESC')
end
announcements_controller.rb
def new
#announcement = Announcement.new
end
def create
#announcement = Announcement.new(announcement_params)
if #announcement.save
flash[:success] = "Post został pomyślnie dodany"
redirect_to root_path
else
render 'new'
end
end
comments_controller.rb
def create
#announcement = Announcement.find(params[:announcement_id])
#comment = #announcement.comments.new(comments_params)
if #comment.save
flash[:success] = "Komentarz dodano"
redirect_to root_path }
else
render 'staticpages#home'
end
end
Part of the home.html.erb which lists all anouncements and under each of them it adds a form to add a comment:
<% #announcements.each do |announcement| %>
.... some code ....
<% if current_user && user_signed_in? %>
<div class="create_comment" >
<%= render partial: 'shared/create_comments_form', locals: {announcement: announcement } %>
</div>
<% end %>
... some code ...
<% end %>
And _create_comment.html.erb :
<%= form_for announcement.comments.create, url: announcement_comments_path(announcement.id) do |form| %>
<%= form.label :author, "#{current_user.username}:" %>
<%= form.hidden_field :author, value: current_user.username %>
<%= form.hidden_field :user_id, value: current_user.id %>
<%= form.text_field :content, class: "form-control content_length" %>
<div class="char_counter">255</div>
<%= form.submit "Dodaj!", class: "btn btn-primary" %>
<% end %>
My question is there a better way of writing this form, because It just doesn`t look rails for me when I have to specify everywhere all those parameters. Eg. when I send parameters to _create_comment I have to pass announcement object, and in form it self I have to specify the url as it looks for create_comment_path by default.
I think you wanted just to handle nested resources, and need a form to create such a nested resource.
Then this should work:
form_for( [announcement, announcement.comments.new] ) do |form|
Do not use create in the form template, as it actually saves it to the database, you end with lots of empty comments just because somebody viewed the form.
You can use cokoon gem for this. This gem will create Dynamic nested forms using jQuery.
Also I have one more suggestion. Rather then using only you can use except. Because you have used all 6 routes without index.
root 'static_pages#home'
resources :announcements, except: [:index] do
resources :comments, only: [:create, :destroy]
end
Update:
You can use like below:
Form:
<%= form_for :comments do |form| %>
<%= form.label :author, "#{current_user.username}:" %>
<%= form.hidden_field :author, value: current_user.username %>
<%= form.hidden_field :user_id, value: current_user.id %>
<%= form.text_field :content, class: "form-control content_length" %>
<div class="char_counter">255</div>
<%= form.submit "Dodaj!", class: "btn btn-primary" %>
<% end %>
For more info: http://www.sitepoint.com/complex-rails-forms-with-nested-attributes/
Related
invites_controller.rb
def index
#invites = Invites.all
end
invite.rb
has_one :answer
answers_controller.rb
def new
#invite = Invite.find(params[:invite_id])
#answer = #invite.build_answer
end
def create
#invite = Invite.find(params[:invite_id])
#answer = #invite.create_answer
redirect_to :back
end
answer.rb
belongs_to :invite
routes.rb
resources :invites, only: [:index] do
resources :answers, only: [:new, :create]
end
Is it possible to create invites/index.html where for each line-item will be exist working form for answer without any javascript?
With some logic like this:
<% #invites.each do |i| %>
<%= form_for(i, i.build_answer) do |f| %>
<%= f.hidden_field :accept, value: true %>
<%= f.submit "accept" %>
<% end %>
<% end %>
What are the routing and the rendering of forms will be?
ps: I can solve my problem by creating accept column at Invite model and simply updating each line item like this
<% #invites.each do |i| %>
<%= form_for(i) do |f| %>
<%= f.hidden_field :accept, value: true %>
<%= f.submit "accept" %>
<% end %>
<% end %>
but interesting about other way))
Yes, you can achieve it as
<% #invites.each do |i| %>
<%= form_for([i,i.answer.build]) do |f| %>
<%= f.text_field :name_of_you_answer_field %>
<%= f.submit %>
<% end %>
<% end %>
I want to create a form for "Comments" route which is a member of Article Resources:
resources :articles do
member do
post 'comments'
end
end
I want the comment form to be in Articles#Show page. The problem i got an error:
First argument in form cannot contain nil or be empty
If the for is like this:
<div>
<%= form_for #comm do |c| %>
<%= c.label :Your_comment %>
<%= c.text_area :commBody %>
<%= c.submit 'submit' %>
<% end %>
</div>
So how to do it ?
If this is your controller,
def show
#article = Article.find(params[:id])
end
and you want to create a form for a new Comment related to #article that points to POST /articles/3/comments:
<%= form_for([#article, Comment.new], as: :article, url: comments_article_path(#article)) do |f| %>
<%= f.label :body %>
<%= f.text_area :body %>
<%= f.submit 'Submit' %>
<% end %>
Don't forget to add accepts_nested_attributes_for :comments in the Article model. And also don't forget to setup the whitelisted params in the ArticleController.
Another thing: don't use abbreviations for your variable names. Use #article and #comment, not #art and #comm.
#config/routes.rb
resources :articles do
post :comment, on: :member #-> url.com/articles/:id/comment
end
#app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
def show
#article = Article.find params[:id]
#comment = #article.comments.new
end
end
#app/views/articles/show.html.erb
Comment:
<%= form_for [#article, #comment], url: article_comment_path(#article) do |c| %>
<%= c.label "Your Comment" %>
<%= c.text_area :commBody %>
<%= c.submit %>
<% end %>
I was following this tutorial http://www.sitepoint.com/nested-comments-rails/ to implement nested comments for an image board. It worked fine until I made "comments" belong to "boards" and then had to nest my routes.
Here are my routes:
Rails.application.routes.draw do
root "boards#index"
devise_for :users do
get '/users/sign_out' => 'devise/sessions#destroy'
end
resources :boards do
resources :comments
get '/comments/new/(:parent_id)', to: 'comments#new', as: :new_comment
get '/comments/(:parent_id)', to: 'comments#destroy', as: :delete_comment
get '/comments/edit/(:parent_id)', to: 'comments#edit', as: :edit_comment
end
end
Here is my form:
<%= form_for [#board, #comment] do |f| %>
<% if #comment.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#comment.errors.count, "error") %> prohibited this comment from being saved:</h2>
<ul>
<% #comment.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.hidden_field :parent_id %>
<div class="form-group">
<% if #comment.parent_id == nil %>
<%= f.label :title %>
<%= f.text_field :title, class: 'form-control' %>
<% else %>
<% nil %>
<% end %>
</div>
<div class="form-group">
<%= f.radio_button(:user_id, current_user.id) %>
<%= f.label(:user_id, "I want to post as myself") %>
<%= f.radio_button(:user_id, nil) %>
<%= f.label(:user_id, "I want to post anonymously") %>
</div>
<div class="form-group">
<%= f.label :content %>
<%= f.text_area :content, class: 'form-control', required: true %>
</div>
<div class="form-group">
<%= f.label :image %>
<%= f.file_field :image %>
</div>
<%= f.submit class: 'btn btn-primary' %>
<% end %>
And here is my controller:
class CommentsController < ApplicationController
def index
#comments = Comment.hash_tree
end
def new
#comment = Comment.new(parent_id: params[:parent_id])
end
def edit
#comment = Comment.find(params[:parent_id])
end
def create
if params[:comment][:parent_id].to_i > 0
parent = Comment.find_by_id(params[:comment].delete(:parent_id))
#comment = parent.children.build(comment_params)
else
#comment = Comment.new(comment_params)
end
if #comment.save
redirect_to root_url
else
render 'new'
end
end
def update
#comment = Comment.find(params[:id])
if #comment.update(comment_params)
redirect_to #comment
else
render 'edit'
end
end
def make_parent
#comment.parent_id = nil
#comment.save
end
def destroy
#comment = Comment.find(params[:parent_id])
#comment.destroy
respond_to do |format|
format.html { redirect_to comments_url }
end
authorize! :destroy, #comment
end
private
def comment_params
params.require(:comment).permit(:title, :content, :user_id, :image)
end
end
I've tried setting a custom route in the form, this gets the form to at least appear, however when you hit the submit button it returns 'No route matches [POST] "/boards/1/comments/new"'. If I got to the controller and then change the corresponding "get" to a "post" then it causes the form to just reappear after pressing submit and nothing is added to the database. I've also experimented with shallow nesting my routes as per my instructors advice but this didn't work.
Your association between boards and comments must be:
board.rb
has_many :comments
comment.rb
belongs_to :user
routes.rb
resources :boards do
resources :comments, only: [:new, :edit, :destroy]
end
this will create a route
new_board_comment GET /boards/:board_id/comments/new(.:format) comments#new
edit_board_comment GET /boards/:board_id/comments/:id/edit(.:format) comments#edit
board_comment DELETE /boards/:board_id/comments/:id(.:format) comments#destroy
i have some problems with my app, i have posts, posts has_many responces
when i create new responce to the post, not added in the db 'responce' post_id
my routes.rb
resources :categories do
resources :posts
end
resources :posts do
resources :responces
end
controller
class ResponcesController < ApplicationController
def new
#post = Post.find(params[:post_id])
#responce = #post.responces.new(post_id:params[:post_id])
end
def create
#responce = current_user.responces.build(responce_params)
#post = Post.find(params[:post_id])
if #responce.save
flash[:success] = "Вы откликнулись на задание"
redirect_to post_path #post
else
render 'new'
end
end
def show
end
private
def responce_params
params.require(:responce).permit(:price, :comment, :post_id)
end
end
view
<%= form_for([#post, #responce]) do |f| %>
<%= f.text_area :price %>
<%= f.submit "GO", class: "btn btn-large btn-primary" %>
<% end %>
but if add to the view this
<%= f.collection_select :post_id, Post.all, :id, :name %>
rails create post_id to the db
help
You are doing several things the wrong way.
First: I don't think you need two separate resources for the same model. I'd recomend nesting all three resources upon each other like this.
resource :categories do
resource :posts do
resource :responces
end
end
This way you'll be able to find the needed category_id and post_id in the params hash.
I'd also recomend adding :shalow => true to the :categories resource to make your routes a bit prettier.
Second: you need to assign the params[:post_id] in your create action, like this.
#responce = current_user.responces.build(responce_params)
#responce.post_id = params[:post_id]
#post = #responce.post
Alternatevely you can just add a hidden field to your form like I show below, but it I don't like that approach, 'cause it can lead to security risks.
<%= form_for([#post, #responce]) do |f| %>
<%= f.text_area :price %>
<%= f.hidden_field :post_id, :value => #post.id %>
<%= f.submit "GO", class: "btn btn-large btn-primary" %>
<% end %>
In your form you aren't passing in the post_id. You probably want something like this:
<%= form_for([#post, #responce]) do |f| %>
<%= f.text_area :price %>
<%= f.hidden_field :post_id, :value => #post.id %>
<%= f.submit "GO", class: "btn btn-large btn-primary" %>
<% end %>
The hidden field will pass the id of the current post into your form as the post_id parameter.
I have user/micropost/comment models where users can comment on others' microposts. Under every post a textfield is shown so that users can enter comments however I am struggling to get find the Micropost Id. I assume the issue is in my form_for comments or the controllers but I am not really sure. Would love some help, thanks.
Error: Couldn't find micropost without an ID
Models:
User Model: has many microposts, has many comments
Micropost Model: belongs to user, has many comments
Comment Model: belongs to micropost, belongs to user
User Controller:
def show #(the profile page where all the posts and comments are)
#user = User.find(params[:id])
#microposts = #user.microposts.paginate(page: params[:page])
#micropost = current_user.microposts.build if signed_in?
#comments = #micropost.comments
#comment = current_user.comments.build(:micropost => #micropost) if signed_in?
end
Comment Controller:
def create
#micropost = Micropost.find(params[:id])
#comment = current_user.comments.build(:micropost => #micropost) #can someone explain what happens in the parentheses?
#comment.user = current_user
#comment.save
redirect_to :back
end
View/comments/_comment_form:
<%= form_for(#comment) do |f| %>
<div id="comment_field">
<%= f.text_field :content, placeholder: "Say Something..." %>
</div>
<% end %>
Routes:
resources :users
resources :microposts, only: [:create, :destroy]
resources :comments, only: [:create, :destroy]
just add a hidden field for the micropost_id
<%= form_for(#comment) do |f| %>
<%= f.hidden_field :micropost_id, value: #micropost.id %>
<div id="comment_field">
<%= f.text_field :content, placeholder: "Say Something..." %>
</div>
<% end %>
UPDATE: passing micropost_id without any changes to the controller
Based on your comments controller, you're finding micropost based on params[:id] which is missing when you submit the form. The code below fixes that. However, I suggest you look at nested resources which will make the controller code prettier and more slick
<%= form_for #comment do |f| %>
<%= hidden_field_tag :id, #micropost.id %>
<div id="comment_field">
<%= f.text_field :content, placeholder: "Say Something..." %>
</div>
<% end %>
or update the action of the form
<%= form_for #comment, url: comments_path(id: #micropost.id) do |f| %>
<div id="comment_field">
<%= f.text_field :content, placeholder: "Say Something..." %>
</div>
<% end %>
UPDATE: with edits to the comment controller
# view
<%= form_for #comment do |f| %>
<%= hidden_field_tag :micropost_id, #micropost.id %>
<div id="comment_field">
<%= f.text_field :content, placeholder: "Say Something..." %>
</div>
<% end %>
# comments_controller.rb
def create
#micropost = Micropost.find params[:micropost_id]
#comment = current_user.comments.build
#comment.micropost = #micropost
#comment.save
end
Yous should setup your comment resources in this way:
resources :users
resources :microposts, only: [:create, :destroy] do
resources :comments, only: [:create, :destroy]
end
The above resources are called nested resources. And as in your case the comment always relate to a micropost, you should nest comment resources into microposts
and in the Comment Controller:
def create
#micropost = Micropost.find(params[:id])
#comment = current_user.comments.build(:micropost => #micropost) #can someone explain what happens in the parentheses?
#comment.save
redirect_to :back
end
The build method above creates a new object/instance of Comment model and as you have used current_user.comments that means, the object will have user_id = current_user.id automatically you don't need to specify it again. And 'build(:micropost => #micropost)' will add the micropost's id to #comment object.