I was trying to create associated model Comment for Post via ajax in Rails 5, but get the error.
ArgumentError in Posts#show
Showing /home/mnml/rails/ajax-app/app/views/comments/_form.html.erb where line #2 raised:
wrong number of arguments (given 1, expected 0)
routes.rb
resources :posts, only: [:new, :create, :show, :destroy] do
resources :comments, only: [:new, :create, :show, :destroy]
end
post.rb
belongs_to :user
has_many :comments
comment.rb
belongs_to :user, optional: true
belongs_to :post
posts_controller.rb
def show
#post = Post.find(params[:id])
#comment = #post.comments.build
#comments = #post.comments
end
comments_controller.rb
def new
#post = Post.find(params[:post_id])
#comment = #post.comments.build
end
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(comment_params)
#comment.user_id = current_user.id
respond_to do |format|
if #comment.save
format.html { redirect_to #comment.post, notice: 'Comment was successfully created.' }
#format.json { render #comment, status: :created, location: #comment }
format.js
else
format.html { render :new }
format.json { render json: #comment.errors.full_messages, status: :unprocessable_entity }
end
end
end
private
def comment_params
params.require(:comment).permit(:description)
end
posts/show.html.erb
<%= render "comments/form"%>
comments/_form.html.erb
<form>
<%= form_with [#post, #post.comments.build], id: :new_comment do |form| %>
<div class="form-group">
<%= form.label :description %>
<%= form.text_field :description, id: :comment_description, class: "form-control" %>
</div>
<div class="form-group">
<%= form.submit "Comment it", class: "btn btn-default", data: { "disable-with": "Comment is saving..." } %>
</div>
<% end %>
</form>
comments/create.js.erb
$('#comments').prepend('<%= j render(#comment) %>')
Where I do my mistake? Thanks for help
You need to call form_with like this in your _form.html.erb file
<%= form_with model: [#post, #post.comments.build], id: :new_comment do |form| %>
Related
I'm building a forum app where users can create posts and comment them, at this point the post functionally works fine but the comment form only works once and from then on the post page crashes showing the following error:
NoMethodError in Post#show
undefined method `to_key' for #
Did you mean? to_set
to_ary
through rails console i can see that the comment was saved.
here is the terminal error:
ActionView::Template::Error (undefined method `to_key' for #<Comment::ActiveRecord_Relation:0x007fb845e9cca8>
Did you mean? to_set
to_ary):
7: <h3>Reply to thread</h3>
8:
9: <div class="comments_form">
10: <%= form_for #comment, url: new_comments_path(#post), method: :post, remote: true do |f| %>
11: <%= f.label :title %><br>
12: <%= f.text_field :title, placeholder: "Type the comment title here" %><br>
13: <%= f.label :content %><br>
app/views/post/show.html.erb:10:in `_app_views_post_show_html_erb__4054771198415677123_70214711817160'
here is the form:
<%= form_for #comment, url: new_comments_path(#post), method: :post, remote: true do |f| %>
<%= f.label :title %><br>
<%= f.text_field :title, placeholder: "Type the comment title here" %><br>
<%= f.label :content %><br>
<%= f.text_area :content, placeholder: "Type the comment content here" %><br>
<%= f.submit %>
<% end %>
rails mention that error is in this line of the form:
<%= form_for #comment, url: new_comments_path(#post), method: :post, remote: true do |f| %>
the comment_controller.rb:
class CommentController < ApplicationController
before_action :set_comment, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
def index
#comments = Comment.all
end
def new
#comment = Comment.new
end
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(params[:comment].permit(:comment))
#comment.user = current_user
#comment.assign_attributes(comment_params)
if #comment.save
format.html { redirect_to #link, notice: 'Comment was successfully created.' }
format.json { render json: #comment, status: :created, location: #comment }
else
format.html { render action: "new" }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
def destroy
#comment.destroy
respond_to do |format|
format.html { redirect_to :back, notice: 'Comment was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_comment
#comment = Comment.find(params[:id])
end
def comment_params
params.require(:comment).permit(:post_id, :content, :title, :user_id)
end
end
the routes.rb:
devise_for :users
root to: 'post#index'
#Posts
get '/posts/new', to: 'post#new'
get '/post/:id', to: 'post#show', as: 'show'
get '/post/:id/edit', to: 'post#edit', as: 'edit'
post '/post', to: 'post#create'
put '/post/:id', to: 'post#update', as: 'update'
delete '/post/:id', to: 'post#destroy', as: 'delete'
#Comments
post '/post/:post_id', to: 'comment#create', as: 'new_comments'
and the post_controller:
class PostController < ApplicationController
def index
if params[:user].blank?
#posts = Post.all.order("created_at DESC")
else
posts = Post.where(user_id: #user_id).order("created_at DESC")
end
end
before_action :authenticate_user!
before_action :find_post, only: [:show, :edit, :update, :destroy]
def show
if Comment.where(post_id: #post.id).present?
#comment = Comment.where(post_id: #post.id)
else
#comment = Comment.new
end
end
def new
#post = Post.new
end
def create
#post = Post.new
#post.user_id = current_user.id.to_i
#Post.create(title: title.string, content: content.string, user_id: current_user.id)
#post.assign_attributes(post_params)
if #post.save
flash[:notice] = "Successfully created post!"
#Post.create(title: title.string, content: content.string, user_id: current_user.id)
redirect_to show_path(#post)
else
flash[:alert] = "Error creating new post!"
render :new
end
end
def edit
end
def update
#post.assign_attributes(post_params)
if #post.changed?
#post.save
redirect_to show_path(#post)
else
render edit_path(#post)
end
end
def destroy
#post.destroy
redirect_to root_path#, notice: “Post destroyed”
end
private
def post_params
params.require(:post).permit(:title, :content, :user_id)
end
def find_post
#post = Post.find(params[:id])
end
end
Thanks in advance for your help.
I think replacing this line:
<%= form_for #comment, url: new_comments_path(#post), method: :post, remote: true do |f| %>
with this:
<%= form_for #post.comments.new, url: new_comments_path(#post), method: :post, remote: true do |f| %>
Should fix the issue!
I am trying to improve an exercice that I did on treehouse, the idea was to remake a little version of facebook thing, where users could publish statuses.
Now I want that a user can comment any statuses... And I am kinda lost...
The idea is to have all on the same page (if possible?, like on the real facebook)
So the comment form and the "displaying" content...
I hope anyone could help me :)
This is my github repository
I think I haven't understand how to call variables from a controller to another...
If someone could explain me with very easy words ... I am not native english speaker... so sometime it's difficult..
Here are the statuses part
controllers/statuses_controller/rb
class StatusesController < ApplicationController
before_filter :authenticate_user!, only: [:new, :create, :edit, :update]
before_action :set_status, only: [:show, :edit, :update, :destroy]
def index
#statuses = Status.all
#comments = Comment.all
end
def show
#status = Status.find(params[:id])
#comments = #status.comments.all
end
def new
#status = Status.new
#comment = #status.comments.build
end
def create
#status = Status.new(status_params)
#status.user = current_user
respond_to do |format|
if #status.save
format.html { redirect_to #status, notice: 'Status was successfully created.' }
format.json { render :show, status: :created, location: #status }
else
format.html { render :new }
format.json { render json: #status.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #status.update(status_params)
format.html { redirect_to #status, notice: 'Status was successfully updated.' }
format.json { render :show, status: :ok, location: #status }
else
format.html { render :edit }
format.json { render json: #status.errors, status: :unprocessable_entity }
end
end
end
def destroy
#status.destroy
respond_to do |format|
format.html { redirect_to statuses_url, notice: 'Status was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_status
#status = Status.find(params[:id])
end
def status_params
params.require(:status).permit(:user_id, :content, :comments_attribute[:id, :status_id, :content])
end
end
models/status.rb
class Status < ActiveRecord::Base
belongs_to :user
has_many :comments
default_scope -> { order(created_at: :DESC)}
validates :content, presence: true,
length: {minimum: 2}
validates :user_id, presence: true
end
views/comments/_form.html.erb I create a render in my index below:
<% simple_form_for #status.comments do |f|%>
<%= f.input :content %>
<%= f.button :submit %>
<% end %>
view/statuses/index.html.erb
<div class="page-header">
<h1>All of the Statuses</h1>
</div>
<%= link_to "Post A New Status", new_status_path, class: "btn btn-success"%>
<br>
<br>
<% #statuses.each do |status| %>
<div class="status">
<div class="row">
<div class="col-xs-1 avatar">
<%= image_tag status.user.avatar.thumb if status.user.avatar?%>
</div>
<div class="col-xs-7">
<h4><%= status.user.full_name%></h4>
</div>
</div>
<div class="row">
<div class="col-xs-8">
<p><%= simple_format(status.content) %></p>
</div>
</div>
<div class="row">
<div class="col-xs-8">
<%= link_to time_ago_in_words(status.created_at) + " ago", status %>
<% if status.user == current_user %>
<span class="admin">
| <%= link_to "Edit", edit_status_path(status) %> |
<%= link_to "Delete", status, method: :delete, data: {confirm: "Are you sure?"} %>
</span>
<% end %>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<p>Comments</p>
<% #comments.each do |comment| %>
<%= comment.content %>
<% end %>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<%= render "comments/form" %>
</div>
</div>
</div>
Now the comments part:
model/comment.rb
class Comment < ActiveRecord::Base
belongs_to :status
belongs_to :user
end
controllers/comments_controller.rb
class CommentsController < ApplicationController
def create
#comment = Comment.new(params_comment)
end
def index
#statuses = Status.all
#comments = Comment.all
#comment = Comment.find_by(params[:id])
end
private
def params_comment
params.require(:comment).permit(:content)
end
end
routes.rb
resources :statuses do
resources :comments
end
user.rb
that's a part of what I have in there
has_many :statuses
has_many :comments
your comments creation method should look like this:
#status = Status.find(params[:status_id])
#comment = #status.comments.create(comment_params)
#comment.user_id = current_user.id if current_user
#comment.save
I have a problem a week ago with nested form. I have a model called users another Auto and another Perfil. I need that when a user creates a new Auto you can change your perfil data if needed
Please can you help me? We'll be very grateful
Try the following:
user model
class User < ActiveRecord::Base
has_many :autos
has_one :perfil
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
Auto model
class Auto < ActiveRecord::Base
belongs_to :user
belongs_to :perfil
accepts_nested_attributes_for :perfil, allow_destroy: true
end
Perfil Model
class Perfil < ActiveRecord::Base
belongs_to :user
has_many :autos
end
Auto Controller
class AutosController < ApplicationController
before_action :set_auto, only: [:show, :edit, :update, :destroy]
def new
#auto = Auto.new
#perfil = #auto.build_perfil
end
def create
#auto = Auto.new(auto_params)
#auto.user_id = current_user.id
#auto.perfil_id = current_user.perfil.id
respond_to do |format|
if #auto.save
format.html { redirect_to #auto, notice: 'Auto was successfully created.' }
format.json { render :show, status: :created, location: #auto }
else
format.html { render :new }
format.json { render json: #auto.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #auto.update(auto_params)
format.html { redirect_to #auto, notice: 'Auto was successfully updated.' }
format.json { render :show, status: :ok, location: #auto }
else
format.html { render :edit }
format.json { render json: #auto.errors, status: :unprocessable_entity }
end
end
end
private
def set_auto
#auto = Auto.find(params[:id])
end
def auto_params
params.require(:auto).permit(:perfil_id, :marca, :modelo, :año, :user_id, perfil_attributes: [:user_id, :nombre, :rut, :telefono, :direccion])
end
end
Perfil Controller
class PerfilsController < ApplicationController
before_filter :authenticate_user!
def show
current_user.perfil ||= Perfil.new
#perfil = current_user.perfil
end
def update
#perfil = current_user.perfil
respond_to do |format|
if current_user.perfil.update_attributes(perfil_params)
format.html {redirect_to root_path, notice: "Datos Actualizados" }
else
format.html {render action: "show"}
end
end
end
def create
current_user.perfil ||= Perfil.new
#perfil = current_user.perfil
respond_to do |format|
if current_user.perfil.update_attributes(perfil_params)
format.html {redirect_to root_path, notice: "Datos Actualizados" }
else
format.html {render action: "show"}
end
end
end
private
def perfil_params
params.require(:perfil).permit(:user_id, :nombre, :rut, :telefono, :direccion)
end
end
And _form.html.erb
<%= simple_form_for #auto do |f| %>
<div class="form-inputs">
<%= f.input :marca %>
<%= f.input :modelo %>
<%= f.input :año %>
<%= f.simple_fields_for :perfil, current_user.perfil do |p| %>
<%= p.input :nombre %>
<%= p.input :telefono %>
<%= p.input :direccion %>
<% end %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
<%= link_to "Save", autos_path, :class => 'button_submit' %>
Routes.rb
Rails.application.routes.draw do
resources :autos
devise_for :users
resources :perfils, only: [:index,:show, :update , :create]
root 'autos#index'
end
This is almost perfect, just 2 little things will get you going.
1 The fields_for need only have :perfil as the object
<%= simple_form_for #auto do |f| %>
<div class="form-inputs">
<%= f.input :marca %>
<%= f.input :modelo %>
<%= f.input :año %>
<%= f.simple_fields_for :perfil do |p| %>
<%= p.input :nombre %>
<%= p.input :telefono %>
<%= p.input :direccion %>
<% end %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
<%= link_to "Save", autos_path, :class => 'button_submit' %>
2 The controller needs to create the object as the current user
def create
#auto = current_user.autos.new(auto_params)
respond_to do |format|
if #auto.save
format.html { redirect_to #auto, notice: 'Auto was successfully created.' }
format.json { render :show, status: :created, location: #auto }
else
format.html { render :new }
format.json { render json: #auto.errors, status: :unprocessable_entity }
end
end
end
I am following RailsCast # 196 Nested Model Form Part 1. I have given the same name of controllers as well as model and it's all attributes. But now when I try to go in edit and remove the question. It doesn't delete the question if I select the checkbox.
Like this:
Model:
class Survey < ActiveRecord::Base
has_many :questions, :dependent => :destroy
accepts_nested_attributes_for :questions, :reject_if => lambda {|a| a[:content].blank?} , :allow_destroy => true
end
class Question < ActiveRecord::Base
belongs_to :survey
end
Survey Controller:
class SurveysController < ApplicationController
before_action :set_survey, only: [:show, :edit, :update, :destroy]
def index
#surveys = Survey.all
end
def show
end
def new
#survey = Survey.new
3.times {#survey.questions.build}
end
def edit
end
def create
#survey = Survey.new(survey_params)
respond_to do |format|
if #survey.save
format.html { redirect_to #survey, notice: 'Survey was successfully created.' }
format.json { render :show, status: :created, location: #survey }
else
format.html { render :new }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
def update
#abort params[:survey][:questions_attributes].inspect
respond_to do |format|
if #survey.update(survey_params)
format.html { redirect_to #survey, notice: 'Survey was successfully updated.' }
format.json { render :show, status: :ok, location: #survey }
else
format.html { render :edit }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
def destroy
#survey.destroy
respond_to do |format|
format.html { redirect_to surveys_url, notice: 'Survey was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_survey
#survey = Survey.find(params[:id])
end
def survey_params
params.require(:survey).permit(:name, questions_attributes: [ :id, :content ])
end
end
View File
<%= form_for(#survey) do |f| %>
<% if #survey.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#survey.errors.count, "error") %> prohibited this survey from being saved:</h2>
<ul>
<% #survey.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<%= f.fields_for :questions do |builder| %>
<div class="field">
<%= builder.label :content, 'Question' %><br>
<%= builder.text_area :content, :rows=>3 %><br>
<%= builder.check_box :_destroy %>
<%= builder.label :_destroy, "Remove Question" %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
And As I have commented in update method for abort. If that way I do abort I get
{"0"=>{"content"=>"SEM 1", "_destroy"=>"0", "id"=>"4"}, "1"=>{"content"=>"Sem 2", "_destroy"=>"0", "id"=>"5"}, "2"=>{"content"=>"Sem 3", "_destroy"=>"1", "id"=>"6"}}
Where I do mistake. Please guide me. Thanks in advance.
Add :_destroy to permitted params
def survey_params
params.require(:survey).permit(
:name,
questions_attributes: %i(
id
content
_destroy
survey_id
)
)
end
Also, make sure you have allow_destroy: true in line, where you define accepts_nested_attributes_for :questions
My Rails application have two model. Location and Post, Location have many post.I am Using
ancestry gem.
class Post < ActiveRecord::Base
belongs_to :location, :counter_cache => true
end
class Location < ActiveRecord::Base
include Tree
has_ancestry :cache_depth => true
has_many :posts
end
My Post Controller
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
def index
#posts = Post.all
end
def show
end
def new
#post = Post.new
end
def edit
end
def create
#post = Post.new(post_params)
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render action: 'show', status: :created, location: #post }
else
format.html { render action: 'new' }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #post.update(post_params)
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
def destroy
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
#post = Post.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def post_params
params.require(:post).permit(:name)
end
end
If i am create new Post with which Location belongs in _form.html.erb
<%= form_for(#post) do |f| %>
<% if #post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#post.errors.count, "error") %> prohibited this post from being saved: </h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<%= select :location_id, Location.all.at_depth(4) { |l| [ l.name, l.id ] } %>
<div class="actions">
<%= f.submit %>
</div>
Browser show error message which is display bellow
ArgumentError in Posts#new
Not sure if this fixes your error, but:
To make the dropdown working, change the select line to:
<%= f.select :location_id, Location.all.at_depth(4) { |l| [ l.name, l.id ] } %>
This is because you want the formbuilder f to handle the creation of the form element.
You also have to whitelist the :location_id parameter in the controller:
def post_params
params.require(:post).permit(:name, :location_id)
end