You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义Core Component中Assigns无法绑定属性问题排查

问题描述

在LiveView中实现了一个复选框组自定义组件,新增:meow属性后,在input函数中访问该属性始终返回nil,直接调用assigns.meow会触发KeyError。

代码示例

core_components.ex

@doc """
生成用于多选的复选框组。

## 示例

  <.checkgroup
    field={@form[:genres]}
    label="Genres"
    options={[{"Fantasy", "fantasy"}, {"Science Fiction", "sci-fi"}]}
    selected={[{"Fantasy", "fantasy"}]}
  />

"""
attr :id, :any
attr :name, :any
attr :label, :string, default: nil
attr :field, Phoenix.HTML.FormField, doc: "从表单获取的表单字段结构体,例如:@form[:genres]"
attr :errors, :list
attr :required, :boolean, default: false
attr :rest, :global, include: ~w(form readonly)
attr :class, :string, default: nil
attr :options, :list, default: [], doc: "传递给Phoenix.HTML.Form.options_for_select/2的选项"
attr :selected, :list, default: [], doc: "当前选中的选项,用于标记哪些复选框被勾选"

attr :meow, :any, default: "meow", doc: "一个超酷的新属性!"

def checkgroup(assigns) do
  new_assigns =
    assigns
    |> assign(:multiple, true)
    |> assign(:type, "checkgroup")

  input(new_assigns)
end


def input(%{type: "checkgroup"} = assigns) do
  ~H"""
  <p> <%= "Cat says: #{@meow}" %> </p> 

  <div class="mt-2">
    <%= for opt <- @options do %>
      <div class="relative flex gap-x-3">
        <div class="flex h-6 items-center">
          <input id={opt.id} name={@name} type="checkbox" value={opt.id} checked={opt in @selected} disabled={false} />
        </div>
        <div class="text-sm leading-6">
          <label  for={opt.id} ><%= opt.name %></label>
        </div>
      </div>

    <% end %>
  </div>
  """
end

错误场景

使用组件时传入meow={[1,2]}:

<.checkgroup field={@form[:my_form]} label="SUper question!" options={@sounds} selected={@selected_sounds} meow={[1, 2]} required />

渲染时触发KeyError,错误信息显示:meow存在于__given__中,但在assigns顶层无法找到。


问题原因

Phoenix组件的attr定义是与对应函数绑定的:

  • :meow属性只在checkgroup/1函数的attr声明里定义了,但input/1函数没有声明该属性。
  • 当调用input(new_assigns)时,Phoenix会自动过滤掉input/1函数未声明的属性,导致:meow不会被保留在input函数的assigns中,因此访问@meow会返回nil,直接取assigns.meow会触发KeyError。

修复方案

需要在input/1函数中也声明:meow属性,确保该属性能被传递到渲染模板中:

修改input/1函数的attr部分(如果input是处理多种组件类型的通用函数,可以把:meow的attr放在通用定义里,或者针对checkgroup类型单独处理):

# 在input函数上方添加attr声明
attr :meow, :any, default: "meow", doc: "一个超酷的新属性!"

def input(%{type: "checkgroup"} = assigns) do
  ~H"""
  <p> <%= "Cat says: #{@meow}" %> </p> 

  <div class="mt-2">
    <%= for opt <- @options do %>
      <div class="relative flex gap-x-3">
        <div class="flex h-6 items-center">
          <input id={opt.id} name={@name} type="checkbox" value={opt.id} checked={opt in @selected} disabled={false} />
        </div>
        <div class="text-sm leading-6">
          <label  for={opt.id} ><%= opt.name %></label>
        </div>
      </div>

    <% end %>
  </div>
  """
end

如果input函数是处理多种组件类型的通用函数,也可以用assign_new在input函数中确保:meow存在:

def input(%{type: "checkgroup"} = assigns) do
  assigns = assign_new(assigns, :meow, fn -> "meow" end)
  
  ~H"""
  <p> <%= "Cat says: #{@meow}" %> </p> 
  <!-- 其余渲染代码 -->
  """
end

推荐第一种方案,因为它符合Phoenix组件的规范,明确声明组件接收的属性,提高代码可读性。


内容的提问来源于stack exchange,提问作者Flame_Phoenix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 08:57:21