Search code examples
elixirphoenix-framework

Elixir Phoenix How to change and retain class using %JS{} in this modal text editor example


The code below lets a user click on a list item (called a testbed.note) and as a result a modal pops up whereby they can either preview or edit the content.

The problem is if the user keeps the editor open and the modal closes, the next time the modal is opened it does so in the "edit" state.

In the code, the user should NEVER be able to open the modal and have it default to the edit state. In other words, each time the modal launches it should always be in preview mode.

I tried placing %JS{} code to toggle the class when the user clicks the submit button. It didn't work.

I have linked a video: https://imgur.com/q8Junfb

defmodule AppWeb.PageLive do 

  use AppWeb, :live_view  
  alias App.Testbeds 
  def mount(_params, _session, socket)do   
     {:ok, assign(socket, testbeds: Testbeds.list_testbeds())}   
  end

    def button_event("invoke_editing") do
    %JS{}
    |> JS.remove_class("active", to: "#editing_button")
    |> JS.add_class("hidden", to: "#editing_button")
    |> JS.remove_class("hidden", to: "#preview_button")
    |> JS.add_class("active", to: "#preview_button")
    |> JS.remove_class("active", to: "#preview")
    |> JS.add_class("hidden", to: "#preview")
    |> JS.remove_class("hidden", to: "#editing")
    |> JS.add_class("active", to: "#editing")
  end

  def button_event("invoke_preview") do
    %JS{}
    |> JS.remove_class("active", to: "#preview_button")
    |> JS.add_class("hidden", to: "#preview_button")
    |> JS.remove_class("hidden", to: "#editing_button")
    |> JS.add_class("active", to: "#editing_button")
    |> JS.remove_class("active", to: "#editing")
    |> JS.add_class("hidden", to: "#editing")
    |> JS.remove_class("hidden", to: "#preview")
    |> JS.add_class("active", to: "#preview")

  end

  def handle_event("send", params, socket) do

    {:noreply, socket}
  end





  def render(assigns) do  
     ~H"""



    <%= for testbed <- @testbeds do %>
        <.modal id={"notes-modal-#{testbed.id}"}>
            <.button phx-click={button_event("invoke_editing")} id="editing_button" class="hidden">
              Preview
            </.button>
            <.button phx-click={button_event("invoke_preview")} id="preview_button">
              Edit Me
            </.button>
            <div id="editing"> PREVIEWING THE WORK</div>
            <form phx-submit = "send"   id="preview" class="hidden">
              <textarea value = {testbed.note}></textarea>
              <.button type="submit" phx-click={hide_modal("notes-modal-#{testbed.id}")}>SAVE WORK</.button>
            </form>
        </.modal>

    <%= if testbed.note do %>
      <div phx-click={show_modal("notes-modal-#{testbed.id}")}>READ ME  ...<%= testbed.note %></div>
    <% end %>
       
    <% end %>
       
      """
  end

end

EDIT

I do not know how to set selects with dynamic ID's and still select them using JS{}. As it stands, the current ID's repeat

EDIT

I tried to change the code so that it only consist of unique ID's. There are no errors rendered and the code looks like it should work but the "item" argument is not being read in the function bodie(s). IO.inspect item does not log anything when the button(s) are clicked.

defmodule AppWeb.PageLive do 

  use AppWeb, :live_view  
  alias App.Testbeds 
  def mount(_params, _session, socket)do   
     {:ok, assign(socket, testbeds: Testbeds.list_testbeds())}   
  end

    def button_event("invoke_editing",item) do
      IO.inspect item
    %JS{}
    |> JS.remove_class("active", to: "#editing_button-#{item}")
    |> JS.add_class("hidden", to: "#editing_button-#{item}")
    |> JS.remove_class("hidden", to: "#preview_button-#{item}")
    |> JS.add_class("active", to: "#preview_button-#{item}")
    |> JS.remove_class("active", to: "#preview-#{item}")
    |> JS.add_class("hidden", to: "#preview-#{item}")
    |> JS.remove_class("hidden", to: "#editing-#{item}")
    |> JS.add_class("active", to: "#editing-#{item}")
  end

  def button_event("invoke_preview", item) do
    IO.inspect item
    %JS{}
    |> JS.remove_class("active", to: "#preview_button-#{item}")
    |> JS.add_class("hidden", to: "#preview_button-#{item}")
    |> JS.remove_class("hidden", to: "#editing_button-#{item}")
    |> JS.add_class("active", to: "#editing_button-#{item}")
    |> JS.remove_class("active", to: "#editing-#{item}")
    |> JS.add_class("hidden", to: "#editing-#{item}")
    |> JS.remove_class("hidden", to: "#preview-#{item}")
    |> JS.add_class("active", to: "#preview-#{item}")

  end

  def handle_event("send", params, socket) do
    IO.inspect "TEST"

     
    {:noreply, socket}
  end

  

  def render(assigns) do  
     ~H"""


    <%= for testbed <- @testbeds do %>
        <.modal id={"notes-modal-#{testbed.id}"}>
            <.button phx-click={button_event("invoke_editing",testbed.id)} id={"editing-button-#{testbed.id}"} class="hidden">
              Preview
            </.button>
            <.button phx-click={button_event("invoke_preview",testbed.id)} id={"preview-button-#{testbed.id}"} >
              Edit Me
            </.button>
            <div id={"editing-#{testbed.id}"}> PREVIEWING THE WORK</div>
            <form phx-submit = "send"   id={"preview-#{testbed.id}"} class="hidden">
              <textarea value = {testbed.note}></textarea>
              <.button type="submit" >SAVE WORK</.button>
            </form>
        </.modal>

      <%= if testbed.note do %>
        <div phx-click={show_modal("notes-modal-#{testbed.id}")}>READ ME  ...<%= testbed.note %></div>
      <% end %>
       
    <% end %>
       
      """
  end

end

EDIT Removed all the duplicate ID's again and it works the same as in the video.

defmodule AppWeb.PageLive do 

  use AppWeb, :live_view  
  alias App.Testbeds 
  def mount(_params, _session, socket)do   
     {:ok, assign(socket, testbeds: Testbeds.list_testbeds())}   
  end

    def button_event("invoke_editing", eleID) do
      IO.inspect eleID
    %JS{}
    |> JS.remove_class("active", to: "#editing_button_#{eleID}")
    |> JS.add_class("hidden", to: "#editing_button_#{eleID}")
    |> JS.remove_class("hidden", to: "#preview_button_#{eleID}")
    |> JS.add_class("active", to: "#preview_button_#{eleID}")
    |> JS.remove_class("active", to: "#preview_#{eleID}")
    |> JS.add_class("hidden", to: "#preview_#{eleID}")
    |> JS.remove_class("hidden", to: "#editing_#{eleID}")
    |> JS.add_class("active", to: "#editing_#{eleID}")
  end

  def button_event("invoke_preview",eleID) do
    IO.inspect eleID
    %JS{}
    |> JS.remove_class("active", to: "#preview_button_#{eleID}")
    |> JS.add_class("hidden", to: "#preview_button_#{eleID}")
    |> JS.remove_class("hidden", to: "#editing_button_#{eleID}")
    |> JS.add_class("active", to: "#editing_button_#{eleID}")
    |> JS.remove_class("active", to: "#editing_#{eleID}")
    |> JS.add_class("hidden", to: "#editing_#{eleID}")
    |> JS.remove_class("hidden", to: "#preview_#{eleID}")
    |> JS.add_class("active", to: "#preview_#{eleID}")

  end

  def handle_event("send", params, socket) do
    IO.inspect "TEST"
    IO.inspect params

     
    {:noreply, socket}
  end

  

  def render(assigns) do  
     ~H"""

    <%= for testbed <- @testbeds do %>
        <.modal id={"notes-modal-#{testbed.id}"}>
            <.button phx-click={button_event("invoke_editing",testbed.id)} id={"editing_button_#{testbed.id}"} class="hidden">
              Preview
            </.button>
            <.button phx-click={button_event("invoke_preview",testbed.id)} id={"preview_button_#{testbed.id}"}>
              Edit Me
            </.button>
            <div id={"editing_#{testbed.id}"}> PREVIEWING THE WORK</div>
            <form phx-submit = "send"   id={"preview_#{testbed.id}"} class="hidden">
              <textarea value = {testbed.note}></textarea>
              <.button type="submit" >SAVE WORK</.button>
            </form>
        </.modal>

    <%= if testbed.note do %>
      <div phx-click={show_modal("notes-modal-#{testbed.id}")}>READ ME  ...<%= testbed.note %></div>
    <% end %>
       
    <% end %>
       
      """
  end

end

Solution

  • defmodule AppWeb.PageLive do 
    
      use AppWeb, :live_view  
      alias App.Testbeds 
      def mount(_params, _session, socket)do   
         {:ok, assign(socket, testbeds: Testbeds.list_testbeds())}   
      end
    
        def button_event("invoke_editing", eleID) do
          IO.inspect eleID
        %JS{}
        |> JS.remove_class("active", to: "#editing_button_#{eleID}")
        |> JS.add_class("hidden", to: "#editing_button_#{eleID}")
        |> JS.remove_class("hidden", to: "#preview_button_#{eleID}")
        |> JS.add_class("active", to: "#preview_button_#{eleID}")
        |> JS.remove_class("active", to: "#preview_#{eleID}")
        |> JS.add_class("hidden", to: "#preview_#{eleID}")
        |> JS.remove_class("hidden", to: "#editing_#{eleID}")
        |> JS.add_class("active", to: "#editing_#{eleID}")
      end
    
      def button_event("invoke_preview",eleID) do
        IO.inspect eleID
        %JS{}
        |> JS.remove_class("active", to: "#preview_button_#{eleID}")
        |> JS.add_class("hidden", to: "#preview_button_#{eleID}")
        |> JS.remove_class("hidden", to: "#editing_button_#{eleID}")
        |> JS.add_class("active", to: "#editing_button_#{eleID}")
        |> JS.remove_class("active", to: "#editing_#{eleID}")
        |> JS.add_class("hidden", to: "#editing_#{eleID}")
        |> JS.remove_class("hidden", to: "#preview_#{eleID}")
        |> JS.add_class("active", to: "#preview_#{eleID}")
    
      end
    
      def handle_event("send", params, socket) do
        eleID = params["id_data"]
    
        %JS{}
        |> JS.remove_class("active", to: "#preview_button_#{eleID}")
        |> JS.add_class("hidden", to: "#preview_button_#{eleID}")
        |> JS.remove_class("hidden", to: "#editing_button_#{eleID}")
        |> JS.add_class("active", to: "#editing_button_#{eleID}")
        |> JS.remove_class("active", to: "#editing_#{eleID}")
        |> JS.add_class("hidden", to: "#editing_#{eleID}")
        |> JS.remove_class("hidden", to: "#preview_#{eleID}")
        |> JS.add_class("active", to: "#preview_#{eleID}")
        {:noreply, redirect(socket, to: "/")}
      end
    
      
    
      def render(assigns) do  
         ~H"""
        <%= for testbed <- @testbeds do %>
            <.modal id={"notes-modal-#{testbed.id}"}>
                <.button phx-click={button_event("invoke_editing",testbed.id)} id={"editing_button_#{testbed.id}"} class="hidden">
                  Preview
                </.button>
                <.button phx-click={button_event("invoke_preview",testbed.id)} id={"preview_button_#{testbed.id}"}>
                  Edit Me
                </.button>
                <div id={"editing_#{testbed.id}"}> PREVIEWING THE WORK</div>
                <form phx-submit = "send"   id={"preview_#{testbed.id}"} class="hidden">
              <input value = {testbed.id} name="id_data" hidden/>
                  <textarea value = {testbed.note} name="data"> </textarea>
                  <.button type="submit" >SAVE WORK</.button>
                </form>
            </.modal>
        <%= if testbed.note do %>
          <div phx-click={show_modal("notes-modal-#{testbed.id}")}>READ ME  ...<%= testbed.note %></div>
        <% end %>
        <% end %>
          """
      end
    end