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.