こんにちは!
プログラミング未経験文系出身、Elixirの国に迷い込んだ?!見習いアルケミストのaliceと申します。
今回はcore_components.exの外に関数コンポーネントを作ってみました。
■「Phoenix1.7 関数コンポーネントで遊んでみる」シリーズの目次
① モーダル上に文字数制限無しのtextareaを作成する
|> ②Phoenix.Component.attr/3 を使って関数コンポーネントを作る
|> ③core_components.exの外に関数コンポーネントを作る
|> ④自作した関数コンポーネントを<.関数名>で呼び出す
目的
前回作ったtextareaをレンダリングする関数コンポーネントinput(%{type: "textarea"} = assigns)
について、core_components.exの外に別モジュールとして作り、そちらを呼び出すように切り替えたい。
なお、新しいモジュールを置く場所と名前はlib/contact_web/components/my_components.ex
とする。
前回の記事はこちら
実行環境
Windows 11 + WSL2 + Ubuntu 22.04
Elixir v1.14.3
Phoenix v1.7.3
仮説
-
my_components.ex
└use Phoenix.Component
を記述するのでは?
└core_components.ex
内input(%{type: "textarea"} = assigns)
を真似して作成するのでは? -
form_component.ex
└呼び出す関数の切り替えが必要では?
結果
以下のように実施して目的を達成しました。
1.my_components.ex内にattr/3とslot/3を移植する1
+ defmodule ContactWeb.Mycomponents do
+ use Phoenix.Component
+ attr :id, :any, default: nil
+ attr :name, :any
+ attr :label, :string, default: nil
+ attr :value, :any
+ attr :rows, :integer, default: 2
+ attr :type, :string,
+ default: "text",
+ values: ~w(checkbox color date datetime-local email file hidden month number password
+ range radio search select tel text textarea time url week)
+ attr :field, Phoenix.HTML.FormField,
+ doc: "a form field struct retrieved from the form, for example: @form[:email]"
+ attr :errors, :list, default: []
+ attr :checked, :boolean, doc: "the checked flag for checkbox inputs"
+ attr :prompt, :string, default: nil, doc: "the prompt for select inputs"
+ attr :options, :list, doc: "the options to pass to Phoenix.HTML.Form.options_for_select/2"
+ attr :multiple, :boolean, default: false, doc: "the multiple flag for select inputs"
+ attr :rest, :global,
+ include: ~w(accept autocomplete capture cols disabled form list max maxlength min minlength
+ multiple pattern placeholder readonly required rows size step)
+ slot :inner_block
2. my_components.ex内にinput/1の共通処理部分を移植する
これはまだコピペしただけです。手順3のaliasを書かないとエラーになります。
+ def input(%{field: %Phoenix.HTML.FormField{} = field} = assigns) do
+ assigns
+ |> assign(field: nil, id: assigns.id || field.id)
+ |> assign(:errors, Enum.map(field.errors, &translate_error(&1)))
+ |> assign_new(:name, fn -> if assigns.multiple, do: field.name <> "[]", else: field.name end)
+ |> assign_new(:value, fn -> field.value end)
+ |> input()
+ end
3. core_components.exおよびmy_components.exのaliasを書く
my_components.ex
内のinput/1
と、core_components.ex
内のinput/1
が呼び分けできるようにmy_components.ex
およびcore_components.ex
のaliasを書きます。
defmodule ContactWeb.Mycomponents do
use Phoenix.Component
+ alias ContactWeb.CoreComponents
+ alias ContactWeb.Mycomponents
aliasを書いたので以降は「モジュール名.関数名」で呼び出します。
def input(%{field: %Phoenix.HTML.FormField{} = field} = assigns) do
assigns
|> assign(field: nil, id: assigns.id || field.id)
- |> assign(:errors, Enum.map(field.errors, &translate_error(&1)))
+ |> assign(:errors, Enum.map(field.errors, &CoreComponents.translate_error(&1)))
|> assign_new(:name, fn -> if assigns.multiple, do: field.name <> "[]", else: field.name end)
|> assign_new(:value, fn -> field.value end)
- |> input()
+ |> Mycomponents.input()
end
4. my_components.ex内にinput(%{type: "textarea"} = assigns)を移植し、手順3を適用する
core_components.ex内のinput(%{type: "textarea"} = assigns)とUI上で見分けがつくように、CSSプロパティにbg-green-500/50
2を指定しています。
手順3の通り、core_components.exの関数は「モジュール名.関数名」で呼び出します。
+ def input(%{type: "textarea"} = assigns) do
+ ~H"""
+ <div phx-feedback-for={@name}>
+ <CoreComponents.label for={@id}><%= @label %></CoreComponents.label>
+ <textarea
+ id={@id}
+ name={@name}
+ rows={@rows}
+ class={[
+ "mt-2 block w-full rounded-lg text-zinc-900 focus:ring-0 sm:text-sm sm:leading-6",
+ "phx-no-feedback:border-zinc-300 phx-no-feedback:focus:border-zinc-400 bg-green-500/50",
+ @errors == [] && "border-zinc-300 focus:border-zinc-400",
+ @errors != [] && "border-rose-400 focus:border-rose-400"
+ ]}
+ {@rest}
+ ><%= Phoenix.HTML.Form.normalize_value("textarea", @value) %></textarea>
+ <CoreComponents.error :for={msg <- @errors}><%= msg %></CoreComponents.error>
+ </div>
+ """
+ end
5. form_component.exで呼び出す関数を作成したものに切り替える
def render(assigns) do
~H"""
<div>
<.header>
<%= @title %>
<:subtitle>Use this form to manage comment records in your database.</:subtitle>
</.header>
<.simple_form
for={@form}
id="comment-form"
phx-target={@myself}
phx-change="validate"
phx-submit="save"
>
<.input field={@form[:name]} type="text" label="Name" />
- <.input field={@form[:message]} type="textarea" label="Message" />
+ <Mycomponents.input field={@form[:message]} type="textarea" label="Message" />
<:actions>
<.button phx-disable-with="Saving...">Save Comment</.button>
</:actions>
</.simple_form>
</div>
"""
end
できました(^▽^)/
背景色がbg-green-500/50
のtextareaが反映されています。
~Elixirの国のご案内~
↓Elixirって何ぞや?と思ったらこちらもどぞ。Elixirは先端のアレコレをだいたい全部できちゃいます
↓ゼロからElixirを始めるなら「エリクサーチ」がおすすめ!私もエンジニア未経験から学習中です。
↓We Are The Alchemists, my friends!3
Elixirコミュニティは本当に優しくて温かい人たちばかり!
私が挫折せずにいられるのもこの恵まれた環境のおかげです。
まずは気軽にコミュニティを訪れてみてください。4
-
厳密には使用していないattr/3(第一引数が:checkedなど)およびslot/3は必要無いのですが、ひとまず全部前回の記事から移植してきています。 ↩
-
@torifukukaiouさんのAwesomeな名言をお借りしました。Elixirコミュニティを一言で表すと、これに尽きます。 ↩