I have tried multiple methods to show error message from views, but it is not appearing.
<%= 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 %>
_form.html.erb
<center>
<h1>New article</h1>
<%= render 'form' %>
<%= link_to 'Back', articles_path %>
</center>
new.html.erb
class Article < ActiveRecord::Base
#attr_accessible :title, :text
has_many :comments, dependent: :destroy
validates :title, presence: true
end
article.rb
Rails.application.routes.draw do
root "articles#index"
resources :articles do
resources :comments
end
end
routes.rb
class ArticlesController < ApplicationController
http_basic_authenticate_with name: "deba", password: "12345", except: [:index, :show]
def index
#articles = Article.all
end
def new
#article = Article.new
end
def show
#article = Article.find(params[:id])
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new' ##article.errors, status: :unprocessable_entity
end
end
def edit
#article = Article.find(params[:id])
end
def update
#article = Article.find(params[:id])
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
end
def destroy
#article = Article.find(params[:id])
#article.destroy
redirect_to articles_path
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
articles_controller.rb
Kindly help me. I tried it, validation is working as I can check that if there is no title it redirect me to new page without saving it but not appearing the error messages.
The problem is with your redirecting - it causes the browser to initiate a new request and then you lose all the data (including the instance variables where the error is stored). Instead of redirecting you should simply render the "new" partial when article is invalid.
You haven't posted your controller code but this should give you a direction
if #article.save
# whatever you wanna do if its valid
else
render "new" # render the view without redirecting
end
try to edit your controller:
class ArticlesController < ApplicationController
before_action :initialize_article, only: [:new, :create]
...
def new
end
def create
if #article.update(article_params)
redirect_to #article
else
render :new
end
end
....
private
def initialize_article
#article ||= Article.new
end
end
Related
Full source code is here https://github.com/tenzan/postfile
Creating a post working fine.
I have a parent element "Conversation" and its child/nested element "Post".
When I click on "Create Post" with nothing entered, it should throw an error "Body can't be blank".
Instead, it giving another error:
conversation.rb:
class Conversation < ApplicationRecord
belongs_to :contact
has_many :posts
end
post.rb:
class Post < ApplicationRecord
belongs_to :conversation
belongs_to :author, polymorphic: true
has_rich_text :body
validates :body, presence: :true
end
posts_controller.rb:
class PostsController < ApplicationController
before_action :authenticate_user!
before_action :set_conversation
def create
#post = #conversation.posts.new(post_params)
#post.author = current_user
respond_to do |format|
if #post.save
format.html { redirect_to #conversation }
end
end
end
private
def set_conversation
#conversation = Conversation.find(params[:conversation_id])
end
def post_params
params.require(:post).permit(:body)
end
end
I show all posts within from conversation's show.html.erb:
<p id="notice"><%= notice %></p>
<p>
<strong>Subject:</strong>
<%= #conversation.subject %>
</p>
<p>
<strong>Contact:</strong>
<%= link_to #conversation.contact.name, #conversation.contact %>
</p>
<%= link_to 'Edit', edit_conversation_path(#conversation) %> |
<%= link_to 'Back', conversations_path %>
<div id="posts">
<%= render #posts %>
</div>
<%= render partial: "posts/form", locals: { conversation: #conversation, post: Post.new } %>
Posts's partial _form.html.erb:
<%= form_with model: [conversation, post], id: "form" do |form| %>
<div>
<% form.object.errors.full_messages.each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<br>
<%= form.rich_text_area :body %>
<%= form.submit %>
<% end %>
Full source code is here https://github.com/tenzan/postfile
Thanks in advance.
You have this block in your posts_controller, which is where your error is arising:
respond_to do |format|
if #post.save
format.html { redirect_to #conversation }
end
end
Inside a respond_to block, you should have blocks identified by the format type, but you've added an if statement at that top level of the block where Rails is expecting a format.xxx. Move the if outside your respond_to block and you should be fine:
if #post.save
respond_to do |format|
format.html { redirect_to #conversation }
end
else
DO SOMETHING WITH THE ERROR
end
(Also NB that you should handle the error if the post doesn't save, even if it's just to say "Sorry, please try again".)
Following the example guides.rubyonrails.org/getting_started.html I receive an error undefined method `articles' for nil:NilClass in attempt of addition of article on the page.
routes.rb
Rails.application.routes.draw do
root :to => redirect('/pages/1')
resources :articles
resources :pages do
resources :articles
end
views/articles/new.html.erb
<h1>New Article</h1>
<%= form_for([#page, #page.articles.build]) do |f| %>
<p>
<%= f.label :item %><br>
<%= f.text_field :item %>
</p>
<p>
<%= f.label :description %><br>
<%= f.text_area :description %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
articles_controller.rb
class ArticlesController < ApplicationController
def new
#article = Article.new
end
def edit
#article = Article.find(params[:id])
end
def create
#page = Page.find(params[:page_id])
#article = #page.articles.create(article_params)
redirect_to root_path if #article.save
end
private
def article_params
params.require(:article).permit(:item, :description)
end
end
What am I doing wrong?
You aren't defining #page in your new action. You need to add something similar to what you've done in create to the new action (and probably the edit action as well).
before_action :load_page
...
protected
def load_page
#page ||= Page.find(params[:page_id])
end
I have a many_to_many association between Categories and Articles, using has_and_belongs_to_many in a Rails 4 app:
Here are the corresponding migration and classes:
class CategoriesArticles < ActiveRecord::Migration
def change
create_table :categories_articles, id: false do |t|
t.belongs_to :category, index: true
t.belongs_to :article, index: true
end
add_index :categories_articles, [:category_id, :article_id]
end
end
class Category < ActiveRecord::Base
has_and_belongs_to_many :articles
end
class Article < ActiveRecord::Base
has_and_belongs_to_many :categories
end
When a user creates a new article, I simply want to give him or her the option to select categories that he/she wants to associate with the new article. I want the user to be able to select these categories with checkboxes.
Here's the ArticlesController:
class ArticlesController < ApplicationController
before_action :set_article, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, only: [:new, :create, :edit, :destroy, :update]
before_action :verify_own_article, only: [:destroy]
respond_to :html
def index
if user_signed_in?
#articles = current_user.article_feed
# TODO: if there are too few articles on a user's feed, we want to display more articles
else
#articles = Article.all
end
#articles = #articles.page(params[:page] || 1).per(12)
respond_with(#articles)
end
def show
respond_with(#article)
end
def new
#categories = Category.all
#article = Article.new
respond_with(#article)
end
def edit
if current_user.articles.find_by_id(params[:id]).nil?
flash[:notice] = "You do not have permission to edit this article."
redirect_to #article
end
end
def create
# Creates article object with current_user_id, initial_comment, and URL
#article = current_user.articles.build(article_params)
# Uses Pismo (gem) to grab title, content, photo of URL
#article.populate_url_fields
if #article.save
flash[:success] = "Article created!"
# Might need to change the location of this redirect
redirect_to root_url
else
flash[:notice] = "Invalid article."
redirect_to new_article_path
end
end
def update
#article.update(article_params)
flash[:notice] = "Article successfully updated."
respond_with(#article)
end
def destroy
if #article
#article.destroy
flash[:notice] = "Article successfully destroyed."
else
flash[:notice] = "You do not have permission to delete this article."
end
# TODO: change this to another redirect location
redirect_to root_path
end
private
def set_article
#article = Article.find(params[:id])
end
def article_params
params.require(:article).permit(:url, :title, :datetime, :content, :photo, :initial_comment)
end
# Ensure that a signed in user can only delete articles that they have posted
def verify_own_article
#article = current_user.articles.find_by_id(params[:id])
end
end
Here's the article new.html.erb view:
<h1>New article</h1>
<%= render 'form' %>
<%= link_to 'Back', articles_path %>
... and the form partial:
<%= 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 |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :url %><br>
<%= f.text_field :url %>
</div>
<div class="field">
<%= f.label :initial_comment %><br>
<%= f.text_field :initial_comment %>
</div>
<% #categories.each do |t| %>
<div class="field">
<%= f.label t.name %>
<%= f.check_box "categories[#{t.id}]" %>
<br />
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
However, this is erroring for me, specifically the lines:
<% #categories.each do |t| %>
<div class="field">
<%= f.label t.name %>
<%= f.check_box "categories[#{t.id}]" %>
<br />
</div>
<% end %>
Specifically, it's telling me:
undefined method 'categories[1]' for #<Article:0x007f401193d520>. How do I fix this? Thanks.
I note your join table is named
create_table :categories_articles, id: false do |t|
The name needs to be in alphabetical order.
create_table :articles_categories, id: false do |t|
My pick is that Rails cannot find your join table. This means that the form when it calls #article.categories cannot find what it needs. The same will happen if you call #category.articles (ie: being able to find the join table is not related to the arbitrary order of the objects).
See my answer to this question
I generate 3 models: "User", "Article" and "Comment", and the "Comment" model have foreign key "user_id" and "article_id". However I can't automatically add "article_id" when I want to create an comment to a specific article in view.
In rails console, I can add it successfully by using
comment = Comment.new(:content => "Great post")
comment.user = user
comment.article = Article.find(1)
comment.save
I tried to write some code in my controller, but is doesn't work
articles_controller.rb
class ArticlesController < ApplicationController
before_filter :signed_in_user, only: [:create, :destroy]
before_filter :correct_user, only: :destroy
def index
#articles = Article.all
end
def show
#article = Article.find(params[:id])
#comment = Comment.new()
end
def create
#article = current_user.articles.build(params[:article])
if #article.save
flash[:success] = "Article created!"
redirect_to root_url
else
render 'static_pages/home'
end
end
def destroy
#article.destroy
redirect_to current_user
end
private
def correct_user
#article = current_user.articles.find_by_id(params[:id])
redirect_to root_url if #article.nil?
end
end
comments_controller.rb
class CommentsController < ApplicationController
before_filter :signed_in_user, only: [:create, :destroy]
before_filter :correct_user, only: :destroy
def create
#comment = current_user.comments.build(params[:comment])
if #comment.save
flash[:success] = "Comment created!"
redirect_to articles_url
else
render 'static_pages/home'
end
end
def destroy
#comment.destroy
redirect_to current_user
end
private
def correct_user
#comment = current_user.comments.find_by_id(params[:id])
redirect_to root_url if #comment.nil?
end
end
in view files
/articles/show.html file render a partial file "_comment_form.html.erb"
_comment_form.html.erb
<%= form_for(#comment) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose your comment..." %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
I can add some new lines to manually add article_id to the comments table. But it is not a good way.
<% #comment.article_id = #article.id %>
<%= form_for(#comment) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :article_id %>
<%= f.text_field :article_id %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose your comment..." %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
How can I do?
Thank you so much.
changed _comment_form.html.erb
# views/comments/_comment_form.html.erb
<%= form_for[:articles, #comment] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose your comment..." %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
The error:
SyntaxError in Articles#show
Showing F:/RailsProject/project/gamespace/app/views/comments/_comment_form.html.erb where line #2 raised:
F:/RailsProject/project/gamespace/app/views/comments/_comment_form.html.erb:2: syntax error, unexpected keyword_do_block, expecting keyword_end
...orm_for[:articles, #comment] do |f| #output_buffer.safe_conc...
... ^
F:/RailsProject/project/gamespace/app/views/comments/_comment_form.html.erb:9: syntax error, unexpected keyword_ensure, expecting $end
Extracted source (around line #2):
1:
2: <%= form_for[:articles, #comment] do |f| %>
3: <%= render 'shared/error_messages', object: f.object %>
4: <div class="field">
5: <%= f.text_area :content, placeholder: "Compose your comment..." %>
# comments_controller.rb
class CommentsController < ApplicationController
before_filter :signed_in_user, only: [:create, :destroy]
before_filter :correct_user, only: :destroy
def create
#comment = Comment.new(comment_params)
#comment.article_id = params[:id]
if #comment.save
flash[:success] = "Comment created!"
redirect_to articles_url
else
render 'static_pages/home'
end
end
def destroy
#comment.destroy
redirect_to current_user
end
private
def correct_user
#comment = current_user.comments.find_by_id(params[:id])
redirect_to root_url if #comment.nil?
end
def comment_params
params.require(:comment).permit(:content).merge(:user_id => current_user.id, :article_id => params[:id])
end
end
Error:
TypeError in CommentsController#create
can't convert Symbol into String
Rails.root: F:/RailsProject/project/gamespace
Application Trace | Framework Trace | Full Trace
app/controllers/comments_controller.rb:33:in `comment_params'
app/controllers/comments_controller.rb:7:in `create'
This error occurred while loading the following files:
comment
Request
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"ybkPs+c2068AZIIqCNJz1epBtS4L1zgT/vU9LL2Fs+E=",
"comment"=>{"content"=>"sdfadfa"},
"commit"=>"Post",
"article_id"=>"10"}
-------------------------------------------------------------------------------
Solution:
# comments_controller.rb
def create
comment = current_user.comments.build(params[:comment])
comment.article = Article.find(params[:article_id])
comment.save
end
use hidden_field method
# views/_comments_form.html.erb
<%= form_for [#article, #comment] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.hidden_field :article_id, :value => #article.id %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose your comment..." %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
I was going to write a long post here, but I actually agree with Monk_code - you'll be better to inject the foreign_key into the new record rather than playing around with any tricks
#app/controllers/comments_controller.rb
def create
comment = Comment.new(comment_params)
comment.article_id = params[:id]
comment.save
end
OR
#app/controllers/comments_controller.rb
def create
comment = Comment.new(comment_params)
comment.save
end
private
def comment_params
params.require(:comment).permit(:content).merge(:user_id => current_user.id, :article_id => params[:id])
end
Associative Data
The problem I can see is that your model will probably look like this:
#app/models/comment.rb
Class Comment > ActiveRecord::Base
belongs_to :article
belongs_to :user
end
If this is the case, you have to remember that you're saving data which is dependent on a parent model. As it's a dependency, it will not automatically know the foreign key, meaning you'll have to assign it yourself
If you were saving a new Article, you'll be able to pass accepts_nested_attributes_for or even just create a comment record after_create
Correctly Designing Your Application
My solution is to use the params[:id] you'll likely have in place as a result of using
resources :articles do
resources :comments
end
This means every comment will have to be created on an article's page, thus allowing you to use the params[:id] variable when you create a new record
Update
class CommentsController < ApplicationController
before_filter :signed_in_user, only: [:create, :destroy]
#before_filter :correct_user, only: :destroy
def new
#comment = Comment.new
end
def create
#comment = Comment.new(comment_params)
if #comment.save
flash[:success] = "Comment created!"
redirect_to articles_url
else
render 'static_pages/home'
end
end
def destroy
#comment.destroy
redirect_to current_user
end
private
def comment_params
params.require(:comment).permit(:content).merge(:user_id => current_user.id, :article_id => params[:article_id])
end
end
you need something that
comment = Comment.new(:content => "Great post")
comment.user = user
comment.articles << Article.find(1)
comment.save
use << instead =
read this and about has_many
I am developing an app with users where they each have set of microposts displayed on their pages. I am trying to add comments to these microposts. Every time I visit localhost:3000/users/(user_id_#)
I get this error: undefined method `comments' for nil:NilClass
This error only comes when the user has microposts to show. Otherwise it just shows their blank page. The error comes from this view for app/views/users/show.html.erb This view renders this partial, where the error occurs in line 13.
<li>
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete,
confirm: "You sure?",
title: micropost.content %>
<% end %>
<h2>Comments</h2>
<% micropost.comments.each do |comment| %>
<p>
<b>Commenter:</b>
<%= comment.commenter %>
</p>
<p>
<b>Comment:</b>
<%= comment.body %>
</p>
<% end %>
<h3>Add a comment:</h3>
<%= form_for([micropost, micropost.comments.build]) do |f| %>
<div class="field">
<%= f.label :commenter %><br />
<%= f.text_field :commenter %>
</div>
<div class="field">
<%= f.label :body %><br />
<%= f.text_area :body %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
</li>
Here is my comment.rb file
class Comment < ActiveRecord::Base
belongs_to :micropost
attr_accessible :body, :user_id
end
and my micropost.rb file
class Micropost < ActiveRecord::Base
attr_accessible :content
belongs_to :user
has_many :comments
validates :content, presence: true, length: { maximum: 140 }
validates :user_id, presence: true
end
and my comments_controller.rb
class CommentsController < ApplicationController
def create
#micropost = Micropost.find(params[:micropost_id])
#comment = #micropost.comments.create(params[:comment])
redirect_to micropost_path(#micropost)
end
end
and finally my microposts_controller.rb
class MicropostsController < ApplicationController
before_filter :signed_in_user
before_filter :correct_user, only: :destroy
def create
#micropost = current_user.microposts.build(params[:micropost])
if #micropost.save
flash[:success] = "Micropost created!"
redirect_to root_path
else
#feed_items = []
render 'static_pages/home'
end
end
def new
#micropost = Micropost.new(params[:micropost])
end
def show
#micropost = Micropost.find(params[:id])
end
def destroy
#micropost.destroy
redirect_back_or root_path
end
private
def correct_user
#micropost = current_user.microposts.find_by_id(params[:id])
redirect_to root_path if #micropost.nil?
end
end
class CommentsController < ApplicationController
def create
#micropost = Micropost.find(params[:micropost_id])
#comment = #micropost.comments.create(params[:comment])
redirect_to micropost_path(#micropost)
end
end
also here is the users_controller.rb
class UsersController < ApplicationController
before_filter :signed_in_user,
only: [:index, :edit, :update, :destroy, :following, :followers]
before_filter :correct_user, only: [:edit, :update]
before_filter :admin_user, only: :destroy
def index
#users = User.paginate(page: params[:page])
end
def show
#user = User.find(params[:id])
#micropost = #user.microposts.first
#comment = Comment.new
end
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:success] = "Welcome to the Sample App!"
redirect_to #user
else
render 'new'
end
end
def edit
end
def update
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated"
sign_in #user
redirect_to #user
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User destroyed."
redirect_to users_path
end
def following
#title = "Following"
#user = User.find(params[:id])
#users = #user.followed_users.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
private
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user?(#user)
end
def admin_user
redirect_to(root_path) unless current_user.admin?
end
end
from the error it sounds like the #micropost isnt being initialized in the microposts_controller.rb file under def show. but I think it is? What am I doing wrong? Thanks
also here is app/views/users/show.html.erb
<% provide(:title, #user.name) %>
<div class="row">
<aside class="span4">
<section>
<h1>
<%= gravatar_for #user %>
<%= #user.name %>
</h1>
</section>
</aside>
<div class="span8">
<%= render 'follow_form' if signed_in? %>
<% if #user.microposts.any? %>
<h3>Microposts (<%= #user.microposts.count %>)</h3>
<ol class="microposts">
<%= render #microposts %>
</ol>
<%= will_paginate #microposts %>
<% end %>
</div>
</div>
I suspect the error is here in your partial:
<% #micropost.comments.each do |comment| %>
I am assuming that you are iterating over #microposts and passing each micro post to your partial as micropost. In the above line you are using the instance variable #micropost when you should be using the local micropost.
Update: and also here:
<%= form_for([#micropost, #micropost.comments.build]) do |f| %> –
Could you try to change this #microposts = #user.microposts.paginate(page: params[:page]) into this #micropost = #user.microposts.first and see if it works ?
I think since the partial is rendered in the context of users#show that why #micropost is nil.
This is not an answer, I just want to comment but I don't have enough reputation points.