I am going through the "Getting Started with Rails Tutorial" and am stuck on update aka Edit. It is throwing the ArgumentError in Posts#edit - first argument in form can't be nil or empty. Here is the highlighted line:
First argument in form cannot contain nil or be empty
Extracted source (around line #1):
<%= form_for #post do |f| %>
It seems to have started when I implemented the partial forms part of the tutorial.
Here is the post_contoller, edit action and _forms.html respectively:
Post_controller:
class PostsController < ApplicationController
def new
#post = Post.new
end
def create
#post = Post.new(params[:post].permit(:title, :text))
if #post.save
redirect_to #post
else
render 'new'
end
end
def show
#post = Post.find(params[:id])
end
def index
#posts = Post.all
end
def update
#post = Post.find(params[:id])
if #post.update(params[:post].permit(:title, :text))
redirect_to #post
else
render 'edit'
end
end
def destroy
#post = Post.find(params[:id])
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :text)
end
end
Edit.html
<h1>Edit post</h1>
<%= render 'form' %>
<%= link_to 'Back', posts_path %>
_form.html
<%= 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 %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
The error shows an ID of "7" which is the record I am trying to update.
All other functions work (show, new, delete) and BTW "new" uses the same partial form and works fine.
Any help would be much appreciated. Thanks!
U should add this in your _controller.rb
def edit
#post = Post.find(params[:id])
end
you have to pass in the post object as local. You cannot directly access instance variables defined in controller in partial. It will be nil
<h1>Edit post</h1>
<%= render :partial => 'form', :locals => {:post => #post} %>
<%= link_to 'Back', posts_path %>
In the partial
<%= form_for post do |f| %>
check below the private is used for the below function only and write all functions above this:
private
def article_params
params.require(:article).permit(:title, :text)
end
Related
I've been following the rails blog tutorial (you know, that one ) and i've come to a point where every time i reference #articles on the update form, rails takes it as a nil, it says:
First argument in form cannot contain nil or be empty
Here's the form
<h1>Edit article</h1>
<%= form_for #article do |f| %>
<% if #article.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#article.errors.count, "error") %> prohibited
this article from being saved:
</h2>
<ul>
<% #article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to 'Back', articles_path %>
And the controller for the articles:
class ArticlesController < ApplicationController
def new
#article = Article.new
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
def show
#article = Article.find(params[:id])
end
def index
#articles = Article.all
end
def update
#article = Article.find(params[:id])
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
It does not look like you have an edit method in your Articles controller.
def edit
#article = Article.find(params[:id])
end
Just so it is clear. The edit method is what is called with the GET route that shows the form. Update is the PATCH/PUT route that takes the form in and updates the record. So, the form is shown by the edit method via GET, and processed by the update method via PUT/PATCH.
I can see in my console that the erorr_messages partial I made is getting rendered, and if a comment does not pass validations, then it will not be posted, but I can't get the actual error contents to render.
Error Partial:
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(object.errors.count, "error") %>
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
Comment form
<%= form_for #comment, url: comments_path do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.hidden_field :post_id, value: post.id %>
<%= f.text_area :content, size: "60x2", placeholder: "Comment on this post..." %>
<%= f.submit "Comment" %>
Post Form
<%= form_for [#user, #post] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_area :content, size: "60x12", placeholder: "What do you want to say?" %>
<%= f.submit "Post" %>
Users/show
<% if #user == current_user %>
<h4>Welcome <%= current_user.email %>! </h4>
<%= render "notifications" %>
<%= render 'shared/post_form' %>
<%= render 'feed' %>
<% end %>
class CommentsController < ApplicationController
def index
#comments = Comment.all
end
def new
#comment = Comment.new
#user = User.find(params[:user_id])
end
def create
#user = current_user
#comment = #user.comments.build(comment_params)
if #comment.save
flash[:success] = "Comment Posted!"
redirect_back(fallback_location: root_path)
else
flash[:danger] = "Could not post comment"
redirect_back(fallback_location: root_path)
end
end
private
def comment_params
params.require(:comment).permit(:content, :user_id, :post_id)
end
end
class PostsController < ApplicationController
def index
#posts = Post.all
#user = User.find(params[:user_id])
#comment = Comment.new
end
def new
#post = Post.new
#user = User.find(params[:user_id])
end
def create
#post = current_user.posts.build(post_params)
if #post.save
flash[:success] = "Posted!"
redirect_to user_path(current_user)
else
flash[:danger] = "Post could not be submitted"
redirect_to users_path
end
end
private
def post_params
params.require(:post).permit(:content)
end
end
In your CommentsController#create, when the save fails, rather than redirecting:
redirect_back(fallback_location: root_path)
Try staying on the same page and just rendering the "new" template:
render action: "new"
If you redirect, the browser will make a second request and #comment will get overwritten with a freshly-built Comment.
If you stay on the same page and render the "new" template, it will use the #comment instance that's already loaded and which failed to save (this instance has all the validation errors set on it).
P.S. the flash message works because that's what flash is for - a way to store messages in your session so that they survive across redirects.
I am trying to to edit a form through a reveal-modal. So i have multiple posts.
When I click the link 'Edit' it will reveal a div and display a form inside that is repopulated with the information. However, I don't know how to pass the post id into the form it will know which post to edit.
If i try to render the partial form I get this error:
undefined method `model_name' for Post::ActiveRecord_Relation:Class
All my information are on a controller view that is called Dashboard
controllers/dashboards_controller.rb
class DashboardsController < ApplicationController
def index
#profile = Profile.find(current_user)
#profileAll = Profile.all
#post = Post.all
end
def show
end
def edit
#profile =Profile.find(current_user)
#post = #profile.post.find(params[:id])
end
end
controllers/posts_controller.rb
class PostsController < ApplicationController
def create
#profile = Profile.find(current_user)
#post = #profile.post.create(post_params)
redirect_to dashboards_path(current_user)
end
def destroy
#post =Profile.find(params[:profile_id])
#post = profile.post.find(params[:id])
#post.destroy
redirect_to dashboards_path(current_user)
end
def show
#profile =Profile.find(current_user)
#post = #profile.post.find(params[:id])
end
def edit
#profile =Profile.find(current_user)
#post = #profile.post.find(params[:id])
end
def update
#post = profile.post.find(params[:id])
if #post.update(post_params)
redirect_to dashboards_path(current_user)
else
render 'edit'
end
end
private
def post_params
params.require(:post).permit(:title, :content)
end
end
posts/_form2.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 %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :content %><br>
<%= f.text_area :content %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
routes.rb
Rails.application.routes.draw do
get 'homepages/index'
resources 'dashboards' do
resources 'posts'
end
#get 'users/sign_in'
resources 'profiles' do
resources 'posts'
end
devise_for :users, :controller => {:registrations => "users/registrations"}
devise_scope :user do
root :to => 'devise/sessions#new'
end
end
Dashboards/index.html.erb
<% #post.each do |post| %>
<div class="panel">
<%= #profileAll.find(post.profile_id).name %>
<hr>
<h5><%= post.title %></h5>
<p><%= post.content %></p>
<%# link_to "Edit", edit_dashboard_post_path(current_user,[post.profile, post]) %>
<%= link_to "Edit", {id: #post},'data-reveal-id' => 'edit-post' %>
</div>
<% end %>
<div id="edit-post" class="reveal-modal" data-reveal>
<%= render 'posts/form2' %>
<a class="close-reveal-modal">×</a>
</div>
Well, it simply does not work that way. First, you have in your controller #post = Post.all and within your _form2.html.erb you want to render form for a collection. That should be:
# index.html.erb
<div id="edit-post" class="reveal-modal" data-reveal>
<%= render :partial => 'posts/form2', collection: #posts %>
<a class="close-reveal-modal">×</a>
</div>
#dashboard controller:
def index
#...
#posts = Post.all
end
_form2.html.erb:
Every #post object instance has to be replaced with form2. Read more here: http://api.rubyonrails.org/classes/ActionView/PartialRenderer.html about rendering a collection.
However, consider a case when you have hundreds of posts. Then, for each post, a partial is rendered. That would be very inefficient so instead, consider an asynchronous request which will load only one post the user requested.
I'm doing the Ruby on Rails tutorial at http://guides.rubyonrails.org/getting_started.html
However when I get to "5.11 Adding Some Validation", after adding the error message to display when I try to create a new post from the index view I get
"undefined method `errors' for nil:NilClass"
This is the error line highlighted.
I have already created a new Post in my new action in the controller.
Here is the source
new.html.erb
<h1>New Post</h1>
<%= form_for :post, url: posts_path 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 %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
posts_controller.rb
class PostsController < ApplicationController
def new
#Post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
def show
#post= Post.find(params[:id])
end
def index
#posts = Post.all
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
private
def post_params
params.require(:post).permit(:title, :text)
end
end
Thanks.
Change
#Post = Post.new
To
#post = Post.new
Subtle but deadly
You've got error in your controller:
def new
#Post = Post.new
end
just change #Post to #post.
I got two models: Post and Comment. Comment is a nested resource of Post:
routes.rb:
resources :posts do
resources :comments
end
I'm enabling users to edit the comments that are displayed in the post show view:
posts/show.hmtl.erb:
<%= render #comments %>
comments/_comment.html.erb:
<%= link_to "Edit Post Comment", edit_post_comment_path(#post, comment) %>
This form:
comments/_form.html.erb:
<h4>Add a comment:</h4>
<%= form_for([#post, #post.comments.build]) do |f| %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_area :content %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<% unless current_user == nil %>
<% if current_user.id == #post.user_id %>
<%= link_to 'Edit', edit_post_path(#post) %> |
<% end %>
<% end %>
<%= link_to 'Back', posts_path %>
Is for creating a comment in the show view.
I need another form to edit the comment in a new template:
comments/edit.html.erb:
<h1>Edit comment</h1>
<%= render 'form2' %>
<%= link_to 'Back', posts_path %>
comments/form2.html.erb:
<h4>Edit comment:</h4>
<%= form_for() do |f| %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_area :content %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<%= link_to 'Back', posts_path %>
Not sure what to place here:
<%= form_for(HERE) do |f| %>
Any suggestions?
EDIT
comments_controller.rb:
class CommentsController < ApplicationController
def show
#comment = Comment.find(params[:id])
end
def new
#comment = Comment.new
end
def edit
#comment = Comment.find(params[:id])
end
def update
#post = Post.find(params[:post_id])
#comment = Comment.find(params[:id])
#comment.update_attributes(params[:comment])
redirect_to #post
end
def create
#post = Post.find(params[:post_id])
comment_attr = params[:comment].merge :user_id => current_user.id
#comment = #post.comments.create(comment_attr)
redirect_to post_path(#post)
end
end
Comments are nested resources and that means a comment is always tied to a particular Post. In the edit action you'll get both comment_id and post_id as parameters. You should load both comment and post by those id's. Don't build the comment in the form to reuse the form for both new and edit actions. Change your form to:
comments/_form.html.erb
<%= form_for([#post, #comment]) do |f| %>
comments_controller.rb
def edit
#comment = Comment.find(params[:id])
#post = Post.find(params[:post_id])
end
and in the posts_controller.rb
def show
#post = Post.find(params[:id])
#comment = #post.comments.build
end