2

Rails & Javascript beginner here,

On a training project, I made flash messages disappear after few seconds using JQuery. A visitor would send AJAX request to add a product to his cart, then a flash partial 'Added to cart' appears and automatically fades out after few seconds.


# application.html.erb

<div id='flash' class='flex-column'>
   <%= render partial: 'shared/flash'  %>
</div>
# shared/_flash.html.erb

<% flash.each do |key, value| %>
  <%= display_flash(key, value) %>
  <%= javascript_pack_tag 'custom/flash' %>  
  # this works, but injects the script each times the partial is rendered
<% end %>
# helpers/application_helper.rb

def display_flash(key, value)
  def display_flash(key, value)
    div_class = [flash_class(key), 'text-center fade show alert-dismissible'].join(' ')

    content_tag(:div, class: div_class) do
      content_tag(:p, value) +
      button_tag(class: 'ml-auto close', 'data-dismiss': 'alert', type: 'button') do
        content_tag(:span, '&times;'.html_safe, 'aria-hidden': 'true') +
        content_tag(:span, 'Close', class: 'sr-only')
      end
    end
  end
end
// app/javascript/packs/custom/flash.js

function closeFlash(){
  let lastAlert = $('#flash .alert:last')

  function fadeFlash() {
    lastAlert.animate( {opacity: 0}, 2000, function() {
      $(this).hide('slow', function() {
        $(this).remove()
      });
    });
  };

  setTimeout(fadeFlash, 2000)
};

closeFlash();

The issue with this is that it pollutes my DOM with unnecessary <script> tags: unneeded script tags

This could be fixed, but is there a suitable way to execute one specific javascript function after rendering a (AJAX) partial ? In my case, executing closeFlash() located in packs/custom/flash.js each time a partial is rendered.

Thanks for your help and your time


EDIT Solution:

From Amit Patel answer and this post

# app/views/shared/_flash.html.erb

<% flash.each do |key, value| %>
  <%= display_flash(key, value) %>

  <script>
    $(function() {
      closeFlash();
    });
  </script>

<% end %>
// app/javascript/packs/custom/flash.js

window.closeFlash = function() {
    let lastAlert = $('#flash .alert:last')

    function fadeFlash() {
        lastAlert.animate( {opacity: 0}, 2000, function() {
            $(this).hide('slow', function() {
                $(this).remove()
            });
        });
    };

    setTimeout(fadeFlash, 2000)
};

It doesn't inject the whole function but (I believe) the minimal javascript code to call it.

2
  • Flash messages comes from different sources (AJAX requests or not), and I want a generic way to deal with those. I'm not sure I'm clearly understanding your point though. I'm posting additional code Commented Apr 8, 2020 at 15:42
  • I believe what's interests you is create.js.erb which .append a rendering of the flash partial to the concerned <div> and the display_flash helper that generates HTML. The message content is store in the flash value, whereas the key is used to set the correct CSS class. Commented Apr 8, 2020 at 15:51

2 Answers 2

2

Move <%= javascript_pack_tag 'custom/flash' %> to your layout or your application.js` so that it is available across the app.

modify your template like

application.html.erb

<div id='flash' class='flex-column'>
   <%= render partial: 'shared/flash'  %>
   <script>
     $(function(){
       closeFlash();
     });
   </script>
</div>
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for your answer Amit, I'll take your solution as it looks clear. I believe it should work, but somehow I've got an error jQuery.Deferred exception: closeFlash is not defined. Fixing it will teach me more about webpacker I guess ! Thanks again :)
To make it works I had to place the <script> inside my partial, and declare the closeFlash function like so: window.closeFlash = function() { .... }. It still injects the content of the <script> tag, but it's cleaner.
1

A more recent approach would be to set a Stimulus controller.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.