I have some custom validations in my model for when the user submits a URL using simple_form, but I can't seem to get the error message associated with each custom validation to show in the view (yet the validations seem to work)?
The only error I see is the one defined in the create method. Any guidance would be appreciated....
Model
class Product < ActiveRecord::Base
validates :url, presence: true
validate :check_source, :must_contain_product_id
def check_source
valid_urls = ["foo", "bar"]
errors.add(:url, "Must be from foo or bar") unless valid_urls.any? {|mes| self.url.include? mes}
end
def must_contain_product_id
errors.add(:url, "Must be product page") unless self.url.include? "productID"
end
end
Controller
def create
#product = Product.new
if #product.save
flash[:success] = "Product added to your list"
redirect_to root_path
else
flash[:message] = "Sorry we can't add this product"
redirect_to root_path
end
end
View (using Simple_form)
# Various messaging I've tried
<% if flash[:success].present? %>
<div data-alert class="alert-box success">
<%= flash[:success] %>
</div>
<% end %>
<% if flash[:error].present? %>
<div data-alert class="alert-box">
<%= flash[:error] %>
</div>
<% end %>
<% if flash[:message].present? %>
<div data-alert class="alert-box">
<%= flash[:message] %>
</div>
<% end %>
<% if #product.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#product.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #product.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
# The actual form...
<%= simple_form_for Product.new do |form| %>
<%= form.input :url, maxlength: false %>
<%= form.button :submit, "Get Product", data: { disable_with: "Retrieving product ..." } %>
<% end %>
I had the same issue solved using:
errors.add(:base, "message here")
However this won't be assigned to a specific form input (e.g url) which was not necessary in my case
You're redirecting to the root_path in the create action regardless of whether or not the save is successful. Your view for the Product form contains divs for the flashes, but unless the view for the root_path renders flash divs as well you won't see them.
I think you neet to redirect to products, or to edit. Otherwise you are unable to see the error, because you are not on the page where it should be displayed.
Try
def create
#product = Product.new
if #product.save
flash[:success] = "Product added to your list"
redirect_to root_path
else
flash[:message] = "Sorry we can't add this product"
render 'edit'
end
end
errors.add(:base, "message")
This is the correct syntax to add message in custom validations.
See your code you are redirecting to root_path if you redirecting means values and variable will not come to your partial.
so you have to change your code like,
def create
#product = Product.new
if #product.save
flash[:success] = "Product added to your list"
redirect_to root_path
else
flash[:message] = "Sorry we can't add this product"
render :action => "new"
end
end
after that in model Instead of
errors.add(:url, "Must be from foo or bar")
Please try like this:
errors[:base] << ("Must be from foo or bar")
Related
I have action index:
def index
if params['type'] == 'random'
#objects = Object.order("RANDOM()").limit(1)
else
#objects = Object.all.limit(1)
end
end
and create action:
def create
object = Object.find(params[:object_id])
comment = object.comments.create(params[:comment].permit(:body))
respond_to do |format|
format.html
format.js #ajax
end
if comment.save
redirect_to root_path(params[:object_id]) #doesn't work
else
flash[:error] = comment.errors.full_messages[0]
redirect_to root_path(params[:object_id]) #doesn't work
end
end
I can comment an object in my index page. When I put a comment, I want to redirect to the object that was commented.
With my code, the page is reloaded, but the next object is displayed, and I cannot see the comment. How can I redirect to the same object?
My root_path
<span class="random-icon"><%= link_to icon('random'), "http://localhost:3000/?type=random" %></span>
<div class="inner-container">
<% #objects.each do |object| %>
<h1 class="title"><%= object.title %></h1>
<p class="obj"><%= object.body %></p>
<h3 class="comments-title">Comments:</h3>
<div id="comments">
<% object.comments.each do |comment| %>
<div class="comments"> <%= comment.body %>
<span class="time-to-now"><%= distance_of_time_in_words_to_now(comment.created_at) %> ago</span>
</div>
<% end %>
</div>
<div id="error"><%= flash[:error] %></div>
<%= form_for([object, object.comments.build], remote: true) do |f| %>
<%= f.text_area :body, class: "text-area" %>
<p class="char-limit">255 characters limit</p>
<%= f.submit "Comment", class: 'button' %>
<% end %>
<% end %>
</div>
If params['type'] is true, Object.order("RANDOM()").limit(1) will always br reevaluated and usually return a new object. To ensure you return to the same object, you might want to store it in session and then check first in your index if there is a liked comment in your sessions, if so, #objects = Object.find(session[:comment_object_id])
def index
if session[:comment_object_id]
#objects = Object.find(session[:comment_object_id])
session.delete(:comment_object_id) # delete the session after use
elsif params['type'] == 'random'
#objects = Object.order("RANDOM()").limit(1)
else
#objects = Object.all.limit(1)
end
end
def create
object = Object.find(params[:id])
comment = object.comments.create(params[:comment].permit(:body))
respond_to do |format|
format.html
format.js #ajax
end
if comment.save
session[:comment_object_id] = :object_id # set the session here
redirect_to root_path # should work now
else
flash[:error] = comment.errors.full_messages[0]
redirect_to root_path #should work now
end
end
This is pretty easy, and you're very close.
Inside your create method, you've got the object you want to redirect to. So just use it directly inside the if comment.save like:
redirect_to object_path(object)
You can get a list of all these path "helpers" via the command:
rake routes
And in that listing, you should see, by the way, that root_path does not accept any arguments ... for future reference.
I am having trouble finding any information on how to iterate through an array and create objects.
My form creates a selectable list of users that when checked, pass the user_ids as an array object.
invitations\new.html.rb
<%= bootstrap_form_for Invitation.new do |f| %>
<br>
<ul>
<%= f.hidden_field :attended_event_id, :value => #event_selected.id %>
<li>
<%= check_box_tag 'attendee_id[]', user.id %>
<%= h user.name %>
</li>
<% end %>
</ul>
<br>
<%= f.submit "Invite Selected Users" %>
<% end %>
I would like to then create new Invitations objects by combining the attended_event_id with all of the objects in the attendee_id array.
After a bit of trouble I got the basics of my controller working but only by passing in the user_id as a text entry. Below is my Invitations controller. Not really sure where to start on this one as I haven't been able to find a good example.
invitations_controller.rb
def create
#invitation = Invitation.new(invite_params)
if #invitation.save!
flash.now[:success] = "Invited!"
redirect_to root_path
else
flash.now[:error] = "Failure!"
redirect_to root_path
end
end
private
def invite_params
params.require(:invitation).permit(:attended_event_id, :attendee_id)
end
end
Do you mean something like this?
<%= bootstrap_form_for Invitation.new do |f| %>
<br>
<ul>
<%= f.hidden_field :attended_event_id, :value => #event_selected.id %>
<% users.each do |user| %>
<li>
<%= check_box_tag 'invitation[attendee_id][]', user.id %>
<%= h user.name %>
</li>
<% end %>
</ul>
<br>
<%= f.submit "Invite Selected Users" %>
<% end %>
def create
#invitations = invite_params[:attendee_id].map do |attendee_id|
Invitation.new(
attended_event_id: invite_params[:attended_event_id],
attendee_id: attendee_id
)
end
if #invitations.any?(&:invalid?)
flash.now[:error] = "Failure!"
redirect_to root_path
else
#invitations.each(&:save!)
flash.now[:success] = "Invited!"
redirect_to root_path
end
end
private
def invite_params
params.require(:invitation).permit(:attended_event_id, {:attendee_id => []})
end
There is a good basic example on RailsGuides
http://guides.rubyonrails.org/form_helpers.html#binding-a-form-to-an-object
Do you want to achieve something like this:
def create
params[:attendee_id].each do |user_id|
Invitation.create(:attended_event_id => params[:attended_event_id], :attendee_id => user_id)
end
.
.
.
end
I'm trying to create custom form that allows admins to toggle the user privileges of users on and off, and save them to the database. I must be doing something completely wrong, because the pages generate fine, and the submit button submits to the action fine, but nothing gets saved to the database, and it just renders my initial view again. Can anyone see what I'm doing wrong?
The Code
Routes:
resources :users do
member do
get 'assign'
put 'assign_update'
end
end
...
Controller (This weird way of doing it is an attempt to circumvent the fact that the admin and other attributes are not accessible. It might be a mess.):
...
def assign
#user = User.find(params[:id])
end
def assign_update
admin_protected = params[:user].delete(:admin)
#user = User.find(params[:id])
#user.admin = admin_protected
if #user.save
flash[:success] = "User updated"
redirect_to users_path
else
render 'assign'
end
end
View:
...
<%= form_for(#user, url: { controller: 'users',
action: 'assign_update'}, method: 'put') do |f| %>
<%= f.label :admin, 'Is admin?', class: 'checkbox inline' %>
<%= f.check_box :admin %>
<%= f.submit "Save changes", class: "btn btn-large btn-primary" %>
<% end %>
So to summarize the conversation we had in the comment section under the question...
Problem was found to be validations on other user model attributes. The user instance couldn't be saved because validations were not passing when save method was being executed. As a result, Rails was just rendering assign view.
update_attribute method updates an attribute without model validation nor mass assignment protection. And admin attribute fits those two criteria in this case.
You should be using attr_accessible in your model and manually picking those fields from the params has and assigning them individually making sure that the admin field is NOT included in the list of fields assigned to the attr_accessible declaration
So
def assign_update
admin_protected = params[:user].delete(:admin)
#user = User.find(params[:id])
#user.admin = admin_protected
if #user.save
flash[:success] = "User updated"
redirect_to users_path
else
render 'assign'
end
end
becomes
def assign_update
# admin_protected = params[:user].delete(:admin)
#user = User.find(params[:id])
#user.admin = params[:user][:admin]
if #user.save
flash[:success] = "User updated"
redirect_to users_path
else
render 'assign'
end
end
The problem is that in your approach you are still mass assigning.
To debug what is actually happening you should take a careful look at you log file output
UPDATE
Check the errors list.
Add the following to your form
<% if #user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user.errors.count, "error") %> prohibited this account from being saved:</h2>
<ul>
<% #user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
It should give you, ad your users a clearer idea of what is wrong and how to fix it
I have a user and guideline model.
I would like a user to be able to mark a guideline as their favourite and then view a list of all their favourite guidelines.
It's not quite right. I'm not sure if the 'favourite' action is adding a favourite correctly; or if it is adding correctly it's not displaying correctly in the favourites view (so 'show' action may not be right)...
*CONTROLLER*guidelines_controller.rb
def favourite
type = params[:type]
if type == "favourite"
#guideline= Guideline.find_all_by_id(params[:guideline_id])
current_user.favourite_guidelines << #guideline
redirect_to :back, notice: 'You favourited #{#guideline.name}'
elsif type == "unfavourite"
current_user.favourite_guidelines.delete(#guideline)
redirect_to :back, notice: 'Unfavourited #{#guideline.name}'
else
# Type missing, nothing happens
redirect_to :back, notice: 'Nothing happened.'
end
end
*CONTROLLER*favourites_controller.rb
def show
#user = User.find_by_profile_name(params[:id])
if #user
#guidelines = #user.favourite_guidelines.all
render action: :show
else
render file: 'public/404', status: 404, formats: [:html]
end
end
end
*ROUTES*routes.rb
get "guidelines/favourite"
get "favourites/show"
*MODEL*user.rb
has_many :guidelines
has_many :favourite_guidelines
*MODEL*favourite_guidelines.rb
attr_accessible :guideline_id, :user_id
belongs_to :user
*VIEWS*guidelines/index.html.erb
<% if current_user %>
<%= link_to "favourite", guidelines_favourite_path(guideline, type: "favourite"), method: "get" %>
<%= link_to "unfavourite", guidelines_favourite_path(guideline, type: "unfavourite"), method: "get" %>
*VIEWS*favourites/show.html.erb
<% if #guidelines %>
<% #guidelines.each do |guideline| %>
<div class="well">
<%= link_to guideline.title, guideline %>
<br />
<%= guideline.content %>
<br />
Added <%= time_ago_in_words(guideline.created_at) %> ago
</div>
<% end %>
<% end %>
As per your comment following returns nil:
#guideline= Guideline.find_by_id(params[:guideline_id]) #=> nil
current_user.favourite_guidelines << nil #Gives association mismatch error
I think params[:guideline_id] is nil. Post your params from log file.
Or try this:
Change your link to like this:
<%= link_to "favourite", guidelines_favourite_path(guideline_id: guideline.id, type: "favourite"), method: "get" %>
<%= link_to "unfavourite", guidelines_favourite_path(guideline_id: guideline.id, type: "unfavourite"), method: "get" %>
In your earlier case:
#guideline= Guideline.find_all_by_id(params[:guideline_id]) #=> []
current_user.favourite_guidelines << [] #=> Valid and inserting nothing
There have been many questions about this, but none of them seem to help. And yes, I have watched this rails cast.
I have an Author who has many Books, like so:
Author:
class Author < ActiveRecord::Base
attr_accessible :name
has_many :books, dependent: :destroy
accepts_nested_attributes_for :books, allow_destroy: true
validates :name, presence: true
validates :name, length: { minimum: 3 }
end
Book:
class Book < ActiveRecord::Base
attr_accessible :name, :year
belongs_to :author
validates :name, :year, presence: true
validates :year, numericality: { only_integer: true, less_than_or_equal_to: Time.now.year }
end
I created the following form to add a book to an author in authors#show:
<%= form_for([#author, #book], html: { class: "well" }) do |f| %>
<% if #book.errors.any? %>
<div class="alert alert-block">
<ul>
<% #author.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
#labels and buttons...
<% end %>
...with the following authors_controller method:
def show
#author = Author.find(params[:id])
#book = #author.books.build
end
...and the following books_controller method:
def create
#author = Author.find(params[:author_id])
if #author.books.create(params[:book])
redirect_to author_path(#author)
else
render action: :show
end
end
I cannot seem to figure out why the form does not display any error messages. I followed the example from railscasts where they say there should be an instance variable of books in the form instead of #author.books.build, so I put the latter in the controller and #book in the form - still to no avail.
Thanks for any help!
Let's step through it.
You submit the create, and that enters your create action
def create
#author = Author.find(params[:author_id])
if #author.books.create(params[:book])
redirect_to author_path(#author)
else
render action: :show
end
end
(Side note, what if #author is not found. You are not handling that case.)
Now, the Author is found, but #author.books.create fails (returns false), so you render the show action.
This uses the show template, but does not call the show action code. (Side note, maybe the new page would be a better choice, so the user can try to create again.)
At this point #author is instantiated with the Author you found, but not #book. So #book, if called will be nil.
Your show template does
if #book.errors.any?
which will not be true, so the rest of the template inside the if will be skipped. That's why there are no errors.
You don't need a form_for to display error messages. If you switch to using the new template, then there will be a form to try again.
So let's switch to rendering new.
Class BooksController < ApplicationController
def new
#author = Author.find(params[:author_id])
#book = #author.books.build
end
def create
#author = Author.find(params[:author_id])
#book = #author.books.build(params[:book])
if #author.save
redirect_to author_path(#author)
else
render action: :new
end
end
Your new template will be
<% if #author.errors.any? %>
<div class="alert alert-block">
<ul>
<% #author.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<% if #book.errors.any? %>
<div class="alert alert-block">
<ul>
<% #book.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= form_for([#author, #book], html: { class: "well" }) do |f| %>
#labels and buttons...
<% end %>
In Books controller
/books_controller.rb
def new
#author = Author.find_by_id(params[:author_id])
#book = #author.books.build
end
def create
#author = Author.find_by_id(params[:author_id])
if #author
#book = #author.books.build(params[:book])
if #book.save
flash[:notice] = "Book saved successfully"
redirect_to author_path(#author)
else
render :new
end
else
flash[:notice] = "Sorry no author found"
redirect_to author_path
end
end
If author is not present redirect to authors index page with error message dont render the new form as you'll not be able to build the books form as author is nil.
And in your books new form you can have the error listed for books
/books/new.html.erb
<% if #book.errors.any? %>
<div class="alert alert-block">
<ul>
<% #books.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>