Need some help building a modal with hotwire and `<dialog>`

So, I’ve already searched the forum and although I found this issue, I wasn’t able to fix my problem. So here we go.

What I’m trying to do

As the title says, I’m just trying to build a modal using the native html dialog element. I have everything wired up already: a button that opens it, it closes and it has my form in it with the turbo frame and all, as below:

# views/feeds/index.html.erb

<div class="flex flex-col w-full">
  <div class="flex items-center gap-2">
    <h1 class="text-2xl font-bold my-0">Feeds</h1>
    <button class="text-xs flex border px-2" command="show-modal" commandfor="my-dialog">Add Feed</button>
  </div>
  <div class="flex" data-controller="context-menu">
    <div class="w-1/12 flex-none">
      <div class="flex flex-col gap-2" id="feeds" data-action="contextmenu->context-menu#open">
        <%= render partial: "feeds/feed", collection: @feeds %>
      </div>
    </div>
    <div class="w-1/4 flex-none">
      <turbo-frame id="current_feed">
      </turbo-frame>
    </div>
    <div>
      <turbo-frame id="current_item">
      </turbo-frame>
    </div>
  <%= render "feeds/context_menu" %>
  </div>
</div>

<dialog id="my-dialog" class="m-auto p-2 w-lg">
  <turbo-frame id="new-feed">
    <%= render "form", feed: Feed.new %>
  </turbo-frame>
</dialog>

And then the form partial:

# views/feeds/_form.html.erb

<turbo-frame id="new-feed">
  <div class="flex place-content-stretch w-full" data-controller="add-feed-modal">
    <%= form_with model: feed, local: true, class: "flex flex-col gap-2 w-full" do |f| %>
      <% if feed.errors.any? %>
        <div class="flex flex-col">
          <h2><%= pluralize(feed.errors.count, "error") %> prohibited this article from being saved:</h2>
          <ul>
            <% feed.errors.full_messages.each do |message| %>
              <li><%= message %></li>
            <% end %>
          </ul>
        </div>
      <% end %>
      <div class="flex gap-2">
        <%= f.label :link, "Link:" %>
        <%= f.text_area :link, autofocus: true, class: "grow border" %>
      </div>
      <div class="flex gap-2 justify-end">
        <button commandfor="my-dialog" command="close">Close</button>
        <%= f.submit "Add feed", class: "border px-2", data: { action: "addfeedmodal->add-feed-modal#submit" } %>
      </div>
    <% end %>
  </div>
</turbo-frame>

And new.html.erb

# views/feeds/new.html.erb

<%= render "form", feed: @feed %>

Finally, my controller:

# controllers/feeds_controller.rb
class FeedsController < ApplicationRecord
...
  def create
    feed = InitializeFeed.new(link: feed_params[:link]).call

    if feed.save
      respond_to do |format|
        format.turbo_stream do
          render turbo_stream: turbo_stream.append(:feeds, partial: "feeds/feed", locals: {feed: feed})
        end
        format.html { redirect_to :feeds }
      end
    else
      render :new, assigns: { feed: feed }, status: :unprocessable_content
    end
  end
...
end

As far as opening the modal and turbo replacing the html inside the modal in case of an error, it all works. There is one problem, though.

The Issue

Some CSS styles get lost. That’s pretty much it. Here’s the modal before I submit and empty form:

And then, after I submit, the errors show up as I’d expect, buuut:

The funny thing is that it seems to only lose the styling for the grow class which would make that text area fill the width of its flex container. In fact, inspecting the element, that’s exactly what’s not happening:

I’ve read in my search and using chatGPT that it may be because, since turbo replaces only part of the DOM and not the head element, Tailwind’s scripts that should rerun on a page reload don’t get run and styles don’t get applied. Is that correct? In that case, how can I make it work?

I intended to write the whole “dismiss on form submission” using a stimulus controller. Is that were I’d need to make these styles apply? What might I be missing?

Do the (tailwind) classes get added on feeds/new ? That is the first thing I would check. Are the expected classes are actually in the built-css?

This looks more like a Tailwind issue, than Rails/Hotwire. Most common issue with “missing classes” is the production-built is taking precedence. That is the next thing I’d check.

1 Like

So, I ended up finding out that the issue is that I’m using form_for and because it adds a <div class="field_with_errors"></div> wrapper on the text area, the grow class is no longer growing the item of a flex container.

I was able to test this out by simply manually adding an extra div in the form.

The fix I adopted was to not have Rails add the extra div. I know I could’ve styled it with tailwind, but I’m just trying to make the thing work for now.

Thanks for the response!

For those wondering, this is what I did as per Gemini’s suggestion:

ActionView::Base.field_error_proc = proc do |html_tag, instance|
  html_tag.html_safe
end

Also, a minor observation: you’ve got <turbo-frame id="new-feed"> twice: in views/feeds/index.html.erb and in views/feeds/_form.html.erb. It’s not related to your current problem but it might cause confusion down the road. You probably only need the one in the form partial.

Yeah, I was doing a lot of moving around while debugging this issue. Thanks!