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

Set form attribute on inputs when form is given an id #610

Open
simonmcconnell opened this issue Jun 1, 2022 · 2 comments
Open

Set form attribute on inputs when form is given an id #610

simonmcconnell opened this issue Jun 1, 2022 · 2 comments

Comments

@simonmcconnell
Copy link
Contributor

When you're creating forms and associating inputs with them by using the form attribute like so

<form id="form-1"></form>
<form id="form-2"></form>

<input type="text" name="form-1-name" form="form-1" />
<input type="text" name="form-2-name" form="form-2" />

... you need to get the form from the Context and set opts={form: form.id}. It would be nice if the input components set the form attribute if the form has an id such that the below could use the IdealFormsComponent. Would you consider a PR that added this? Or should I be asking the question on phoenix_html?

defmodule MyWeb.FormsComponent do
  use Surface.Component
  alias Surface.Components.Context
  alias Surface.Components.Form.{Field, Label, TextInput}

  def render(assigns) do
    ~F"""
    <Context get={form: form}>
      <Field name={:thing}>
        <Label text="Thing" form={form} />
        <TextInput form={form} opts={form: form.id} />
      </Field>
    </Context>
    """
  end
end

defmodule MyWeb.FormsComponentNamedspaceContext do
  use Surface.Component
  alias Surface.Components.Context
  alias Surface.Components.Form.{Field, Label, TextInput}

  def render(assigns) do
    ~F"""
    <Context get={Surface.Components.Form, form: form}>
      <Field name={:thing}>
        <Label text="Thing" />
        <TextInput opts={form: form.id} />
      </Field>
    </Context>
    """
  end
end

defmodule MyWeb.IdealFormsComponent do
  use Surface.Component
  alias Surface.Components.Context
  alias Surface.Components.Form.{Field, Label, TextInput}

  def render(assigns) do
    ~F"""
    <Field name={:thing}>
      <Label text="Thing" />
      <TextInput />
    </Field>
    """
  end
end

defmodule MyWeb.FormsLive do
  use MyWeb, :surface_live_view
  alias MyWeb.FormsComponent
  alias MyWeb.FormsComponentNamedspaceContext
  alias Surface.Components.Context

  @impl true
  def mount(_params, _session, socket) do
    {:ok,
     assign(socket,
       thing1: changeset(%{thing: "thing1"}),
       thing2: changeset(%{thing: "thing2"})
     )}
  end

  defp changeset(data \\ %{}, params) do
    types = %{thing: :string}
    Ecto.Changeset.cast({data, types}, params, Map.keys(types))
  end

  @impl true
  def render(assigns) do
    ~F"""
    <main>
      {form1 =
        form_for(@thing1, "#",
          as: :thing1,
          id: "thing1form",
          phx_change: "change1",
          autocomplete: "off"
        )}
      <#Raw></form></#Raw>

      {form2 =
        form_for(@thing2, "#",
          as: :thing2,
          id: "thing2form",
          phx_change: "change2",
          autocomplete: "off"
        )}
      <#Raw></form></#Raw>

      {!--
      <Context put={Surface.Components.Form, form: form1}>
        <IdealFormsComponent />
      </Context>
      --}

      <Context put={Surface.Components.Form, form: form1}>
        <FormsComponentNamedspaceContext />
      </Context>

      <Context put={form: form2}>
        <FormsComponent />
      </Context>
    </main>
    """
  end

  @impl true
  def handle_event(event, params, socket) do
    IO.inspect([event, params], label: "forms live")
    {:noreply, socket}
  end
end
@msaraiva
Copy link
Member

msaraiva commented Jun 2, 2022

Hi @simonmcconnell!

Yeah, I think the built-in inputs could do that. Just not sure if it should be the default behavior. Would there be any side effect on always doing that?

@miguel-s WDYT?

@simonmcconnell
Copy link
Contributor Author

simonmcconnell commented Jun 3, 2022

You associate an input with a form by either putting it in the form or setting the form attribute. Forms can't be nested but this is how we can get around that, i.e. allowing us to intermingle different parts of forms on a page or place inputs for a form anywhere we like on the page. With this proposed change, the input would have the form attribute set if either the user has nested the input inside a form (usual way of doing things) and given that form an id, or the user has put the form into the Context themselves. Either way, the input will be associated with the form that they expect, I expect.

MDN on the form attribute of an input:

A string specifying the <form> element with which the input is associated (that is, its form owner). This string's value, if present, must match the id of a <form> element in the same document. If this attribute isn't specified, the <input> element is associated with the nearest containing form, if any.

The form attribute lets you place an input anywhere in the document but have it included with a form elsewhere in the document.

Note: An input can only be associated with one form.

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

2 participants