Phoenix의 폼 입력을 통하는 헤르파로 사용하기

30538 단어 formelixirphoenix
フォームは一度スタイリングができたら、他のページでも同じスタイリングでいくことがほとんどであると思います.inputタグを共通InputHelpersとして隠蔽すると、コーディングが楽になり、CSSクラスを気にせずに済むようになり、またTEN プレートもすきりとして読みやすくなります.

2021/3/12(金)〜 2021/3/15(月)開催のautoracex #16 での成果입니다.

TL;DR



元ネタは José ValimさんDynamic forms with Phoenix ですが、それをもとに必須項目の印を表示させたり、 Bootstrap 4 に対応させたりしました.これは一例です.ひとそれぞれ好きなようにElixirで카스타마이즈데키마스.

以下のようなのフォームがあったとして


카스탐input_tag 헤르파를 사용하는 라벨, 스타일링, 에라멧세이지를 を含んだHTML을 움직이는 に生成させます.

<%= f = form_for @changeset, "#", phx_submit: "submit-check-in-form" %>
  <%= input_tag f, :name %>
  <%= input_tag f, :phone %>
  <%= submit "Check In", phx_disable_with: "Saving ...", class: "btn btn-primary" %>
</form>


未提出、提出後GOOD、提出後BAD의 3파탄の状態が考えられます.formの状態に対応したCSScrasto共にHTMLが生成されます.

formtype은, フィールド名から推測して生成します.데포르트はPhoenix.HTML.Form.text_input/3
입니다.


피르드명
HTML 생성에 사용하는 방법

:email Phoenix.HTML.Form.email_input/3
:password Phoenix.HTML.Form.password_input/3
:search Phoenix.HTML.Form.search_input/3
:url Phoenix.HTML.Form.url_input/3



<!-- 提出前 -->
<div class="form-group">
  <label for="volunteer_name">Name *</label>
  <input type="text"
          class="form-control "
          id="volunteer_name"
          name="volunteer[name]"
          placeholder="Name">
</div>

<!-- 提出後BAD -->
<div class="form-group">
  <label for="volunteer_name">Name *</label>
  <input type="text"
          class="form-control is-invalid" <-- CSSが変化
          id="volunteer_name"
          name="volunteer[name]"
          placeholder="Name"
          value="">
  <span class="invalid-feedback d-inline-block" 
        phx-feedback-for="volunteer_name">
        can't be blank
  </span>
</div>

<!-- 提出後GOOD -->
<div class="form-group">
  <label for="volunteer_name">Name *</label>
  <input type="text"
          class="form-control is-valid" <-- CSSが変化
          id="volunteer_name"
          name="volunteer[name]"
          placeholder="Name"
          value="Masatoshi">
</div>

카스탐 input_tag 헤르파実装예



defmodule MnishiguchiWeb.InputHelpers do
  use Phoenix.HTML

  @custom_field_form_mapping %{
    "phone" => :telephone_input
  }

  @doc """
  Dynamically generates a Bootstrap 4 form input field.
  http://blog.plataformatec.com.br/2016/09/dynamic-forms-with-phoenix/

  ## Examples

      input_tag f, :name, placeholder: "Name", autocomplete: "off"
      input_tag f, :phone, using: :telephone_input, placeholder: "Phone", autocomplete: "off"

  """
  def input_tag(form, field, opts \\ []) do
    # Some input type can be inferred from the field name.
    input_fun_name = opts[:using] || Phoenix.HTML.Form.input_type(form, field, @custom_field_form_mapping)
    required = opts[:required] || form |> input_validations(field) |> Keyword.get(:required)
    label_text = opts[:label] || humanize(field)

    permitted_input_opts = Enum.filter(opts, &(elem(&1, 0) in [:id, :name, :autocomplete, :placeholder]))
    phx_attributes = Enum.filter(opts, &String.starts_with?(to_string(elem(&1, 0)), "phx_"))
    custom_class = [class: "form-control #{form_state_class(form, field)}"]

    input_opts =
      (permitted_input_opts ++ phx_attributes ++ custom_class)
      |> Enum.reject(&is_nil(elem(&1, 1)))

    content_tag :div, class: "form-group" do
      label = label_tag(form, field, label_text, required)
      input = apply(Phoenix.HTML.Form, input_fun_name, [form, field, input_opts])
      error = MnishiguchiWeb.ErrorHelpers.error_tag(form, field)

      [label, input, error]
    end
  end

  defp form_state_class(form, field) do
    cond do
      # Some forms may not use a Map as a source. E.g., :user
      !is_map(form.source) -> ""
      # Ignore Conn-based form.
      Map.get(form.source, :__struct__) == Plug.Conn -> ""
      # The form is not yet submitted.
      !Map.get(form.source, :action) -> ""
      # This field has an error.
      form.errors[field] -> "is-invalid"
      true -> "is-valid"
    end
  end
end

입력 유형



Phoenix.HTML.Form.input_type/3 により 、 、 입력 項目 項目 名 を もと もと に に に に の の 決定 決定 し ます ます. 仕組 み は シンプル です.. 予め 用意 用意 さ さ れ た マッピング が 使用 使用 さ れ. デフォルト の マッピング は は 下記 の の です です.. 第 3 3 引数 し し し し し し し し し し し の の の の の マッピング マッピング マッピング マッピング マッピング マッピング マッピング マッピング マッピング し し し し し し し し し し し し マッピング マッピング マッピング マッピング マッピング マッピング マッピング は 下記 の の です です です. ∎ていするとデフォルトのマッpingにマージされます.

%{"email"    => :email_input,
  "password" => :password_input,
  "search"   => :search_input,
  "url"      => :url_input}

:xxx_input アトムは Phoenix.HTML.Form に予め用意された関数名와 一致している必要があります.

理解を深めるために、Iexで挙動を確認してみます.例では、 volunteers テーブルと Volunteer skiーmaがあることを想定しています.

iex> alias Mnishiguchi.Volunteers.Volunteer
iex> import Ecto.Changeset

iex> changeset = %Volunteer{} |> cast(%{}, [:name]) |> validate_required([:name])
#Ecto.Changeset<
  action: nil,
  changes: %{},
  errors: [
    name: {"can't be blank", [validation: :required]},
  ],
  data: #Mnishiguchi.Volunteers.Volunteer<>,
  valid?: false
>

iex> form = Phoenix.HTML.Form.form_for changeset, "#"
%Phoenix.HTML.Form{
  action: "#",
  data: %Mnishiguchi.Volunteers.Volunteer{
    __meta__: #Ecto.Schema.Metadata<:built, "volunteers">,
    id: nil,
    inserted_at: nil,
    name: nil,
    updated_at: nil
  },
  errors: [],
  hidden: [],
  id: "volunteer",
  impl: Phoenix.HTML.FormData.Ecto.Changeset,
  index: nil,
  name: "volunteer",
  options: [method: "post"],
  params: %{},
  source: #Ecto.Changeset<
    action: nil,
    changes: %{},
    errors: [
      name: {"can't be blank", [validation: :required]},
    ],
    data: #Mnishiguchi.Volunteers.Volunteer<>,
    valid?: false
  >
}

# using default mapping
iex> Phoenix.HTML.Form.input_type(form, :name)
:text_input

iex> Phoenix.HTML.Form.input_type(form, :email)
:email_input

iex> Phoenix.HTML.Form.input_type(form, :search)
:search_input

iex> Phoenix.HTML.Form.input_type(form, :password)
:password_input

iex> Phoenix.HTML.Form.input_type(form, :url)
:url_input

# 第3引数にカスタムマッピングを指定するとデフォルトにマージされます。
iex> Phoenix.HTML.Form.input_type(form, :denwa, %{"denwa" => :telephone_input})
:telephone_input


必須項目かどうか



必須項目かどうかは Phoenix.HTML.Form.input_validations/2 で確認できます.

iex> form |> input_validations(:name)
[required: true]

iex> form |> input_validations(:name) |> Keyword.get(:required)
true

iex> form |> input_validations(:hello) |> Keyword.get(:required)
false


HTMLをElixir로 끝내기



Phoenix.HTML.Tag.content_tag/2 を用い、ElixirでHTMLを組み立てることができます.他にも同様の関数が Phoenix.HTML.Form functions に用意されてます.

iex> Phoenix.HTML.Tag.content_tag(:p, "hello")
{:safe, [60, "p", [], 62, "hello", 60, 47, "p", 62]}

iex> Phoenix.HTML.Tag.content_tag(:p, "hello") |> Phoenix.HTML.safe_to_string
"<p>hello</p>"


인간화하다



Phoenix.HTML.Form.humanize/1 が便利입니다.

iex> Phoenix.HTML.Form.humanize("name")
"Name"

iex> Phoenix.HTML.Form.humanize("hello_world")
"Hello world"


資料



  • Dynamic forms with Phoenix by José Valimさん
  • Phoenix.HTML.Form functions
  • Phoenix.HTML.Form.humanize/1
  • Phoenix.HTML.Form.input_type/3
  • Phoenix.HTML.Form.input_validations/2
  • Phoenix.HTML.Tag.content_tag/2
  • 좋은 웹페이지 즐겨찾기