Примеры кода актуальны для rails 5, 6, 7 и Bootstrap 5.2. Работать будем с комментариями в блоге: любой их них, в случае успеха или неуспеха публикации будет сопровожден flash-сообщением без перезагрузки страницы. В любой момент по ходу рассказа вы можете свериться с кодом гитхаба ("crud-blog" repo) и/или опробовать сказанное на демке.
Итак. Экшены контроллера способны выглядеть (варианты) так:
app/controllers/comments_controller.rb
class CommentsController < ApplicationController
def create
@post = Post.friendly.find(params[:post_id])
@presenter = PostPresenter.new(@post)
@comment = @post.comments.create(comment_params)
respond_to do |format|
if verify_recaptcha && @comment.save
CommentMailer.with(comment: @comment, post: @post).new_comment_email.deliver_later
format.js { flash.now[:warning] = 'Your comment will be published after moderation.' }
else
format.js { flash.now[:error] = see_errors(@comment) }
end
end
end
def see_errors(x)
if x.errors.any?
view_context.pluralize(x.errors.count, 'error').to_s +
' prohibited this call from being send: ' +
x.errors.full_messages.map { |i| %('#{i}') }.join(',')
else
"Verify recaptcha: #{verify_recaptcha}"
end
end
private
def comment_params
params.require(:comment).permit(:commenter, :body)
end
end
Насчет настроек майлера - позвольте переадресовать вас к этому материалу блога; сейчас же сходу перейдем к джаваскрипту (находится в одной директории с вьюхой):
create.js.erb
// Test for ajax success
console.log("This is the create.js.erb file");
// Render flash message
$('#form').html("<%= j render 'form' %>");
$("#new_comment")[0].reset();
$('#flash-message').html("<%= j render 'shared/flash' %>").delay(4000).fadeOut(4000).css({display:"block"});
grecaptcha.reset();
Нелишне заглянуть в модель, которая содержит, помимо прочего, еще и валидации Active Record. В данном случае они только ограничивают минимум и максимум длины никнейма и сообщения, но это, разумеется, далеко не предел творческой фантазии программиста:
app/models/comment.rb
class Comment < ApplicationRecord
belongs_to :post
validates :commenter, length: { minimum: 3, maximum: 15 }
validates :body, length: { minimum: 3, maximum: 999 }
end
Теперь займемся вьюхой.
Расставьте <%= flash_message %> по нужным страницам (или поместите единовременно в application.html.erb), здесь мы используем в красивых алертах Bootstrap 5 еще и иконку svg, также включаем кнопку (X) close. Условие позволяет рендерить, как видите, два различных HTML, с наворотами и без; если в подобном нет необходимости - уберите else из хелпера и удалите ненужный файл. Alert, вызванный посредством ajax, будет красиво затухать на глазах восхищенного читателя вашего блога, информационные сообщения других контроллеров тоже будут работать:
app/views/share/_flash.html.erb
<div class="container p-3">
<% flash.each do |type, msg| %>
<%= content_tag :div, class: "alert #{ bootstrap_class_for_flash(type) } alert-dismissable fade show", role: "alert" do %>
<svg class="bi flex-shrink-0 me-2" width="24" height="24" role="img" aria-label="Info:">
<use xlink:href="#info-fill" />
</svg>
<button type="button" class="btn-close float-end" data-bs-dismiss="alert" aria-label="Close"></button>
<%= msg %>
<% end %>
<% end %>
</div>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="info-fill" fill="currentColor" viewBox="0 0 16 16">
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z" /> </symbol>
</svg>
app/views/share/_flash_without_svg.html.erb
<div class="container p-3">
<% flash.reverse_each do |type, msg| %>
<%= content_tag :div, class: "alert #{ bootstrap_class_for_flash(type) } alert-dismissable fade show", role: "alert" do %>
<button type="button" class="btn-close float-end" data-bs-dismiss="alert" aria-label="Close"></button>
<%= msg %>
<% end %>
<% end %>
</div>
Хелпер вот такой, без изысков:
app/helpers/application_helper.rb
module ApplicationHelper
def bootstrap_class_for_flash(flash_type)
case flash_type
when 'success'
'alert-success'
when 'error'
'alert-danger'
when 'warning', 'alert'
'alert-warning'
when 'notice'
'alert-info'
else
flash_type.to_s
end
end
def flash_message
tag.div id: 'flash-message' do
if controller_name.include? 'something'
render 'shared/flash_without_svg'
else
render 'shared/flash'
end
end
end
end
В комментариях блога необходима recaptcha, согласны? Если да - немного облегчим себе жизнь, позволив чуточку удобств и прописав тэг recaptcha v.2 (вы ведь именно этот ruby-gem используете?) таким образом:
<%= f.submit :Send, class: 'btn', id: 'sbm', disabled: 'disabled' %>
, добавив js, что позволит кнопке остаться заблокированной вплоть до прохождения recaptcha:
<script>
function enableBtn(){
document.getElementById("sbm").disabled = false;
}
</script>
Хм, вроде ничего не забыл? Но продолжение, как всегда, следует...