pejrich

pejrich

How to add JS loaded textarea with LiveView form?

I’m trying to use this WYSIWYG Markdown editor in my live view form https://github.com/sparksuite/simplemde-markdown-editor (Does anyone know which editor Elixir Forum uses, I want something like this).

Here’s my form

<div class="message_form" style="height: 100px;">
        <%= form_for :message, "#", [phx_change: :typing, phx_submit: :save], fn f -> %>
          <%= textarea f, :text, id: "wysiwyg", phx_hook: "messageTextarea" %>
          <%= submit "Save" %>
        <% end %>
      </div>

Here’s my JS

Hooks.messageTextarea = {
  mounted() {
    new SimpleMDE({ element: document.getElementById("wysiwyg") });
  },
  updated() {
    new SimpleMDE({ element: document.getElementById("wysiwyg") });
  },

The editor appears when the page loads, but when the first character is typed, there is a reload, the editor appears again, but there’s no text in it. If I log the events, I seem to get a bunch of beforeUpdate and updated events firing. How can I keep my events, but stop it from reloading the text area on each character typed?

Most Liked

riverside

riverside

Hopefully this helps out anyone in the future. Based on the tip found here. For my use case, what i did was:

  1. Set up a form in a live component.
def render(assigns) do
  ~L"""
  <div class="form_container">
    <%= f = form for @changeset, 
       #,
       phx_target: @myself,
       phx_change: "validate_form" ,
       id: "richtext_form"
    %>
      <div 
        phx-hook="RichText"
        phx-update="ignore"
        phx-target="<%= @myself %>"
        id="richtext_container"
      >
        <%= textarea f, 
          :the_content, 
          value: @clientside_richtext_input 
        %>
      </div>
    </form>
  </div>
  """
end 

Reason being is i still want to validate the form input on the server side. But the pushing the params after a validation function in the returning {:noreply, socket |> ...} kept on failing and the data being put into to the rich text wasn’t stored anywhere. Which led to:

  1. Create two handle events. One for the form validation, the other to handle the clientside hook and pass the data into an assign that will be set as the value for the textarea.
def handle_event(
  "validate_form", 
  %{"richtext_form" => params}, 
  socket) do 
  {:noreply, socket |> validate_form(params)}
end

def handle_event(
  "handle_clientside_richtext", 
  %{"richtext_data" => richtext_data},
  socket) do 
  {:noreply, 
    socket 
    |> assign_form(:clientside_richtext_input, richtext_data)
    |> push_event("richtext_event", %{richtext_data: richtext_data})}
end

This is so that the richtext data can be stored by cycling back and forth between the client and backend for when the live component is updated. Now for the same thing inside the JS code.

  1. In a hook object do
const RichText = {
  mounted(){
    init(this.el.querySelector("#richtext_form_the_content"))
  },
  updated(){
    const textArea = init(this.el.querySelector("#richtext_form_the_content"));
    textArea.codemirror.on("change", ( ) => {
      this.pushEventTo(
        this.el, 
        "handle_clientside_richtext",
        {richtext_data: textArea.value()}
      );
      this.handleEvent(
        "richtext_event",
        (richtext_data) => textArea.value(richtext_data)
      )
    })
  }
}

The logic is in the updated function since mounted rich text area gets erased right away.

  1. And finally to have easyMDE be usable do
import easyMDE from "easyMDE";

const init = (element) => new easyMDE({
  element: element,
  forceSync: true
})

The forceSync: true is for the data to be set as the textarea value on the clientside and play well with the backend.

It took a few days of looking back and forth at different examples, reading the forum, rereading the docs, looking at source code, and feeling dumb. This is the simplest solution that i can think of once it all clicked.

*edit typo fix: phx-update: "TextEditor" to phx-hook: "RichText"

Where Next?

Popular in Questions Top

minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; ...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
Lily
In templates/appointment/index.html.eex: &lt;%= for appointment &lt;- @appointments do %&gt; &lt;tr&gt; &lt;td&gt;&lt;%= appoi...
New
greenz1
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New

Other popular topics Top

minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 31494 112
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 40042 209
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New

We're in Beta

About us Mission Statement