I'm having trouble getting my redirect and error messages to work. From what I've read you cant get a forms errors to show up when you use redirect so I am trying to use render after it fails.
I have a new post form on a topic page. The url is "topic/1". If you make a post about the topic and something is wrong with the input I want it to go back to the page at topic/1 and display errors and I cant figure out how to get it to go back. Redirect (:back) does what I want but doesnt show the forms errors.
The form on the topic's show.html page:
<%= form_for(#post) do |f| %>
<%= render 'shared/post_error_messages' %>
<%= f.label :title, "Post Title" %>
<%= f.text_field :title %>
<%= f.label :content %>
<%= f.text_field :content %>
<%= f.hidden_field :parent_id, value: 0 %>
<%= f.hidden_field :topic_id, value: #topic.id %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.submit "Create Post" , class: "btn btn-small btn-primary" %>
<% end %>
Create action in the Posts controller
def create
#post = Post.new(post_params)
#topic = Topic.find_by(id: params[:topic_id])
if #post.save
redirect_to #post
else
#topic = Topic.new
render "/topics/show"
end
end
I guess I'm mostly trying to do the render with the id from the page that the form was originally on.
Errors
The problem isn't anything to do with the way you're rendering the form (render or redirect) - it's to do with the way you're handling your ActiveRecord object.
When you use form_for, Rails will append any errors into the #active_record_object.errors method. This will allow you to call the following:
form_for error messages in Ruby on Rails
<%= form_for #object do |f| %>
<% #location.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
<% end %>
This only works if you correctly create your ActiveRecord object, which you seem to do
--
Nested
#config/routes.rb
resources :topics do
resources :posts, path: "", path_names: {new: ""}, except: [:index] #-> domain.com/topics/1
end
You'll be much better using the following setup for a nested route:
<%= form_for [#topic, #post] do |f| %>
...
<% end %>
This allows you to create a form which will route to the topics_posts_path, which is basically what you need. The controller will then balance that by using the following:
#app/controllers/topics_controller.rb
Class TopicsController < ApplicationController
def new
#topic = Topic.find params[:topic_id]
#post = Post.new
end
def create
#topic = Topic.find params[:topic_id]
#post = Post.new post_params
end
private
def post_params
params.require(:post).permit(:attributes)
end
end
You are overwriting the Topic you original found with a brand new, empty one - which shouldn't be necessary and which is causing the posts related to it to disappear.
Also - if your topic and post are related - you should create the post on the appropriate association #topic.posts instead of the main Post class.
The #topic.posts.new means that the post's topic-id is automatically updated with the value of the #topic.id ... which means you don't need to set it in the hidden-field on the form.
In fact it's better if you don't - just delete that hidden field entirely.
If you add that to the first time you get a new post too (eg in topics/show) then you won't need to pass in a value to the hidden-field.
Also I'd do the same for all the other hidden-fields on the server-side too. You don't really want the user to use firebug to hack the form and add some other user's id... so do it in the create action and don't bother with the hidden field
This should work:
def create
#topic = Topic.find_by(id: params[:topic_id])
#post = #topic.posts.new(post_params)
#post.user = current_user
#post.parent_id = 0
if #post.save
redirect_to #post
else
render "/topics/show"
end
end
if it doesn't - let me know what error messages you get (so we can debug)
Related
I have Comment belongs_to Post and Post has_many Comments, the comment model as following:
class Comment < ApplicationRecord
belongs_to :post
belongs_to :user
validates :text, presence: true
end
The form which adds new comments is located in Posts show view, as following:
<%= form_with(model: [ #post, #post.comments.build ], local: true) do |form| %>
<% if #comment.errors.any?%>
<div id="error_explanation">
<ul>
<% #comment.errors.messages.values.each do |msg| %>
<%msg.each do |m| %>
<li><%= m %></li>
<%end %>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= form.text_area :text , {placeholder: true}%>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
The Comments create action, as following:
class CommentsController < ApplicationController
def create
#post = Post.find(params[:post_id])
#comment = Comment.new(comment_params)
#comment.post_id = params[:post_id]
#comment.user_id = current_user.id
if #comment.save
redirect_to post_path(#post)
else
render 'posts/show'
end
end
private
def comment_params
params.require(:comment).permit(:text)
end
end
I need to render the posts/show page to show Comment validation errors, but the problem is I'm in CommentsController controller not PostsController so all objects used in pages/show view will be null.
How can I pass the #comment object to pages/show?
I thought about using flash array, but I'm looking for more conventional way.
Yep, rendering pages/show will just use the view template, but not the action, so anything you define in the pages#show controller action won't be available. #comment will be available though as you define that in the comments#create action. Without seeing the PostsController I don't know what else you're loading in the pages#show action - you could consider moving anything required in both to a method on ApplicationController, then calling from both places. Another option would be to change your commenting process to work via AJAX (remote: true instead of local: true on the form), and responding with JS designed to re-render just the comment form (you can move it into a partial used both in pages/show.html.erb and the comments#create response).
Couple of other notes on your code above - in comments#create, you can use:
#comment = #post.comments.new(comment_params)
to avoid needing to set the post_id on #comment manually.
For the form, I'd be tempted to setup a new comment in pages#show:
#comment = #post.comments.build
And then reference that in the form, it'll make it easier if you do re-use that between pages#show and comments#create:
<%= form_with(model: [ #post, #comment ], local: true) do |form| %>
Hope that helps!
I have a basic blogging site in which I want to allow users to offer 'corrections' to posts (just think of it as a comment). The correction object belongs to a post, which in turn belongs to a user (for which I'm using Devise).
I would like the form to create a new correction to be nested in the page for the post,so I'm just rendering the form in posts/show.html.erb with <% render :template => "corrections/new" %>. I'm getting a 'undefined method model_name' error from the line form_for line in corrections/new.html.erb though.
Here's the form:
<% form_for [#correction, :url=> user_post_corrections_path(current_user, #post, #correction)], html: { multipart: true} do |f| %>
<div class="field">
<%= f.label :correction %>
<%= f.text_field :correction %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Here's the corrections_controller:
class CorrectionsController < ApplicationController
def new
#post = Post.find(params[:post_id])
#correction = current_user.#post.corrections.build
respond_with(#correction)
end
def create
#post = Post.find(params[:post_id])
#correction = current_user.#post.corrections.build
if #correction.save
redirect_to user_post_path(current_user, #correction.post_id)
end
end
end
In my routes.rb:
resources :users do
resources :posts do
resources :corrections
end
end
Any help with this would be much appreciated.
There are several issues I can see, first off:
The form is not rendered:
The form for should be evaluated not computed so you should use <%= %> tag instead of <%%> tag, so it becomes:
<%= form_for #correction, url: user_post_corrections_path(current_user, #post, #correction), html: { multipart: true} do |f| %>
Second: You are saying form_for #correction, and you said this form is shown in posts/show, this means that the show action for the post should have the following in its controller:
#correction = Correction.new user: current_user, post: #post
This is assuming you are using the #post variable for the post show meaning you have something like
#post = Post.find params[:id]
This line is completely off
#correction = current_user.#post.corrections.build
you can just say:
#correction = Correction.new correction_params
#correction.user = current_user
#correction.post = #post
if #correction.save
redirect_to user_post_path(current_user, #correction.post_id)
end
The important part is your show action on the posts controller having the initialization of your #correction object.
This line looks off:
#correction = current_user.#post.corrections.build
#post is an instance variable, not a method on current_user
Did you intend to build a correct with the current user? The following should work:
#post.corrections.build(user: current_user)
If your corrections model has_many :users, through: :posts, then the following should work:
#post.corrections.build(users: [current_user])
EDIT
The format of your form_for is also incorrect. The url: key in form_for doesn't belong in the resource array (the first arg in your form_for)
The following modification should resolve the error, provided user_post_corrections_path takes :id, :post_id, and :correction_id
form_for #correction, url: user_post_corrections_path(current_user, #post, #correction), html: { multipart: true} do |f|
I'd also reduce your routing to two levels of nesting if possible.
Perhaps this is easier?
resources :posts do
resources :corrections
end
end
If it's implied a post belongs to the current_user then it may not be necessary to have /users in the route path.
My problem is in rendering error messages, when #post.save returns false. I don't want use flash messages.
I have two resourses:
config/routes.rb
resources :users
resources :posts, only: [:create, :destroy]
User can have many microposts on his personal page (e.g. users/1). And I need to add ability of creating microposts from user's show view. Creating with valid information works well, but I want to display error messages from model Post in another way.
app/controllers/posts_controller.rb
...
def create
#post = current_user.posts.build(post_params)
if #post.save
redirect_to user_path(current_user)
else
#?????
render ???
end
end
app/views/users/show
...
<%= form_for(#post) do |form| %>
<%= render 'shared/error_messages', object: form.object %>
<%= form.text_area :text, placeholder: "Что у Вас нового?" %>
<%= form.submit "Добавить" %>
<% end %>
In partial I am using object.errors.full_messages. Any ideas?
I've been told that I should not create my Quiz object before my quiz is completed; A user could go to the quiz page, not complete it, and there would be an 'unused' quiz sitting on the database. I can see the logic of that.
I CAN'T see how my quiz is supposed to work without being passed a #quiz object. Here's my QuizzesController, which, when the quiz is needed, gets routed to the 'new' action:
class QuizzesController < ApplicationController
def index
end
def new
#user = current_user
#quiz = Quiz.create(user_id: current_user.id)
end
def create
#results = Quiz.where(user_id: current_user.id).last
redirect_to results_path
end
end
At the moment, you can see that I'm coding the actions as simply as possible. Later, in the 'new' action, I'll add a test to see if the current_user has done the quiz and, if so, redirect to results_path.
Here is my form partial which is rendered as part of quizzes/new.html.erb:
<%= form_for(#quiz) do |f| %>
<p>
<%= f.check_box(:answer1) %>
<%= f.check_box(:answer2) %>
<%= f.check_box(:answer3) %>
<%= f.check_box(:answer4) %>
<%= f.check_box(:answer5) %>
<%= f.check_box(:answer6) %>
<%= f.check_box(:answer7) %>
<%= f.check_box(:answer8) %>
</p>
<p>
<%= f.submit("Get my results!") %>
</p>
<% end %>
Once again, the quiz is very simple while I figure out what's going on.
But I'd like to know, if the #quiz object is not created in the 'new' action, what would I pass into form_for to build the form?
You can instantiate a Quiz object without saving it to the database:
def new
#user = current_user
#quiz = Quiz.new(user_id: current_user.id)
end
The generally used sequence of requests/actions is the following:
The new action just initializes the model's instance with default values, and renders the record with empty fields, usually in a edit view.
def new
#quiz = Quiz.new(user_id: current_user.id)
render :edit
end
create action create the record, and after the create action you should render either the view of the newly created record by redirection to show action with the same view, or to redirect to a new action, in case you are creating a sequence of the same instances of a model.
def create
#quiz = Quiz.create(params)
render :show # or redirect_to :new
end
edit action is to prepare edit fields, is it renders edit view with filled-in fields.
def edit
#quiz = Quiz.where(id: params[:id]).first
end
update action updates the record with values set in edit view, then it renders the show view on the current record.
def update
#quiz = Quiz.update(params)
render :show
end
show action just shows the model's found out with stored in the DB values, then it renders show view with filled-in fields.
def show
#quiz = Quiz.where(id: params[:id]).first
end
So in your show.erb view you get rendering the newly built, or found out instance of Quiz:
<%= form_for #quiz, url: {action: "create"} do |f| %>
<p>
<%= f.check_box(:answer1) %>
<%# ... %>
</p>
<p>
<%= f.submit "Create Quiz" %>
</p>
<% end %>
But I prefer simple-form gem:
<%= simple_form_for #quiz do |f| %>
<%= f.input :answer1, as: :boolean, checked_value: true, unchecked_value: false %>
<%= f.button :submit %>
<% end %>
I am new to Rails and working on creating a generic "facebook" type of app as practice with users and posts associated with each user. However, I'm currently having an issue where I think the form that I am using to create the posts is also being rendered out as a blank post with no post ID where I display all of the posts in a section below. I think that this post is being shown even before it is being saved to the database.
Here is my code in my view:
<div class="newpostcontainer">
<div class="newposttext">
<%= form_for([#user, #user.posts.build]) do |f| %>
<%= f.text_area :post, size: "69x1" %>
</div>
<div class="newpostsubmitbutton">
<%= f.submit %>
</div>
<% end %>
</div>
<% #user.posts.reverse_each do |p| %>
<div class="postedcontainer">
<div class="minipostpic">
<%= image_tag #user.photo.url, width: 32, height: 32 %>
</div>
<div class="nameofposter"><%= #user.name %></div>
<div class="dateofpost"><%= p.created_at%></div>
<div class="postcontent"><%= p.id%></div> <br>
<div class="postcontent"><%= p.post%></div> <br>
<div class="likecommentdelete">
<%= link_to "Delete", [p.user, p], method: :delete %> | Like | Comment
</div>
</div>
<%end%>
</div>
Here is my controller:
def index
#user = User.find(params[:user_id])
#posts = #user.posts.all
end
def create
#user = User.find(params[:user_id])
#post = #user.posts.create!(post_params)
redirect_to user_path(#user)
end
def show
#user = User.find(params[:user_id])
#post = #user.posts.find(params[:id])
redirect_to user_path(#user)
end
def destroy
#user = User.find(params[:user_id])
#post = #user.posts.find(params[:id])
#post.destroy
if #post.destroy
redirect_to user_path(#user)
else
redirect_to users_path
end
end
private
def post_params
params.require(:post).permit!
end
end
And here is my model:
class Post < ActiveRecord::Base
belongs_to :user
has_many :comments
validates_presence_of :post
end
I'm pretty sure the issue has something to do with my form to create the new post because when I remove it or comment it out, the extra blank post with no post ID goes away.
Any thoughts or suggestions?
Thank you!!
I think you need to permit the field values to be posted:
i.e.,
params.require(:post).permit!
should be
params.require(:post).permit(:name, :post)
then only it will POST I think.
Hope it helps :)
This is because of rails 4 strong parameter feature. You need to whitelist your active models parameters. For more details refer to here.
In your case you need to do something like this:
params.require(:post).permit(:post)
where the ":post" inside require is your model and the other one is your permitted field that is your textarea.
Several issues -
Form
<%= form_for([#user, #user.posts.build]) do |f| %>
Why are you building an associative object? #user.posts.build will not persist your data, and will cause all sorts of non-conventional issues I would highly recommending building the posts associative object in your controller's new action before using in the view, so you can do this:
#app/controllers/users_controller.rb
def new
#user = current_user
#user.posts.build
end
<%= form_for #user do |f| %>
Association
You're trying to edit the post attribute with this statement:
<%= f.text_area :post, size: "69x1" %>
This won't work in any circumstance, as :post is an association, not an object. Rails only allows you to change / add attributes to specific objects, which means you'll be better doing something like this:
<%= f.fields_for :posts do |p| %>
<%= p.text_area :title %>
<%= p.text_area :body %>
<% end %>
Strong Params
You're currently permitting all your params? You'll be better doing this:
def post_params
params.require(:user).permit(posts_attributes: [:title, :body])
end
Use Posts Controller
A better way will be to just use the posts_controller, like this:
#app/controllers/posts_controller.rb
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
#post.save
end
#app/views/posts/new.html.erb
<%= form_for #post do |f| %>
<%= f.text_field :title %>
<%= f.text_field :body %>
<% end %>