Hi I have a simple app that I am building and am having trouble getting the error messages to appear when someone inputs invalid information or no information at all into a field.
The form I am using is to sign up a new user, the code associated with the user and form are below;
users_controller.rb
Class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.find(params[:id])
#country = Country.all
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to #user
else
redirect_to '/signup'
end
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password)
end
end
user.rb
class User < ApplicationRecord
before_save { self.email = email.downcase }
validates :first_name, presence: true, length: { maximum: 25 }
validates :first_name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }
has_many :trips
has_many :countries, through: :trips
end
new.html.erb
<div class="container">
<h1 class="text-center">Sign up</h1>
<div class="row">
<div class="col-md-6 offset-md-3 ">
<%=form_for(#user) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :first_name %>
<%= f.text_field :first_name, class: "form-control" %>
<%= f.label :last_name %>
<%= f.text_field :last_name, class: "form-control" %>
<%= f.label :email %>
<%= f.email_field :email, class: "form-control" %>
<%= f.label :password %>
<%= f.password_field :password, class: "form-control" %>
<%= f.submit "Create an account", class: 'form-control btn btn-primary' %>
<% end %>
</div>
</div>
</div>
_error_messages.html.erb
<% if #user.errors.any? %>
<div class="alert alert-danger">
The form contains <%= pluralize(#user.errors.count, "error") %>.
</div>
<ul>
<% #user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% else %>
<h3>test</h3>
<% end %>
When I load the form I do see the "Test" string, which I put in my _error_messages.html.erb for visibility. However when I enter data in the signup page, it reloads the page (as it should rather than sending it to the user page when all fields are valid). However, the "Test" string still appears at the top rather than the error messages that should.
My assumption is I need some sort of session or something to remember what the errors were, as right now it reloads a completely new page with nothing in memory, however, I cannot find the solution to this anywhere at the moment.
Any help would be much appreciated!
As I said, you need to change
redirect_to '/signup'
to
render 'new'
From the Guides
The render method is used so that the #user object is passed back
to the new template when it is rendered. This rendering is done within
the same request as the form submission, whereas the redirect_to will
tell the browser to issue another request.
That said, so as the redirect_to issues a new request to the browser, the values of #user is lost, in other words #user is a new instance that is instantiated again. That is why <% if #user.errors.any? %> always returns false as if there are no errors in #user.
Related
I have a multistep form. It's a bit complicated, cause basically first the user gets created through registrations controller, then get's redirected to a form. Speaking precisely, it's not a multistep form, but more of a two-step registration. To do validations with those is quite triky, but I figured the way, which works. In my User.rb I defined validations as follows:
validates :first_name, presence: true, :on => :create
validates :last_name, presence: true, :on => :create
validates :street, presence: true, :on => :update
But now I am having issues, with showing the errors to a user on that update step. I have the update action in my UserStepsController:
class UserStepsController < ApplicationController
include Wicked::Wizard
steps :address
#respond_to :html, :js
def show
#user = current_user || User.from_omniauth(request.env["omniauth.auth"])
render_wizard
end
def update
#user = current_user || User.from_omniauth(request.env["omniauth.auth"])
if #user.update!(user_params)
render_wizard #user
else
render :show
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation, :remember_me, :first_name, :last_name, :street, :house_number, :city, :zip_code)
end
def redirect_to_finish_wizard(options = nil, params = nil)
redirect_to new_user_profile_path(current_user)
end
end
So, if the User can't be updated, cause the validations have failed, I would love to show error messages about what went wrong. For this in my address.html.erb I defined this behaviour:
<%= form_for #user, url: wizard_path do |f| %>
<% if #user.errors.any? %>
<div class="error_explanation">
<h2><%= pluralize(#user.errors.count, "error") %> prevented this record from being saved:</h2>
<ul>
<% #user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="row second 1">
<div class="column">
<div class="field wizard">
<%= f.label :street %><br />
<%= f.text_field :street, class: 'form-control' %>
</div>
</div>
This won't show the errors, but throws active record error instead:
ActiveRecord::RecordInvalid in UserStepsController#update
Validation failed: Street can't be blank
Extracted source (around line #14):
12 def update
13 #user = current_user || User.from_omniauth(request.env["omniauth.auth"])
14 if #user.update!(user_params)
15 render_wizard #user
16 else
17 render :show
What am I doing wrong?
update vs update!. If I remember update! runs save! instead of save on model. It means that it runs exception if record is not valid. update will try to call save method which runs validations
The problem was the update action. I changed it as follows.
def update
#user = current_user || User.from_omniauth(request.env["omniauth.auth"])
if #user.update_attributes(user_params)
render_wizard #user
else
render :address
end
end
I also could change my view, to lead the the shared error messages:
<%= form_for #user, url: wizard_path do |f| %>
<%= render "devise/shared/error_messages" %>
This works for both creating and updating a #user:
<% if #user.errors.any? %>
<div id="error_explanation">
<div class="alert alert-error">
<%= pluralize(#user.errors.count, "error") %>.
</div>
<ul>
<% #user.errors.full_messages.each do |message| %>
<li>* <%= message %></li>
<% end %>
</ul>
</div>
<% end %>
I am relatively new to Rails. I have created a simple app, using Ruby 2.5 & Rails 5.2, that will send an email to an admin when a website visitor requests some information. The app does not use a database, because none of the visitor's information is to be saved or stored, therefore ActiveRecord is not used. Just need to get some information from the visitor via a form and email it. Research suggests using ActiveModel instead but I still can't get it to work. No 3rd party mailing apps needed. What am I overlooking? Model, Controller, Views, Mailer code appear below.
#Model: Visitor.rb
class Visitor
include ActiveModel::Model
include ActiveModel::Conversion
extend ActiveModel::Naming
include ActiveModel::Validations
attr_accessor :fname, :lname, :phone, :email, :interest
# Validations & Requirements
validates :fname, presence: true, length: { maximum: 50 }
validates :lname, presence: true, length: { maximum: 50 }
validates :lname, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }
validates :interest, presence: true
def initialize(fname, lname, phone, email, interest)
#fname = fname
#lname = lname
#phone = phone
#email = email
#interest = interest
post_initialize
end
# #make in lieu of #create
def self.make(fname, lname, phone, email, interest)
attributes = {fname: fname, lname: lname, phone: phone, email: email, interest: interest}
object = new(attributes)
object.save
#visitor = object
#visitor
end
def persisted?
false
end
def send_mail
# Following method defined in NotifierMailer
NotifierMailer.visitor_info(self).deliver_now
end
private
def post_initialize
if email
email.downcase
end
end
end
# Controller: Visitors_controller.rb
class VisitorsController < ApplicationController
def new
#Visitor = Visitor.new
end
def make
#visitor = Visitor.new(visitor_params)
if #visitor.valid?
#visitor.send_mail # Instance method in Guest model
flash[:notice] = "Your request has been sent! Thank you for contacting us."
redirect_to welcome_path
else
render 'new'
end
end
private
def visitor_params
params.require(:visitor).permit(:fname, :lname, :phone, :email, :interest)
end
end
# Routes: Routes.rb
Rails.application.routes.draw do
get 'favicon', to: 'pages#favicon'
root to: 'pages#index', as: 'welcome'
# ... other routes for static page navigation
get '/reqinfo', to: 'visitors#new'
post '/reqinfo', to: 'visitors#make'
end
# View: New.html.erb
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_with model: #visitor, url: reqinfo_path, local: true do |form| %>
<%= render 'layouts/error_messages', object: form.object %>
<%= form.label 'First Name' %>
<%= form.text_field :fname, class: 'form-control' %>
<%= form.label 'Last Name' %>
<%= form.text_field :lname, class: 'form-control' %>
<%= form.label :phone %>
<%= form.text_field :phone, class: 'form-control' %>
<%= form.label 'Email Address' %>
<%= form.text_field :email, class: 'form-control' %>
<%= form.label 'Interest' %>
<%= form.text_field :interest, class 'form-control' %>
<%= form.submit 'Submit', id: "button" %>
<% end %><!-- form_with -->
</div><!-- col-md6-3 -->
</div><!-- row -->
# Mailer: Notifier_mailer.rb
class NotifierMailer < ApplicationMailer
default from: 'noreply#thecompany.com'
def guest_info(visitor)
#visitor = visitor
mail(to: 'admin#thecompany.com', subject: "Website Visitors Request for Information")
end
end
# Message: Visitor_info.html.erb
<h1>Dear Admin;</h1>
<br>
<% timestamp = Time.now %>
<p>
<%= visitor.fname %> <%= #visitor.lname %> visited the TheCompany website on <%= timestamp.localtime %>. He/she is requesting information regarding a <%= #visitor.interest %>. Their contact information phone: <%= #visitor.phone %> and email: <%= #visitor.email %>
</p>
The problem I'm facing most likely has something to do with strong parameters. The thing is when I try to edit some user information and update it, an error appears which is not related with this form. That kind of error's supposed to pop up only when signing up or logging in.
For example, here is my database. And then I click 'Edit'.
After editing some information and submitting it, the error pops up.
controllers/users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to [#user, #task], notice: "Thank you for signing up!"
else
render "new"
end
end
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update(user_params)
redirect_to #user
else
render 'edit'
end
end
def destroy
#user = User.find(params[:id])
#user.destroy
respond_to {|format| format.js }
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
end
views/user/edit.html
<h1>Editing user</h1>
<%= form_for :user, url: #user, method: :patch do |f| %>
<% if #user.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#user.errors.count, "error") %> prohibited
this task from being saved:
</h2>
<ul>
<% #user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :first_name %><br>
<%= f.text_field :first_name %>
</p>
<p>
<%= f.label :last_name %><br>
<%= f.text_field :last_name %>
</p>
<p>
<%= f.submit %>
</p>
<%= link_to 'Back to List', users_path %>
<% end %>
models/user.rb
class User < ActiveRecord::Base
has_secure_password
EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :first_name, presence: true,
length: {maximum: 20}
validates :last_name, presence: true,
length: {maximum: 40}
validates :email, presence: true,
format: {with: EMAIL_REGEX},
uniqueness: {case_sensitive: false}
validates :password,
length: {within: 6..40}
has_many :tasks
end
What do I do? Can anybody help?
The problem is not with the strong params. It is due to validation you have on password.
This
validates :password,length: {within: 6..40}
should be
validates :password,length: {within: 6..40}, on: :create
Yes, the problem is in strong params. You should never permit password and password_confirmation
def user_params
params.require(:user).permit(:first_name, :last_name, :email)
end
Just remove password and password_confirmation from user_params. You wouldn't want to mass assign those values.
And this would be a very bad practice to store a plain password in database. You should store encrypted or hashed password in database; it would enhance the security and integrity of your application.
To get more information about storing an encrypted or hashed password, please take a look at this and this links.
I've seen a lot of posts about how to map 2 form fields to one model field, but what about mapping 1 form field to provide answers for two (or more) model fields? I have a form for users which gives a field, last_name, for a user. But I want the default password for that user to also be last_name (and I have password_confirmation set up, so that also needs to be last_name). How would I do this?
Form:
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#user) do |f| %>
<%= f.label :first_name %>
<%= f.text_field :first_name, class: 'form-control' %>
<%= f.label :last_name %>
<%= f.text_field :last_name, :password, :password_confirmation,
class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.hidden_field :access_level, :value => "Chair" %>
<%= f.label :Phone %>
<%= f.text_field :phone_number, :id => "phone", class: 'form-control' %>
<p> </p>
<div class="col-md-12">
<%= f.submit "Add Chair", class: "btn btn-primary" %>
</div>
<% end %>
</div>
</div>
user_controller.rb
def create
#user = User.new(user_params)
if #user.save
log_in #user
current_user
flash[:success] = "Welcome to the Penn Model Congress!"
redirect_to after_sign_in_path
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password,
:password_confirmation, :access_level,
:phone_number)
end
end
I would add a before_validation callback to the User model like this:
# in app/models/user.rb
before_validation :set_default_password
private
def set_default_password
self.password ||= last_name
self.password_confirmation ||= last_name
end
If this will be your default and you don't want the user to be able to override it on creation, you could set it on the controller:
View:
<%= f.text_field :last_name, class: 'form-control' %>
Controller:
def create
#user = User.new(user_params)
#user.password = #user.last_name
#user.password_confirmation = #user.last_name
if #user.save
log_in #user
current_user
flash[:success] = "Welcome to the Penn Model Congress!"
redirect_to after_sign_in_path
else
render 'new'
end
end
I am having trouble articulating the exact issue I am facing, but I will try with a brief descriptions and code.
I am trying to add a feature to a simple existing app that allows the user to crop an uploaded image for an avatar. I do the file selection on the same view that allows the user to update their password and other various account options. The user submits that form which then renders the view for the cropping feature. The issue is that from the crop view, the submission fails because it fails validation of parameters from the previous form. Basically I would like the form to all be submitted at the same time but from two different views.
user.rb
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation, :avatar,
:crop_x, :crop_y, :crop_w, :crop_h
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 6 }
validates :password_confirmation, presence: true
mount_uploader :avatar, AvatarUploader
attr_accessor :crop_x, :crop_y, :crop_w, :crop_h
after_update :crop_avatar
def crop_avatar
avatar.recreate_versions! if crop_x.present?
end
end
I have tried several different things to remedy this. I am sure I am missing a fundamental concept. Any ideas?
users_controller.rb
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
if params[:user][:avatar].present?
render 'crop'
else
sign_in #user
redirect_to #user, notice: "Successfully updated user."
end
else
render 'edit'
end
end
edit.html.erb
<% provide(:title, "Edit user") %>
<h1>Update your profile</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for #user, :html => {:multipart => true } do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :avatar %>
<%= f.file_field :avatar %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation, "Confirm Password" %>
<%= f.password_field :password_confirmation %>
<%= f.submit "Save changes", class: "btn btn-large btn-primary" %>
<% end %>
</div>
</div>
crop.html.erb
<% provide(:title, 'Crop Avatar') %>
<h1>Crop Avatar</h1>
<div class="row">
<div class="span6 offset3">
<%= image_tag #user.avatar_url(:large), id: "cropbox" %>
<h4>Preview</h4>
<div style="width:100px; height:100px; overflow:hidden">
<%= image_tag #user.avatar.url(:large), :id => "preview" %>
</div>
<%= form_for #user do |f| %>
<% %w[x y w h].each do |attribute| %>
<%= f.hidden_field "crop_#{attribute}" %>
<% end %>
<div class="actions">
<%= f.submit "Crop" %>
</div>
<% end %>
</div>
</div>
You can't have 2 views for one action. On a second thought why do you need it anyways, i mean you are rendering crop only when params[:user][:avatar] is present and that will be called only when you'll submit your edit.html.erb template. I think what you can do is have another method in controller with name crop and there update user's avatar with the dimention's specified.