I have rendered the regular scaffold form partial for a task model in my user show view. My thinking been so the user can post and see the post on the same page. I defined a task in the user show action like so
def show
#user = User.find(params[:id])
#task = current_user.tasks.new
respond_to do |format|
format.html # show.html.erb
format.json { render json: #user }
end
end
And it does create the post but it does not show them. Any ideas as to why that is ?
Show page
#_form
<%= form_for(#task) do |f| %>
<% if #task.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#task.errors.count, "error") %> prohibited this task from being saved:</h2>
<ul>
<% #task.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :description %><br />
<%= f.text_field :description %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
#_form
#index page
<% current_user.tasks.each do |task| %>
<%= task.description %>
<% end %>
This is weird because you find a #user, but you use current_user to create a new task instance for your form:
#user = User.find(params[:id])
#task = current_user.tasks.new
I assume current_user already found the user model according to a cookie, session or token so you probably do not need #user in your show action.
With that aside, if you're concerned with the view not displaying the list of the current_user's tasks, then you'll need to make sure the view has the proper markup, hence you should show us what your view currently looks like as well. Here's what I'm assuming you're trying to do:
class UsersController < ApplicationController
def show
#task = current_user.tasks.new
respond_to do |format|
format.html
format.json { render json: current_user }
end
end
end
class TasksController < ApplicationController
def create
#task = current_user.tasks.new params[:task]
if #task.save
# Send a new request to users#show
redirect_to current_user
else
# No request will be sent to users#show and the template will just get
# rendered with #task containing the same values from the initial request
# with form input
render 'users/show'
end
end
end
# app/views/users/show.html.erb
<ul><%= render current_user.tasks %></ul>
<%= form_for #task do |f| %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
# app/views/tasks/_task.html.erb
<li><%= task.name %></li>
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 have an MainPagesController index page that is rendering the 'new' page from QuotesController that has a form. How do I render the MainPagesController index page with the errors of the form?
MainPages/index
<h1>Welcome to Book Quotes</h1>
<p>
Post your favourite quotes from your favourite books
<%= render 'quotes/new' %>
</p>
<%= render 'quotes/all_quotes' %>
Quotes/new
<h1>Add a quote</h1>
<%= render 'quotes/form' %>
Quotes/_form
<%= form_for #quote do |f| %>
<% if #quote.errors.any? %>
<ul>
<% #quote.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<p>
<%= f.label :passage %><br>
<%= f.text_field :passage %>
</p>
<p>
<%= f.label :book_title %><br>
<%= f.text_field :book_title %>
</p>
<p>
<%= f.label :book_author %><br>
<%= f.text_field :book_author %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
QuotesController
def create
#quote = Quote.new(quote_params)
if #quote.save
redirect_to root_url
else
render #not sure what goes here
end
end
Since the form that you're dealing with is a nested form, the standard advice of render :new won't help you here. Instead, you could redirect the user back to the index page, passing the errors via the flash, and update your view to handle displaying those errors.
(Just a thought: it might be worth looking into making this action powered by AJAX. The user experience might be nicer, and it simplifies your code design.)
Anyway, in your QuotesController, the #create action needs to note the errors and pass them along as it redirects the user back to where they came from:
def create
#quote = Quote.new(quote_params)
if #quote.save
redirect_to root_url
else
flash[:quote_errors] = #quote.errors.full_messages
redirect_to :back # or main_pages_path
end
end
Then, your Quotes/_form view needs to handle those errors:
<%= form_for #quote do |f| %>
<% if flash[:quote_errors] %>
<ul>
<% flash[:quote_errors].each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
# ...
Now, this is a bit ugly. You might be wondering -- couldn't we just pass the #quote object back via the flash, so the view doesn't have to change? But while that's technically possible, serializing objects into session is a dangerous path to take. I'd suggest avoiding it.
One other option would be to make the quote submission an action not on QuotesController, but on your MainPages controller. E.g.,
class MainPagesController < ApplicationController
def index
# ...
end
def create_quote
#quote = Quote.new(quote_params) # need to move quote_params in, too
if #quote.save
redirect_to root_url
else
render :index
end
end
# ...
This allows the #quote instance variable to be accessible from your form, so the error handling as-is will work just fine. It's not very RESTful, but then again, neither are most front-end website flows.
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 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