cssruby-on-railshotwire-rails

Rails / Turbo Stream prepend outside a div so the CSS is not implemented on the inserted element


In my Rails project, I've implemented Turbo Stream to automatically insert a "myfifteen" instance into a list.

On the Myfifteen index page, there's a list with an "Add" button. Clicking this button opens a modal where I can choose the characteristics of the new "myfifteen" instance. Upon validation, the modal closes and, thanks to Turbo Stream, the instance is prepended to the top of the myfifteens list on the index page.

On this index page, there's a div with the class "my_fifteen_list", styled with a grid display to arrange the "myfifteen" instances in rows of three. However, I'm encountering a problem: the new instance is being inserted just before this div, and I haven't found a way to insert it inside the div.

Here is my code :

MYFIFTEEN VIEW INDEX.HTML.ERB :

<div>
  <h1>Mon top 15</h1>
  <%= turbo_frame_tag "myfifteens" do %>
    <div class="my_fifteen_list">
      <%= render @myfifteens %>
      <% (15 - @myfifteens.count).times do %>
        <%= link_to new_year_myfifteen_path(@year), remote: true, data: { turbo_frame: "modal" } do %>
          <div class='add_fifteen'>
            <%= image_tag "add_miss.svg" %>
          </div>
        <% end %>
      <% end %>
    </div>
  <% end %>
</div>

MYFIFTEEN PARTIAL _MYFIFTEEN.HTML.ERB :

 <%= turbo_frame_tag "myfifteen_#{myfifteen.id}" do %>
  <div class="myfifteen-item">
    <%= image_tag "#{myfifteen.miss.photo}.jpg" %>
    <div class="miss_details_15"><%= myfifteen.miss.region %></div>
    <% if myfifteen.my_miss_france_guess == "1" %>
      <%= image_tag "couronne.svg", class: 'myfifteen-crown' %>
    <% end %>
    <%= button_to year_myfifteen_path(@year, myfifteen), method: :delete, data: { confirm: 'Are you sure?' }, remote: true, class: 'delete-button', 'data-url': year_myfifteen_path(@year, myfifteen), 'data-turbo-frame': "myfifteen_#{myfifteen.id}" do %>
      <%= image_tag "cross.svg", class: 'delete-icon', style: 'height:20px;width:20px' %>
    <% end %>
  </div>
<% end %>

MYFIFTEEN VIEW NEW.HTML.ERB :

  <%= turbo_frame_tag "modal" do %>
    <div class="modale">
      <%= render "myfifteens/form", myfifteen: @myfifteen %>
    </div>
  <% end %>
<%= render "shared/bottombar" %>

MYFIFTEEN PARTIAL _FORM.HTML.ERB :

 <h1>Choisir une miss</h1>
<% if myfifteen.errors.any? %>
  <div class="alert alert-danger">
    <%= myfifteen.errors.full_messages.to_sentence %>
  </div>
<% end %>

<%= form_tag new_year_myfifteen_path(@year), method: :get do %>
  <%= select_tag 'category', options_for_select(@categories, @selected_category), include_blank: 'Select category' %>
  <%= submit_tag 'Filter' %>
<% end %>

  <button class="carousel-control-prev" type="button" data-bs-target="#carouselExample" data-bs-slide="prev">
  <span class="carousel-control-prev-icon" aria-hidden="true"></span>
  <span class="visually-hidden">Previous</span>
  </button>
  <button class="carousel-control-next" type="button" data-bs-target="#carouselExample" data-bs-slide="next">
    <span class="carousel-control-next-icon" aria-hidden="true"></span>
    <span class="visually-hidden">Next</span>
  </button>

<div id="carouselExample" class="carousel slide">
  <div class="carousel-inner">
    <% @misses.each do |miss| %>
      <div class="carousel-item <%= 'active' if miss == @misses.first %>">
        <div class="avatar-item">
          <%= miss.region %>
          <%= image_tag "#{miss.photo}.jpg"%>
          <div class="m-5">
            <%= simple_form_for([@year, @myfifteen]) do |f| %>
            <%= f.input :my_miss_france_guess, as: :boolean, label: "C'est elle la Miss France!" %>
            <%= f.hidden_field :miss_id, value: miss.id %>
            <%= f.button :submit, "Valider" %>
          </div>
    <% end %>
        </div>
      </div>
    <% end %>
  </div>
</div>

CREATE.TURBO_STREAM.HTML.ERB :

<%= turbo_stream.prepend "myfifteens", @myfifteen %>

<%= turbo_stream.update "modal", "" %>

THE CSS :

 .my_fifteen_list {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}

.myfifteen-item, .add_fifteen {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.my_fifteen_list img {
  border-radius: 50%;
  width: 97px;
  height: 97px;
  object-fit: cover;
}

.my_fifteen_list h5 {
  margin-top: 5px;
  margin-bottom: 5px;
}


.miss_details_15{
  font-family: 'Albert Sans SemiBold', sans-serif;
  font-size: 15px;
  color:white;
  text-align: center;
  margin-left: 10px;
}

.myfifteen-item {
  position: relative;
}

.delete-button {
  position: absolute;
  top: 0;
  right: 0;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
}

.delete-icon {
  width: 24px;
  height: 24px;

}

.add_fifteen{
  display: flex;
  justify-content: center;
  align-items: center;
  height:97px;
  width:97px;
  position: relative;
}

And here is the result of the html once the instance is inserted with turbo stream: result of the html once the instance is inserted The <turbo-frame id="myfifteen_332"> is the instance I just inserted, unfortunately before the <div class="my_fifteen_list">

Thank for your help

I tried to put the <div class="my_fifteen_list"> just before the <%= turbo_frame_tag "myfifteens" do %> but with this solution, my instances are placed one below the other instead of being aligned in rows of three


Solution

  • I'm not sure if you realize that you don't need turbo frames to use turbo streams. <%= turbo_frame_tag "myfifteens" do %> and <%= turbo_frame_tag "myfifteen_#{myfifteen.id}" do %> don't seem to serve any purpose.

    All you need is an element with id:

    <div id="my_fifteen_list">
    </div>
    
    <%= turbo_stream.prepend "my_fifteen_list", @myfifteen %>
    

    You could use a class selector, but that's usually for multiple targets:

    <%= turbo_stream.prepend_all ".my_fifteen_list", @myfifteen %>
    

    https://github.com/hotwired/turbo-rails/blob/main/app/models/turbo/streams/tag_builder.rb