6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

お題は不問!Qiita Engineer Festa 2023で記事投稿!

Phoenix1.7 core_components.exの外に関数コンポーネントを作る

Last updated at Posted at 2023-06-20

こんにちは!
プログラミング未経験文系出身、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.exinput(%{type: "textarea"} = assigns)を真似して作成するのでは?

  • form_component.ex
    └呼び出す関数の切り替えが必要では?

結果

以下のように実施して目的を達成しました。

1.my_components.ex内にattr/3とslot/3を移植する1

my_components.ex
+ 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を書かないとエラーになります。

my_components.ex
+ 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を書きます。

my_components.ex
 defmodule ContactWeb.Mycomponents do
   use Phoenix.Component
+  alias ContactWeb.CoreComponents
+  alias ContactWeb.Mycomponents

aliasを書いたので以降は「モジュール名.関数名」で呼び出します。

my_components.ex
  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/502を指定しています。
手順3の通り、core_components.exの関数は「モジュール名.関数名」で呼び出します。

my_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で呼び出す関数を作成したものに切り替える

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が反映されています。
image.png

~Elixirの国のご案内~

↓Elixirって何ぞや?と思ったらこちらもどぞ。Elixirは先端のアレコレをだいたい全部できちゃいます:laughing::sparkles::sparkles:

↓ゼロからElixirを始めるなら「エリクサーチ」がおすすめ!私もエンジニア未経験から学習中です。

We Are The Alchemists, my friends!:bouquet:3
Elixirコミュニティは本当に優しくて温かい人たちばかり!
私が挫折せずにいられるのもこの恵まれた環境のおかげです。
まずは気軽にコミュニティを訪れてみてください。4

  1. 厳密には使用していないattr/3(第一引数が:checkedなど)およびslot/3は必要無いのですが、ひとまず全部前回の記事から移植してきています。

  2. https://tailwindcss.com/docs/background-color

  3. @torifukukaiouさんのAwesomeな名言をお借りしました。Elixirコミュニティを一言で表すと、これに尽きます。

  4. @kn339264さんの素敵なスライドをお借りしました。Elixirコミュニティはいろんな形で活動中!

6
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?