How to use a list of items (array) with LiveView form

Hi,

I would like to save post tags in an array so in the schema I added

field :tags, {:array, :string}, default: []

and in Liveview form, I added

<.input field={@form[:tags]} type="text" label="Tags" />

I want to build it in a way so that a user can enter multiple tags in an input separated by comma (tag1, tag2, tag3) but I always get invalid input on form validation.

So, my question is, how to properly get multiple tags and save them as an array so that Liveview form validation passes?

https://hexdocs.pm/ecto/constraints-and-upserts.html

That one should help getting there.

1 Like

OMG, can’t believe I missed this, thanks a lot.

@LostKobrakai I followed the tutorial you’ve sent me and it worked perfectly, data is saved to the database, but how do I get it back? As there are multiple tags they come back inside a list.

tags: [
    %MyApp.Tag{
      __meta__: #Ecto.Schema.Metadata<:loaded, "tags">,
      id: 1,
      name: "aaa"
    },
    %MyApp.Tag{
      __meta__: #Ecto.Schema.Metadata<:loaded, "tags">,
      id: 2,
      name: "bbb"
    },
    %MyApp.Tag{
      __meta__: #Ecto.Schema.Metadata<:loaded, "tags">,
      id: 3,
      name: "ccc"
    }
  ]
<.input field={@form[:tags]} type="text" label="Tags" />

How do I get those tags back to the text_input field, separated by comma so users could easily edit them?

You need to handle that manually just like the splitting of the parameters when going the other direction.

Yeah, I get that, but I’m struggling with getting it back to LiveView for the last 2 hours and to be honest, I have no idea how to even approach this, everything I tried so far failed…

The easy and dirty way:

In your template, where :split_tags isn’t a field in your schema

<.input field={@form[:split_tags]} label="Tags" />

When you’re creating the form:

    video = Videos.get_video_by_slug!(video_slug)

    split_tags = 
        video 
        |> Map.get(:tags, []) 
        |> Enum.map(fn t -> t.name end) |> Enum.join(", ")

    form =
      video
      |> Videos.change_video()

    data = form.data |> Map.put(:split_tags, split_tags)
    form = form |> Map.put(:data, data) |> to_form()

And since you followed the link then when you’re handling the save the split_tags will be sent in the params and you just overwrite the tags param with the split_tags and off you go.

The cleanest way would be using virtual fields and resolving it in the context layer.

1 Like

To convert the list of Tags into a single string you could do something like:

tags
|> Enum.map(&(&1.name))
|> Enum.join(“, “)

Not sure if this fits into your code and I have not tested it since I’m on mobile.

1 Like