Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Working with embeds_many #3

Open
izzues opened this issue May 31, 2020 · 2 comments
Open

Working with embeds_many #3

izzues opened this issue May 31, 2020 · 2 comments

Comments

@izzues
Copy link

izzues commented May 31, 2020

Hello!

Is it possible to use your library with an embeds_many? I'm trying to use it in a LiveView live_modal, with deletions enabled by the virtual field method described in the README. This is what I have in the form_component.html.leex template:

<%= dynamic_inputs_for f, :items, %Item{title: "", content: ""}, fn item -> %>
  <%= text_input item, :title %>
  <%= text_input item, :content %>
  <%= dynamic_delete_button "Remove" %>
  <%= error_tag answer, :content %>
<% end %>
<%= dynamic_add_button :items, "Add" %>

If I add an item, save the form, then remove the item, everything works fine. However, if I add an item, remove it and only then try to save the form, I get this error:

** (RuntimeError) cannot delete related %Item{title: "", content: "", delete: nil, id: nil} because it already exists and it is not currently associated with the given struct. Ecto forbids casting existing records through the association field for security reasons. Instead, set the foreign key value accordingly

I suppose the fix would be to actually delete the field if it wasn't already saved, I just don't know how to do it.

Thanks a lot for creating this library!

@MamesPalmero
Copy link
Owner

Hi!

Is it possible to use your library with an embeds_many?

Yes, you should be able to use it with embedded schemas.

For this case you could mark the changeset action as :ignore. Also, to detect if the element is new and has been marked for deletion, for embedded schemes I think you could add a new virtual field, called new for example, to mark the elements you add in the form. The code could be something like that:

  • In your Item module
  schema "items" do
    ...
    field :delete, :boolean, virtual: true
    field :new, :boolean, virtual: true
  end

  defp maybe_mark_for_deletion_or_ignore(changeset) do
    delete? = get_change(changeset, :delete)
    new? = get_change(changeset, :new)

    if delete? do
      %{changeset | action: if(new?, do: :ignore, else: :delete)}
    else
      changeset
    end
  end
end
  • In your form
<%= dynamic_inputs_for f, :items, %Item{new: true}, [only_mark_deleted: true], fn item -> %>
  <%= hidden_input item, :new %>
  <%= text_input item, :title %>
  <%= text_input item, :content %>
  <%= dynamic_delete_button "Remove" %>
  <%= error_tag answer, :content %>
<% end %>
<%= dynamic_add_button :items, "Add" %>

Please note that I use the only_mark_deleted option for the form. So only the class deleted-fields will be added to the element and you will have to manage the visibility of the element and the validation of the required input fields by means of events.

There may be something wrong with the code because I haven't checked it, but I hope it will serve as an example for you.

@gdub01
Copy link

gdub01 commented Sep 10, 2020

Thanks a lot for this library! It's awesome =)
I had this question too.. what worked for me for both new and existing embedded items was:

if changeset.changes == %{}, do: %{changeset | action: :ignore}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants