Показать ссылку в Rails form error message
В нашей форме регистрации мы validates_uniqueness_of :email
Когда пользователь пытается использовать нашу регистрационную форму, и они указывают существующий адрес электронной почты, я бы хотел, чтобы они увидели сообщение об ошибке вроде этого
Этот адрес электронной почты уже используется. Если у вас возникли проблемы с входом в систему, вы можете reset ваш пароль
Очевидно, я хотел бы использовать именованный маршрут для ссылки, но моя модель User не имеет к ней доступа. Как я могу это сделать?
Боковое примечание: Мы скоро предложим перевод для нашего приложения, и все эти сообщения об ошибках попадут в файлы YAML. Могу ли я каким-либо образом ввести мой new_password_url
в сообщение в своих файлах локали YAML? (например, config/locales/en.yml
)
Ответы
Ответ 1
Я знаю, что это старый вопрос, но для будущих пользователей, которые хотят вставить ссылку в сообщение об ошибке, вот несколько рекомендаций, которые работали для меня.
Во-первых, сообщения об ошибках I18n считаются html safe уже, поэтому вы можете пойти и написать подходящее сообщение об ошибке. В этом примере я меняю сообщение "по электронной почте".
# config/locales/en.yml
activerecord:
errors:
models:
user:
attributes:
email:
taken: 'has already been taken. If this is your email address, try <a href="%{link}">logging in</a> instead.'
Обратите внимание на интерполированную переменную %link
.
Теперь вам нужно передать значение для этой переменной в вашем валидаторе, например:
# app/models/user.rb
validates :email, :uniqueness => {:link => Rails.application.routes.url_helpers.login_path}
(По умолчанию любые параметры, которые вы передаете здесь, будут автоматически отправляться на переводчик I18n в качестве переменных, включая некоторые специальные предварительно заполненные переменные, такие как %value
, %model
и т.д.)
Что это! Теперь у вас есть ссылка в сообщении об ошибке.
Ответ 2
Это может не упорядочить хорошо с переводами, но вот предложение:
В вашем действии user_controller # create оберните все, что у вас уже есть, с помощью оператора if. Вот пример:
class UserController < ApplicationController
...
def create
if User.find(params[:email])
flash[:alert] = "This email address is in use. You can ".concat(generate_reset_password_link(params[:email])
render :action => 'new'
else
<your current code>
end
end
После этого вам нужно будет написать вспомогательный метод generate_reset_password_link
, но я думаю, что это в основном касается макета MVC. Контроллер предназначен для взаимодействия с представлением и моделью. Это немного нарушает принцип DRY, поскольку вы в основном обходите validates_uniqueness_of :email
, но вы получаете какое-то индивидуальное поведение. DRY не кажется мне 100% достижимым, если вы хотите создавать более сложные приложения, но, возможно, вы можете уточнить это и доказать, что я ошибаюсь;)
Возможно, вам придется немного помассировать, чтобы render :action => 'new'
заселил себя ранее введенными данными (в случае, если пользователь просто ошибся своим собственным адресом электронной почты и фактически не находится в системе).
Если вы решите использовать этот подход, я бы добавил комментарий в как контроллер, так и модель, указывающую, что уникальность электронной почты по существу проверена в 2-х местах. В случае, если кто-то еще должен посмотреть на этот код, это поможет им понять и поддерживать его.
Ответ 3
Вы можете поместить свой тэг, например ~ [new_password_url] в сообщениях об ошибках. Затем в точке, где отображаются ваши сообщения об ошибках gsub ur tag с фактическим. если вы хотите сделать это в общем случае, вы можете получить путь с помощью regexp, а затем eval его, чтобы получить URL-адрес, затем снова запустите его. Заставьте использовать исходный метод, если вы помещаете html в свой текст.
Ответ 4
Если вы используете 2.3.x, замените свой вызов на error_messages
своим помощником, написанным в UserHelper. Он должен принять объект FormBuilder или ActiveRecord и настроить сообщение об ошибке, как вам будет удобно. Вы можете сделать столько настроек, сколько захотите, или это может быть так же просто, как gsub:
def user_error_messages(f)
find_error = "This email address is already in use."
replacement = "This email address is already in use. #{link_to(...)} to reset your password"
f.error_messages.sub(find_error, replacement).html_safe
end
Если вы используете Rails3, создайте вспомогательный метод, чтобы просто обработать @user.errors.full_messages
, прежде чем они будут отправлены в представление.
Ответ 5
Наткнулся на это сегодня:
http://api.rubyonrails.org/classes/ActionDispatch/Routing/UrlFor.html
Если вам нужно получить доступ к этому автогенерированному методу из других мест (например, модели), вы можете сделать это, включив ActionController::UrlFor
в свой класс:
Шаг 1
Получение информации о названных маршрутах к модели является трудной частью; это приносит мне большую часть пути.
Теперь я могу сделать что-то по строкам
class User < ActiveRecord::Base
include Rails.application.routes.url_helpers
def reset_password_uri
new_user_password_path(self)
end
end
# User.find(1).reset_password_uri => "/users/password/new"
Шаг 2
Итак, у нас есть доступ к указанному маршруту, но теперь нам нужно ввести его в сообщение YAML.
Вот что я только что узнал о переменных YAML:
# en.yml
en:
welcome: "Hello, %{username}!"
# es.yml
es:
welcome: "¡Hola, %{username}!"
Я могу ввести username
, включив хэш с помощью метода t
<div id="welcome">
<%= t :welcome, :username => @user.username %>
</div>
Шаг 3
Теперь нам просто нужно добавить интерполяцию в сообщение об ошибке, описанное в исходном вопросе. Вот где я сейчас застрял: (
Ответ 6
После нескольких часов, пытаясь понять это для Rails 4 с помощью разработки, я понял, что вы можете просто добавить ссылку непосредственно в подтверждение:
# app/models/user.rb
validates :username, presence: true, uniqueness: {:message => "username has already been taken - <a href='/users'>search users</a>" }
где ссылка в этом случае является моим индексом пользователей. Надеюсь, это поможет кому-то!