Can function components be made to work properly with `defdelegate` somehow?

Maybe I’m asking too much and maybe this is a language issue, not a LiveView one but still, can it be made for function components to be defdelegated to from another module and work as good when used from that other module?

By work as good I mean those slot's and attr's reporting compile time errors when something that’s required is not specified.

Ex:

defmodule SpecificComponents do
   use Phoenix.Component

   attr :id, :string, required: true
   attr :name, :string, required: true
   attr :role, :string, default: nil
   def account( assign)

   embed_templates "specific_components/*"  # containing `account.html.heex`
end
defmodule OverallComponents do
    defdelegate account( assigns), to: SpecificComponents
end
defmodule Example do
  ...

  import OverallCompoenents

  def render( assigns) do
     ~H"""
     <div>
       <.account id="account" />
     </div>
     """
  end
end

This compiles (on latest Phoenix, LiveView, Elixir) without complaining the otherwise required name attribute is missing.

defdelegate is just a macro replace typing out the following

@doc delegate_to: {SpecificComponents, :account, 1}
def account(assigns) do
  SpecificComponents.account(assigns)
end

Nothing here creates’s attrs or slots for OverallComponents.account/1. I’m not sure if the live view compiler could make use of the doc metadata.

Personally I’d argue that this seems like useless complexity. Why not use the SpecificComponents one directly. No indirection and no need to search around where the actual code is. The docs the LSP shows will be correct and so on.

3 Likes

As to why not use them directly..

It’s because they all used to be defined in the OverallComponents (e.g. CoreComponents) where most of them still reside and the app templates depends on those. But then we started replacing Alpine with custom elements, so I thought it would be nice to colocate together the LiveView heex templates of individual components and/or component groups and the related web component heex files into their subfolders.

While at it (and that’s where it went wrong), I thought it’d also be nice to move their definitions into the respective small subfolder context .ex files because I took it for granted that defdelegate would sort it out automatically. Obviously I didn’t take a peek at how defdelegate actually works.

Seems like I’m gonna be moving those definitions back into the “overall” modules leaving just the template files grouped. Not a big deal, but it would be truly nice if there was a way to achieve this intent.

Well this explains a lot… d’oh me. cc: @dviramontes

But ya, I used a very similar pattern at my last job, mostly so all components could be mass imported.

1 Like