Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Masatoshi Nishiguchi
Masatoshi Nishiguchi

Posted on

     

Elixir Phoenix form field one liner

Necessity is the mother of invention.

フォームのスタイリングを抽象化する方法はいくつかありますが、個人的にフォームフィールドはヘルパー関数を自作するのが一番シンプルな気がしており、実際にやってみた結果も気に入ってます。

やりたいこと

  • 各フォームフィールドを一行で書けるようにしたい。
  • 共通のCSSクラスは自動的に適用したい。

例えば、mix phx.gen.authコマンドで生成される以下のようなフォームフィールドがあります。

<%=labelf,:email%><%=email_inputf,:email,required:true%><%=error_tagf,:email%>
Enter fullscreen modeExit fullscreen mode

それをこのように共通のCSSクラスも含め一行で簡潔に記述したいのです。

<%=bulma_inputf,:email%>
Enter fullscreen modeExit fullscreen mode

動作環境

elixir          1.13.4-otp-24erlang          24.3.4
Enter fullscreen modeExit fullscreen mode
❯ mix phx.new --versionPhoenix installer v1.6.8
Enter fullscreen modeExit fullscreen mode

アイデア

カスタムビューヘルパーを書くために必要な知識とアイデアはElixir言語の作者José Valimさんがこの記事(Dynamic forms with Phoenix)の中で丁寧に解説してくれています。
ですのでそれを読めば大体わかります。

また、Phoenix自体がPhoenix.HTML.Form等ビルトインのヘルパー関数を多数持っているのでそれらを大いに活用することも大事だと思います。

やってみる

lib/my_app_web/views/input_helpers.exファイルを作成。

defmoduleMyAppWeb.InputHelpersdousePhoenix.HTML# TODO: define my custom view helper functionsend
Enter fullscreen modeExit fullscreen mode

lib/my_app_web.exview_helpers関数でそれを忘れずにインポートしておく。

   defp view_helpers do     quote do       # Use all HTML functionality (forms, tags, etc)       use Phoenix.HTML       # Import LiveView and .heex helpers (live_render, live_patch, <.form>, etc)       import Phoenix.LiveView.Helpers       import MyAppWeb.LiveHelpers       # Import basic rendering functionality (render, render_layout, etc)       import Phoenix.View+      import MyAppWeb.InputHelpers       import MyAppWeb.ErrorHelpers       import MyAppWeb.Gettext       alias MyAppWeb.Router.Helpers, as: Routes     end   end
Enter fullscreen modeExit fullscreen mode

あとはMyAppWeb.InputHelpersに好きなようにヘルパー関数を定義するだけ。
先日たまたまBulma CSSフレームワークを使って遊んでいた時に、それ用に一つ作ってみました。
一つのサンプルコードになるかもしれません。

defmoduleMyAppWeb.InputHelpersdousePhoenix.HTMLdefbulma_input(form,field,opts\\[])dolabel_opts=Keyword.take(opts,~w[required label]a)input_opts=Keyword.drop(opts,~w[required label]a)content_tag:div,class:"field"do[build_label(form,field,label_opts),build_input(form,field,input_opts),MyAppWeb.ErrorHelpers.error_tag(form,field)]endenddefbulma_checkbox(form,field,opts\\[])docontent_tag:label,class:"checkbox"do[checkbox(form,field,opts),' ',opts[:label]||field|>to_string()|>Phoenix.Naming.humanize()]endenddefpbuild_label(form,field,opts)dorequired=opts[:required]||Keyword.get(input_validations(form,field),:required)label_text=(opts[:label]||humanize(field))<>ifrequired,do:" *",else:""Phoenix.HTML.Form.label(form,field,label_text,class:"label")enddefpbuild_input(form,field,opts)doinput_fun_name=opts[:using]||Phoenix.HTML.Form.input_type(form,field)permitted_attributes=Keyword.drop(opts,[:using])input_class=caseinput_fun_namedo:textarea->"textarea "_->"input "end<>form_state_class(form,field)input_opts=[{:class,input_class}|permitted_attributes]|>Enum.reject(&is_nil(elem(&1,1)))content_tag:div,class:"control"doapply(Phoenix.HTML.Form,input_fun_name,[form,field,input_opts])endenddefpform_state_class(form,field)doconddo# 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-danger"true->"is-success"endendend
Enter fullscreen modeExit fullscreen mode

lib/my_app_web/views/error_helpers.exPhoenixが生成したerror_tagがあるので、そこのCSSクラスも必要に応じて変更します。

   def error_tag(form, field) do     Enum.map(Keyword.get_values(form.errors, field), fn error ->       content_tag(:span, translate_error(error),-       class: "invalid-feedback",+       class: "invalid-feedback help is-danger",         phx_feedback_for: input_name(form, field)       )     end)
Enter fullscreen modeExit fullscreen mode

Bulmaでスタイリングされたフォームフィールドを生成するbulma_input関数ができました。

bulma_inputf,:email
Enter fullscreen modeExit fullscreen mode

使用するPhoenix.HTML.Formの関数を切り替えるオプションも受け付けます。

# Phoenix.HTML.Form.text_input/3の代わりにPhoenix.HTML.Form.textarea/3を使用したい場合bulma_inputf,:email,using::textarea
Enter fullscreen modeExit fullscreen mode

必要に応じてHTML 属性を追加できるようにしました。

bulma_inputf,:email,placeholder:"E-mail",autocomplete:"off"
Enter fullscreen modeExit fullscreen mode

IExで検証

テンプレート上のフォームで実際に実装した方が早いですが、興味があったのでIEx上でランできる方法を探しました。

Phoenixのソースコードのテストの中にヒントがあったので、その知識で適当にフォームを生成します。

iexaliasMyAppWeb.AccountsaliasMyAppWeb.Accounts.Userchangeset=Accounts.change_user_registration(%User{})form=Phoenix.HTML.Form.form_for(changeset,"/registration",[])bulma_input(form,:email,placeholder:"E-mail",autocomplete:"off")|>Phoenix.HTML.Safe.to_iodata()|>to_string()|>IO.puts()
Enter fullscreen modeExit fullscreen mode
<divclass="field"><labelclass="label"for="user_email">Email *</label><divclass="control"><inputautocomplete="off"class="input "id="user_email"name="user[email]"placeholder="E-mail"type="email"></div></div>
Enter fullscreen modeExit fullscreen mode

🎉

Elixirコミュニティに初めて接する方は下記がオススメです

Elixirコミュニティ の歩き方 -国内オンライン編-

https://speakerdeck.com/elijo/elixirkomiyunitei-falsebu-kifang-guo-nei-onrainbian

image.png

日本には28箇所のElixirコミュニティがあります

image.png

日程からイベントを探すならElixirイベントカレンダー📆

** Elixirイベントカレンダー **

https://elixir-jp-calendar.fly.dev/

image.png

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

たのしむ
  • Location
    🇯🇵
  • Work
    ソフトウエアエンジニア
  • Joined

More fromMasatoshi Nishiguchi

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp