ash-project / ash-project/ash_phoenix
Support unstructured data in nested forms
- Dominant language
- Elixir
- Stars
- 164
- Forks
- 102
- Avg merge
- 12h 21m
- Merged PRs (30d)
- 2
Description
### Code of Conduct
- [x] I agree to follow this project's Code of Conduct
### AI Policy
- [x] I agree to follow this project's AI Policy, or I agree that AI was not used while creating this issue.
### Is your feature request related to a problem? Please describe.
Hi and thanks for your work on the Ash ecosystem ❤️
I am trying to create a form with unstructured nested data.
It is probably easiest explained with an example. I have a print where the fields that I should expect is defined in a template.
```elixir
defmodule MyApp.Template do
attributes do
attribute :fields, {:array, :string}
end
end
defmodule MyApp.Print do
use Ash.Resource, domain: MyApp
attributes do
attribute :params, :map
end
actions do
create :create do
argument :template, MyApp.Template, allow_nil?: false
argument :params, :map
change fn changeset, _ ->
template = Ash.Changeset.get_argument(changeset, :template)
params = Ash.Changeset.get_argument(changeset, :params) || %{}
changeset = Ash.Changeset.change_attribute(changeset, :params, params)
Enum.reduce(template.fields, changeset, fn field, changeset ->
if params[to_string(field)] in [nil, ""] do
Ash.Changeset.add_error(changeset, field: field, message: "is required", path: [:params, field])
else
changeset
end
end)
end
end
end
end
```
This code should work perfectly well, but now I am not able to find any way to make a form that would expect this data. The form should look something like this, but I don't see any way to setup a form with for_create.
```heex
<.inputs_for :let={params} field={@form[:params]}>
<%= for field <- @template.fields do %>
<.input field={params[field]} label={to_string(field)} />
<% end %>
```
### Describe the solution you'd like
I vibe coded a solution that seems to work. I allowed for an API like this for creating the form:
```elixir
form = AshPhoenix.Form.for_create(MyApp.Print, :create,
domain: MyApp,
forms: [params: [map?: true]]
)
```
Another alternative could be for the resource to be a map:
```elixir
form = AshPhoenix.Form.for_create(MyApp.Print, :create,
domain: MyApp,
forms: [params: [resource: :map]]
)
```
I would also be super interested in your thoughts on alternative solutions.
### Describe alternatives you've considered
_No response_
### Additional context
I would be happy to create a PR with my current solution (of course after having cleaned up and reviewed the AI slop) or create a different solution, if you have any better ideas.
Contributor guide
Research direction
Start with the AshPhoenix.Form.for_create call and the HEEx inputs_for example in the issue, then trace how the params map is represented and submitted. Compare the proposed forms: [map?: true] and [resource: :map]. Done means a form can render, submit, and validate the unstructured nested params with dynamic fields from the template.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- elixir
- Domain
- frontend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100