Using form_for with LiveView and Bootstrap Modal component

I’ve been stuck on a particularly strange issue using Phoenix LiveView and Bootstrap.

I have a button on the page which opens a bootstrap modal when clicked. Inside the modal is a form.

When I add a phx_submit and phx_change liveview hook to the form_for tag, i encounter a bug, as the modal is closed as soon as the user begins interacting with the form (due to the phx_change) or hits the submit button (due to phx_submit).

I tried to add a regular HTML form with text inputs and a submit button, and attached a javascript submit handler that just called “preventDefault” on the form itself. The modal did not become hidden when submitting the form, as it does with form_for and the associated liveview hooks.

This is a liveview that is live_rendered on a page which is a classic phoenix view. The modal’s backdrop remains open, as it is rendered from the bootstrap JS outside of the liveview. However the modal itself is rendered from within the liveview and closes. The modal backdrop maintains the classes “modal-backdrop fade show”, but the modal itself loses the “show” class and only has “modal fade” once this behaviour occurs.

I’ve tried trimming both event handlers to simply return {:noreply, socket} however the problem persists. I’ve carefully inspected the DOM to try to see what is being changed, but haven’t found anything that would help. If anyone has any suggestions that might help, I would be very grateful.

1 Like

:wave:

Sorry for being off-topic, but I’ve encountered similar problems with bootstrap+liveview (some with the modal as well) and decided to use a no-js css framework, turned out to be much simpler in the end.

Here’s an example of a modal component: https://github.com/dersnek/chirp/blob/master/lib/chirp_web/live/modal_component.ex

A modal component can also be generated with mix phx.gen.live.

1 Like

I had problems with this too and found a tutorial:

I haven’t tried it yet, so YMMV!

1 Like

Yes, we switched to using Bulma for this very reasons.

1 Like

I had problems with this too and found a tutorial:

I saw that video but frankly didn’t find it all that helpful. The modal in the video is actually a LiveView modal that comes with out of the box with phx.gen.live, but seems to just have a few bootstrap classes thrown on it to have the appearance of a Bootstrap modal.

There’s nothing wrong with that at all, but it isn’t a Bootstrap modal in the sense that it uses the JS from bootstrap to toggle a modal, include a backdrop and other dynamic features.

Hi @michaelfich!

There’s a recent article by Dashbit that covers this scenario:
https://dashbit.co/blog/using-bootstrap-native-with-live-view

If you are using Bootstrap 4 with Jquery you could do something like this:

modal_component.ex

def render(assigns) do
    ~L"""
    <div class="modal fade show" tabindex="-1" role="dialog" 
        phx-target="<%= @myself %>"
        phx-capture-click="close"
        phx-page-loading>
    <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title">Modal title</h5>
        <%= live_patch to: @return_to, class: "close", "data-dismiss": "modal" do %>
          <span aria-hidden="true">&times;</span>
        <% end %>
      </div>
      <div class="modal-body">
      <%= live_component @socket, @component, @opts %>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-dismiss="modal" phx-capture-click="close"  phx-target="<%= @myself %>">Close</button>
        <button type="button" class="btn btn-primary">Save changes</button>
      </div>
    </div>
    </div>
    </div>
    """
  end

app.js

import $ from "jquery"

window.addEventListener("phx:page-loading-stop", info => {
  $('.modal.fade.show').modal('show')
  NProgress.done()
})
<%= if @live_action in [:new, :edit] do %>
<%= live_modal @socket, MyAppWeb.ResourceLive.FormComponent,
    return_to: Routes.resource_path(@socket, :index, @resource) %>
<% end %>

I’ve been experimenting with LV only for the past week, so I don’t know if there’s a better way of doing this. What other alternatives have you found since the last time you posted?