Понедельник, 26 сентября 2022 03:08

Rails Ajax Flash Messages with Bootstrap 5

Оцените материал
(0 голосов)

Несложный пример реализации Ajax Flash Messages для проекта на Ruby on Rails.

Примеры кода актуальны для rails 5, 6, 7 и Bootstrap 5.2. Работать будем с комментариями в блоге: любой их них, в случае успеха или неуспеха публикации будет сопровожден flash-сообщением без перезагрузки страницы. В любой момент по ходу рассказа вы можете свериться с кодом гитхаба ("crud-blog" repo) и/или опробовать сказанное на демке.

 

Увидеть на гитхабе.

 

Dev banner 3

 

Итак. Экшены контроллера способны выглядеть (варианты) так:

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>

 

Хм, вроде ничего не забыл? Но продолжение, как всегда, следует...

Последнее изменениеПонедельник, 26 сентября 2022 04:11

Оставить комментарий

Добавьте ваш комментарий

ReactJS