Search code examples
ruby-on-railsruby-on-rails-5materialize

show different toast for each flash in rails


I am using rails and materialize toasts for displaying flash messages. Here is the code am using

<% unless flash.empty? %>
    <script>
      <% flash.each do |f| %>
      <% type=f[0].to_s.gsub('alert', 'red').gsub('warning', 'deep-purple').gsub('success', 'green') %>
      Materialize.toast('<%= f[1] %>', 4000, '<%= type %>')
      <% end %>
    </script>
<% end %>

The issue is that this wont split the flash messages to different toasts instead shows all in a single toast. How can I make each flash show in its own toast?


Solution

  • Flashes are regrouped in a hash of type/value. I suggest that you create a helper to wrap your presentation tags for flashes

    The value of a flash could very well be a single string, or an array or strings. I personally use a wrapper around flash that lets me push as many flashes I want to a same type

    THe following code handles string or array flashes. Note the flash.each do |type, content|

    def flashes
      content_tag(:div, class: 'flash-group') do
        flash.each do |type, content|
          if content.respond_to?(:each)
            # If you have an array of flashes, regroup them under the same "flash" block
            concat(single_flash(type, flash) do
              # The flash messages are added as a list 
              content_tag(:ul) do
                content.flatten.each do |message|
                  msg = if message.respond_to?(:html_safe)
                    message.html_safe
                  else
                    msg
                  end
                  concat(content_tag(:li, msg))
                end
              end
            end)
          else
            concat(single_flash(type, content.html_safe))
          end
        end
      end
    end
    

    This wrapper is kinda framework-agnostic and you can define single_flash however you want, and just use <%= flashes %> in your layout.

    For your material implementation you'd have something like

    def single_flash(type, content = nil)
      type_class = case type
      when 'alert'
        'red'
      when 'warning'
        'deep-purple'
      when 'success'
        'green'
      else
        '?'
      end
      Materialize.toast(content, 4000, type_class)
    end
    

    For example my bootstrap implementation

    # Render a single flash, styles according to the flash type
      def single_flash(type, content = nil)
        alert_class = ['alert alert-dismissible media']
        contextual_class = case type.to_sym
        when  :alert, :danger, :error, :fatal
          'danger'
        when :warning, :todo
          'warning'
        when :notice, :success
          'success'
        else
          'info'
        end
        alert_class << "alert-#{contextual_class}"
        close_class = "close text-#{contextual_class}"
        content_tag(:div, class: alert_class, role: 'alert') do
          concat(content_tag(:div, class: 'flash-icon media-left media-middle') do
            font_awesome(case type.to_sym
            when :fatal, :error
              'exclamation-triangle'
            when :danger, :warning
              'exclamation-triangle'
            when :success
              'check'
            when :notice, :info
              'info-circle'
            else
              'question'
            end)
          end)
          concat(content_tag(:div, class: 'flash-message media-body') do
            block_given? ? yield : simple_format(content)
          end)
          concat(content_tag(:div, class: 'media-right media-middle') do
            concat(content_tag(:button, class: close_class, 'data-dismiss': 'alert') do
              concat(content_tag(:span, aria_hidden: true) {'&times;'.html_safe })
              concat(content_tag(:span, class: 'sr-only') { 'Close' })
            end)
          end)
        end
      end