tfwright / live_admin

Low-config admin UI for Phoenix apps, built on LiveView
MIT License
248 stars 22 forks source link

Managing Associations within Parent Forms #68

Closed KalleJoP closed 9 months ago

KalleJoP commented 10 months ago

Is it possible to edit has_many associations in the parent schema?

I have already come across this issue on GitHub: https://github.com/tfwright/live_admin/issues/15, but it only explains how to select the parent ID.

For instance:

It would be beneficial if I could create or edit ProductVariants for a Product directly within the product form.

Product Schema:

  use Ecto.Schema
  use LiveAdmin.Resource
  import Ecto.Changeset

  schema "products" do
    field :description, :string
    field :pseudo_price, :decimal
    field :title, :string
    field :gender, Ecto.Enum, values: [:male, :female, :unisex]

    has_many :product_variants, ClothoBackend.Products.ProductVariant

    timestamps()
  end

  @doc false
  def changeset(product, attrs) do
    product
    |> cast(attrs, [
      :title,
      :description,
      :pseudo_price,
      :gender
    ])
    |> cast_assoc(:product_variants)
    |> validate_required([
      :title,
      :description,
      :pseudo_price,
      :gender
    ])
  end
end

Product Variants Schema:

  use Ecto.Schema
  use LiveAdmin.Resource
  import Ecto.Changeset

  schema "product_variants" do
    field(:pseudo_price, :decimal)
    field(:title, :string)

    belongs_to(:product, ClothoBackend.Products.Product)

    field :temp_id, :string, virtual: true
    field :delete, :boolean, virtual: true
    field :edit, :boolean, virtual: true

    timestamps()
  end

  @doc false
  def changeset(product_variant, attrs) do
    product_variant
    # So its persisted
    |> Map.put(:temp_id, product_variant.temp_id || attrs["temp_id"])
    |> cast(attrs, [:title, :pseudo_price, :delete, :edit])
    |> validate_required([:pseudo_price])
    |> maybe_mark_for_deletion()
  end

  defp maybe_mark_for_deletion(%{data: %{id: nil}} = changeset), do: changeset

  defp maybe_mark_for_deletion(changeset) do
    if get_change(changeset, :delete) do
      %{changeset | action: :delete}
    else
      changeset
    end
  end
end
tfwright commented 10 months ago

Hi @KalleJoP

Currently this is only possible by overriding the form component for your product resource. Theoretically it should be possible to add built in support using a similar strategy that is currently used to support embeds, but assoc handling can be tricky so I'm not 100% confident how much complexity it would add. There are also UX issues to consider, since an association could contain a large amount of records that would have to be preloaded in order to even render the form and in a lot of cases this would not be desirable, so I don't think it could be enabled by default for every association, which inclines me to think it's better left to overrides.