JSON field input as part of a phoenix form

Just for anyone else running into the same thing: I have a JSON (actually JSONB) field in Postgres that’s defined as a map field on my ecto schema:

field :payload, :map

To make this editable, in form_component.ex I had to specify a textarea input field, e.g.

    <.input
      field={@form[:payload]}
      type="textarea"
      label="Payload"
      autocomplete="off"
      autocorrect="off"
      rows="8"
      value={@form[:payload].value |> Jason.encode!(pretty: true)}
    />

But then I also needed to update the schema’s changeset/2 function like:

  def changeset(event, attrs) do
    attrs =
      with true <- is_binary(attrs["payload"]),
           {:ok, decoded_value} <- Jason.decode(attrs["payload"]) do
        Map.put(attrs, "payload", decoded_value)
      else
        _ -> attrs
      end

    event
    |> cast(attrs, [
      ...
      :payload
    ])
    |> validate_required([
      ...
    ])
  end

What this does is to try converting the incoming text from the textarea field into a map.

If it succeeds, it passes the map along, but if it fails (e.g. while the user is still busy typing and the input doesn’t contain valid json), then the original string is passed along, which causes the cast function to trigger a validation error on the field, because it’s expecting a map.

I’m not sure if there’s a better way, but this is the simplest way that I could come up with that handles the happy path but also triggers a validation error when needed.

Maybe skip (live) validation as you type or have a validation changeset that dosnt include the json field.