Rails show error message of another model - ruby-on-rails

I have a html partial that displays errors:
shared/model_errors.html.erb
<% model.errors.full_messages.each do |msg| %>
and I render it at top of each html file like
<%= render partial: 'shared/model_errors',locals: {model: #model_to_check_errors}%>
This is working fine for most of them.
Let's say that I'm on the show.html.erb template of the ConversationsController, and I have there a form_tag which submits messages to the MessagesController. If the submitted message is empty, the MessagesController won't allow it to be saved, because it has validation errors. How do I display those validations errors in my show.html.erb, considering I don't have access to the #message object of the create action of the MessagesController. Is there any way to add the error message to the ConversationsController? They will be displayed that way.
Edit:
show.html.erb of the ConversationsController
<div class="container nav_exp">
<%= render partial: 'shared/model_errors',locals: {model: #???}%>
<div class="ch-body">
<div class="col-md-12 bg-white ">
<div class="chat-message">
<ul class="chat" id="messages">
<% if #messages != nil %>
<%= render partial: 'conv_message', collection: #messages,:as => :message%>
<% else %>
<div class="panel-body hidden"></div>
<%end%>
</ul>
</div>
<%=form_tag(messages_path, :method=>'post',authenticity_token: true,remote: true) do%>
<div class="chat-box bg-white">
<div class="input-group">
<%= text_field_tag :body,"", id:"message_body",class: "form-control border no-shadow no-rounded", placeholder: "Type your message here"%>
<%= hidden_field_tag :conversation_id, #conversation.id %>
<%= hidden_field_tag :receiver, #other.id %>
<span class="input-group-btn">
<%= submit_tag 'Submit', class: "btn btn-success no-rounded" %>
</span>
</div><!-- /input-group -->
</div>
<% end %>
</div>
</div>
</div>
The create method of the MessagesController - messages_path
def create
message = Message.new(message_params)
message.user = current_user
if message.save
...
else
puts 'not working'
'I think 1 solution would be to add here flash messsages'
end
end
private
def message_params
params.permit(:body, :conversation_id, :receiver)
end

Related

form_with produces first record as nil

comment controller
class CommentsController < ApplicationController
before_action :load_commentable
before_action :checked_logged_in, only: [ :create]
def new
#comment = #commentabl.comments.new
end
def create
#comment = #commentable.comments.new(comment_params)
#comment.user_id = current_user.id
#comment.commenter = current_user.username
if #comment.blank? || #comment.save
flash[:success] = "Commented was created"
ActionCable.server.broadcast 'comment_channel',
commenter: current_user.username,
comment: #comment.content
redirect_to #commentable
else
flash[:danger] = render_to_string(:partial => 'shared/error_form_messaging',
:locals => {obj: #comment},
format: :html)
redirect_to #commentable
end
end
private
def comment_params
params.require(:comment).permit(:content, :commenter, :user_id)
end
def load_commentable
resource, id = request.path.split('/')[1,2]
#commentable = resource.singularize.classify.constantize.find(id)
end
def checked_logged_in
unless logged_in?
flash[:danger] = 'please log in to be able to comment'
redirect_to login_path
end
end
end
my form for creating a comment:
<%= form_with model:[commentable, commentable.comments.new], :html => {class: "form-horizontal", role:"form"} , local: true do |form| %>
<div class="form-group">
<div class="control-label col-sm-2">
<%= form.label :content, 'Comment' %>
</div>
<div class="col-sm-8">
<%= form.text_field :content , class: 'form-control', placeholder: "enter your comment here", autofocus: true %>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<%= form.submit 'Comment' , class: ' btn btn-primary' %>
</div>
</div>
<% end %>
the form is called in show.html.erb
<h2 class="text-center">
Title: <%= #article.title %>
</h2>
<div class="well col-xs-8 col-xs-offset-2">
<div id="user-info-showpage" align="center">
Created by: <%= render 'shared/user-info', obj: #article.user %>
</div>
<h4 class="text-center">
<strong>Description:</strong>
</h4>
<hr />
<%= simple_format(#article.description) %>
<% if #article.categories.any? %>
<p>Categories: <%= render #article.categories %></p>
<% end %>
<div class="article-actions">
<% if logged_in? && (current_user == #article.user || current_user.admin?) %>
<%= link_to "Delete", article_path(#article), method: :delete,
data: {confirm: "Are you sure you want to delete the article?"},
class: 'btn btn-xs btn-danger' %>
<%= link_to "Edit", edit_article_path(#article), class: 'btn btn-xs btn-success'%>
<%end%>
<%= link_to "View All Articles", articles_path , class: 'btn btn-xs btn-primary'%>
</div>
</div>
<% if logged_in? %>
<div class="col-xs-8 col-xs-offset-2">
<%#= render partial: 'comments/form', :locals => {commentable: #article} %>
</div>
<%end%>
<div class="col-xs-8 col-xs-offset-2">
<div id="comments"></div>
<%= #article.comments.inspect %>
<% #article.comments.each do |c| %>
<div class="well">
<%= c.content %> by
<%= c.commenter %>
</div>
<%end%>
<div id="comments"></div>
</div>
my result is in view is
Please if more info needed, ask me so I can provide
Note: I am not sure this empty record is owing to commentable.comments to be nil or I miss something
I commented render form in show page and now the empty record is gone, so my issue must be related to form_with
From my understanding, you
Expect:
in your articles#show page to not show the empty by _________ <div> HTML because the comment is still built (still in-memory), and not yet saved (not yet in DB).
Solution 1:
app/views/articles/show.html.erb
...
<div class="col-xs-8 col-xs-offset-2">
<div id="comments"></div>
<% #article.comments.each do |c| %>
<!-- ADD THIS LINE -->
<% if c.persisted? %>
<div class="well">
<%= c.content %> by
<%= c.commenter %>
</div>
<% end %>
<%end%>
<div id="comments"></div>
</div>
...
Solution 2 (better but still is a workaround):
app/views/comments/_form.html.erb
<%= form_with model:[commentable, Comment.new(commentable: commentable)], :html => {class: "form-horizontal", role:"form"} , local: true do |form| %>
Explanation:
The reason the page is displaying an empty by _________ <div> is that because you "built" a new comment before .each is called. Because they are sharing same memory space, the build basically also adds it to the array in-memory. See the following:
# rails console
article = Article.create!
comment1 = Comment.create!(commentable: article)
# from here, comment1 is then saved already in the DB
# now let's see what happens when you use "build" or "new"
# They have differences, it seem: for details: https://stackoverflow.com/questions/1253426/what-is-the-difference-between-build-and-new-on-rails/1253462
# redefine (to simulate your #article = Article.find(params[:id])
article = Article.find(article.id)
comment2 = article.comments.build
puts article.comments.count
# SQL: Select count(*) FROM ...
# => 1
puts article.comments.size
# => 2
# notice how `count` and `size` are different. `count` value is "DB-based" while `size` is "memory-based". This is because `count` is an `ActiveRecord` method while `size` is a delegated `Array` method.
# now let's simulate your actual problem in the view, where you were looping...
article.comments.each do |comment|
puts comment
end
# => <Comment id: 1>
# => <Comment id: nil>
# notice that you got 2 comments:
# one is that which is already persisted in DB
# and the other is the "built" one
# the behaviour above is to be expected because `.each` is a delegated `Array` method
# which is agnostic to where its items come from (DB or not)
This is the reason why in your page, the "built" comment is shown in the page because you are calling
<%= render partial: 'comments/form', :locals => {commentable: #article} %>
... which calls commentable.comments.build
BEFORE the <% "article.comments.each do |c| %>
If this is not clear enough yet, try putting
<%= render partial: 'comments/form', :locals => {commentable: #article} %>
... which calls commentable.comments.build
AFTER the <% "article.comments.each do |c| %> ... <% end %>
... and the by _________ <div> should already not show up.

Mailboxer N+1 Query detected

I managed to install Mailboxer with this tutorial but i have a recurring error :
N+1 Query detected
Mailboxer::Message => [:sender]
Add to your finder: :includes => [:sender]
N+1 Query method call stack
app/views/conversations/_messages.html.erb:12:in `block in _app_views_conversations__messages_html_erb__3829465222244059655_69982701059040'
app/views/conversations/_messages.html.erb:1:in `_app_views_conversations__messages_html_erb__3829465222244059655_69982701059040'
app/views/conversations/show.html.erb:27:in `_app_views_conversations_show_html_erb__1439517360344897040_69982700516580'
app/views/conversations/_messages.html.erb:12:in `block in _app_views_conversations__messages_html_erb__3829465222244059655_69982701059040'
app/views/conversations/_messages.html.erb:1:in `_app_views_conversations__messages_html_erb__3829465222244059655_69982701059040'
app/views/conversations/show.html.erb:27:in `_app_views_conversations_show_html_erb__1439517360344897040_69982700516580'
I had another one for :message but i fixed the problem with includes. If i try to do the same with :sender, i have this error:
Association named 'sender' was not found on Mailboxer::Receipt; perhaps you misspelled it?
Tutorial original code : conversations_controller.rb
class ConversationsController < ApplicationController
before_action :authenticate_user!
def new
end
def create
recipients = User.where(id: conversation_params[:recipients])
conversation = current_user.send_message(recipients, conversation_params[:body], conversation_params[:subject]).conversation
flash[:success] = "Your message was successfully sent!"
redirect_to conversation_path(conversation)
end
def show
#receipts = conversation.receipts_for(current_user)
# mark conversation as read
conversation.mark_as_read(current_user)
end
def reply
current_user.reply_to_conversation(conversation, message_params[:body])
flash[:notice] = "Your reply message was successfully sent!"
redirect_to conversation_path(conversation)
end
def trash
conversation.move_to_trash(current_user)
redirect_to mailbox_inbox_path
end
def untrash
conversation.untrash(current_user)
redirect_to mailbox_inbox_path
end
private
def conversation_params
params.require(:conversation).permit(:subject, :body,recipients:[])
end
def message_params
params.require(:message).permit(:body, :subject)
end
end
Tutorial original code : show.html.erb
<div class="row">
<div class="spacer"></div>
<div class="col-md-6">
<%= link_to "Compose", new_conversation_path, class: "btn btn-success" %>
</div>
<div class="col-md-6 text-right">
<% if conversation.is_trashed?(current_user) %>
<%= link_to 'Untrash', untrash_conversation_path(conversation), class: 'btn btn-info', method: :post %>
<% else %>
<%= link_to 'Move to trash', trash_conversation_path(conversation), class: 'btn btn-danger', method: :post,
data: {confirm: 'Are you sure?'} %>
<% end %>
</div>
</div>
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-body">
<%= render 'mailbox/folders' %>
</div>
</div>
</div>
<div class="col-md-8">
<div class="panel panel-default">
<div class="panel-body">
<%= render partial: 'messages' %>
</div>
<div class="panel-footer">
<!-- Reply Form -->
<%= form_for :message, url: reply_conversation_path(conversation) do |f| %>
<div class="form-group">
<%= f.text_area :body, placeholder: "Reply Message", rows: 4, class: "form-control" %>
</div>
<%= f.submit "Reply", class: 'btn btn-danger pull-right' %>
<% end %>
<div class="clearfix"></div>
</div>
</div>
</div>
</div>
Tutorial original code : _messages.html.erb
<% #receipts.each do |receipt| %>
<% message = receipt.message %>
<div class="media">
<div class="media-left">
<!-- user avators can go here -->
<a href="#">
<img class="media-object" src="http://placehold.it/64x64" alt="...">
</a>
</div>
<div class="media-body">
<h4 class="media-heading">
<%= message.sender.username %> <br>
<small><b>Subject: </b><%= message.subject %></small><br>
<small><b>Date: </b><%= message.created_at.strftime("%A, %b %d, %Y at %I:%M%p") %></small>
</h4>
<%= message.body %>
</div>
</div>
<% end %>
If i delete <%= message.sender.username %> the problem is solved...
Any ideas ?
Normally, to fix these N+1 query problems we use an includes and by passing a hash, we can includes nested associations. So looking at this code, calling receipt.message.sender is triggering the error, so we have a Receipt model, a message association on it and a sender associated to that. So if we can find where we load the Receipt we can add includes(message: :user) like we would for any other model.
Digging into the mailboxer gem, the receipts_for method in your show action is just a wrapper for a couple of scopes on Mailboxer::Receipt. Since this method just runs some scopes for you, we can chain onto the end of it as if it were a normal ActiveRecord where chain.
So with all that in mind, we should be able to add on our includes and avoid the N+1 query problem, ending up with something like
#receipts = conversation.receipts_for(current_user).includes(message: :sender)

Error: param is missing or the value is empty:retweet

I am new to Rails. I want to pass parameters to DB via controller, but I receive this error param is missing or the value is empty:retweet. I think the problem is the way of passing parameters in view.
Here is the view
<% for #p in #posts %>
<div class="panel panel-default post-panel">
<div class="panel-body row">
<div class="col-sm-1">
<img src="/avatar.png" class="rounded-img" height="50px" width="50px">
</div>
<div class="col-sm-11">
<p class="post-title"><%= User.find(#p.user_id).username %> <span class="post-creation">- <%= #p.created_at.to_formatted_s(:short) %></span></p>
<p class="post-content"><%= #p.content %></p>
</div>
<div class="col-sm-12">
<p class="post-links">
<span class="glyphicon glyphicon-comment g-links" aria-hidden="true"></span>
**<%= form_for Retweet.new do |f| %>
<%= hidden_field_tag current_user.id, (:user_id) %>
<%= hidden_field_tag #p.id, (:post_id) %>
<%= f.submit "Retweet", class:"btn btn-primary"%>**
<% end %>
And here is the Controller
def new
#retweet = Retweet.new
end
def create
#retweet = Retweet.new(post_params)
redirect_to(:back)
end
private def post_params
params.require(:retweet).permit(:user_id, :post_id)
end
end
You should read up some tutorials on basic REST controllers in Rails. Your create action didn't save the retweet.
def create
#retweet = Retweet.new(post_params)
if #retweet.save
redirect_to(:back)
else
render action: 'new'
end
end
Edit: Just noticed your form is all wrong:
<%= form_for Retweet.new do |f| %>
<%= f.hidden_field :user_id, current_user.id %>
<%= f.hidden_field :post_id, #p.id %>
<%= f.submit "Retweet", class:"btn btn-primary"%>**
<% end %>
Mind you that you shouldn't allow settign the user_id like this, it is very easy to change it and thus mess around with your data. Instead you should add this to retweet#create:
#retweet.user = current_user

Ajax/Rails create action not working

Hello and thanks in advance. Rails noob here :)
I have a simple rails app where a user can create an article through ajax. When the "Create article" is clicked _article_form.html.erb and new.js.erb are rendered, but when i click submit form nothing happens. No article is created. Here is new.js.erb
m = $('.modal');
m.html('<%= j(render partial: 'article_form', locals: { articles: #articles }) %>');
m.modal('show');
Here is my ArticlesController
def new
#article = Article.new
end
def create
#article = Article.create(article_params)
#articles = Article.all
end
private
def article_params
params.require(:article).permit(:title, :body)
end
Here is the home.html.erb
...
<div id="article_list">
<%= render partial: 'articles/article_list' %>
</div>
<div class="modal fade" role="dialog"></div>
And my partials _article_form
<div class="modal-dialog">
<div class="modal-body">
<%= simple_form_for #article, class: 'clearfix', remote: true do |f| %>
<%= f.input :title %>
<%= f.input :body %>
</div>
<div class="modal-footer">
<%= f.submit 'Save', class: 'btn btn-primary' %>
<% end %>
</div>
</div>
and _article_list
<% if #articles.empty? %>
<span class="text-warning">
There are no articles yet!
</span>
<% else %>
<div class="panel panel-default">
<div class="panel-heading">Articles</div>
<div class="panel-body">
<ul class="list-group">
<% #articles.each do |article| %>
<li class="list-group-item">
<%= article.title %></br>
<%= article.body.truncate(25, separator: ' ') %>
</li>
<% end %>
</ul>
</div>
</div>
<% end %>
Lastly, my create.js.erb
$('#article_list').html('<%= j(render partial: 'articles/article_list') %>');
$('.modal').modal('hide');
I have been searching/trying to figure this out for 2 days but with no luck.
If you inspect the html of your form you will notice that the submit button is out of scope of form tag. that's why when you click on submit button, no action happens.
Change your html in partial _article_form as following:
<div class="modal-dialog">
<%= simple_form_for #article, class: 'clearfix', remote: true do |f| %>
<div class="modal-body">
<%= f.input :title %>
<%= f.input :body %>
</div>
<div class="modal-footer">
<%= f.submit 'Save', class: 'btn btn-primary' %>
</div>
<% end %>
</div>

Devise - forgot password not showing error message if the email does not exist in the db

When a users clicks forgot password, it takes them to the forgot password page with a field for them to input their email address. If their email address is in the database, everything is peachy, but if the email address does NOT exist in the database, it just redirects to the same page, but does not show an error message.
How can I get the error message to appear?
/view/devise/passwords/new.html.erb
...
<%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| %>
<h1>Reset Password</h1>
<div class="login-fields">
<p>Instructions on resetting your password will be emailed to you.</p>
<%= render :partial => '/shared/messages' %>
<div class="field">
<%= f.label :email %>
<%= f.email_field :email, :placeholder => 'Email', :class => 'login username-field' %>
</div> <!-- /field -->
</div> <!-- /login-fields -->
<div class="login-actions">
<%= content_tag(:button, :type=>:submit, :class => "button btn btn-secondary btn-large") do %>
Send me reset password instructions
<% end %>
</div> <!-- .actions -->
<div class="login-social">
<%= render "devise/shared/links" %>
</div>
<% end %>
...
/views/shared/_messages.html.erb
<% if alert || flash[:alert] || flash[:error] %>
<div class="alert alert-error">
<a class="close" data-dismiss="alert" href="#">x</a>
<h4 class="alert-heading">Error!</h4>
<%= alert %>
<%= flash[:error] %>
</div>
<% end %>
<% if flash[:success] || notice || flash[:notice]%>
<div class="alert alert-success">
<a class="close" data-dismiss="alert" href="#">x</a>
<h4 class="alert-heading">Success!</h4>
<%= flash[:success] %>
<%= notice %>
</div>
<% end %>
In your shared messages partial you are only displaying errors from the controllers. You need to include errors that are attached to your model. In this case the error message is attached to the User model. You /views/shared/_messages.html.erb should actually look like:
<% if alert || flash[:alert] || flash[:error] %>
<div class="alert alert-error">
<a class="close" data-dismiss="alert" href="#">x</a>
<h4 class="alert-heading">Error!</h4>
<%= alert %>
<%= flash[:error] %>
</div>
<% end %>
<% if model.errors.any? %>
<div class="alert alert-error">
<a class="close" data-dismiss="alert" href="#">x</a>
<h4 class="alert-heading">Error!</h4>
<ul>
<% model.errors.full_messages.each do |msg| %>
<li>* <%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<% if flash[:success] || notice || flash[:notice]%>
<div class="alert alert-success">
<a class="close" data-dismiss="alert" href="#">x</a>
<h4 class="alert-heading">Success!</h4>
<%= flash[:success] %>
<%= notice %>
</div>
<% end %>
and while rendering the partial, you need to pass in the model.
<%= render '/shared/messages', model: resource %>
If you look at the default passwords/new.html.erb view that comes with devise, you'll see that it includes a call to
<%= devise_error_messages! %>
You can use that same helper in your view, if you like, to render the error messages attached to the user object (as was pointed out in Anurag's answer, the messages are attached to the model object rather than in the flash).
This helper will return markup that looks like:
<div id="error_explanation">
<h2>1 error prohibited this user from being saved</h2>
<ul><li>email not found</li></ul>
</div>
if you want to customize this, you can do so easily. Just don't use the helper, and instead add your own custom markup that accesses resource.errors.full_messages to get the "email not found" message. Or honestly since that's the only error you're going to get on this page, you could just do:
<% unless resource.errors.empty? %>
<div>email not found</div>
<% end %>
The form is calling the create method at the password_path. Make sure that the create.html.erb displays a flash message on error as well as success, if any exists.

Resources