100phlecs

100phlecs

Are there any downsides, like perf issues, to putting all functional components in CoreComponents as long as you prefix it with the context for organization, i.e. blog_navbar, app_navbar ?
Previously I’ve sectioned them out by context into their own modules, but I think this may not have many benefits as I often prefix the function with its context.

What’s your approach?

Showing Posts 1 to 10

fceruti

fceruti

I don’t like the CoreComponents module approach. To me it feels like that messy drawer in your house where you keep that extra battery, plastic sunglasses and all sort of random things.

What I like to do, is create more topical modules such as Layout, Input or Modal. Then you can alias them where you’d import CoreComponents and then call from anywhere <Layout.sidebar />, <Input.select />, <Modal.toast /> etc etc.

Maybe at first the benefits aren’t that great, but consider these case:

<Layout.sidebar /> v/s <.sidebar_layout />

How do you know where sidebar_layout is coming from? You’d need to check for an implementation in the current module, inside any of the imports, inside your use call or any of it’s imports. That can get really tricky as the project grows. Layout.sidebar stays O(1) for definition retrieval.

Another complexity CoreComponent introduces is the question to the developer: Should this component be a CoreComponent? That is not always easy to answer and ultimately meaningless. It’s the worst kind of decision to make.

19
Post #1
sodapopcan

sodapopcan

I break them up by type under a Components namespace and just import everything everywhere.

While one of my favourite things about Elixir is the locality—I very, very rarely alias and only import if there is one per module so it’s obvious where it’s coming from—this isn’t important to me at all when it comes to components because it’s dead obvious that <.foo /> is a component so I know where to look/grep. The additional module provides no further context, I just grep in live/my_app_web/components/. You also learn very quickly what you own and what comes from Phoenix.

I move stuff from CoreComponents into MyAppWeb.Components.Form, MyAppWeb.Components.Modal, etc and create others. Then my Components module looks like:

defmodule MyAppWeb.Components do
  defmacro __using__(_) do
    import unquote(__MODULE__).{
      Form,
      Modal,
      Links,
      # ....
    }
  end
end

Then use MyAppWeb.Components in live_view/0 in lib/my_app_web.ex and bam, all my components are available everywhere.

The drawback here is that you can’t use MyAppWeb, :component in your components as you’ll obviously get circular dependencies and it won’t compile. If I do need to reference other components in my components, I just use the fully qualified version, but of course you can also alias or whatever.

100phlecs

100phlecs OP

So are there actually perf improvements by using Module.component or by definition retrieval you meant navigating the codebase?

In either case I understand the appeal, especially for large Phoenix projects.
What I’m proposing in the OP would be a nightmare on a team with more than a couple devs.
Interesting way to go about it, thanks!

100phlecs

100phlecs OP

Sectioning them out but still importing them all seems interesting. I think the main appeal to one large CoreComponents is an ability to reference every single component in another one: can that be achieved here? I.e. you can use all the Links components in components defined within the Form or Modal

sodapopcan

sodapopcan

Yes but you have to full qualify them. The big drawback ehere is that it’s easier to introduce cyclical references. This can be avoided if you sort of think about your components in “layers” (there’s a correlation to Tailwind’s layers there) or actually they could be metaprogrammed into one big module. Or they could just be one big module, of course. I’ve learned to accept larger modules since getting comfortable with FP but the way I navigate codebases is still suited to smaller files—for example, if I’m working on a new file with no component calls yet and I want to use a form component, my first instinct is to fuzzy search for comp/form then scroll through the file to see what I got, so that’s why I break them up like that. I could always improve my editor tooling, of course.

cjbottaro

cjbottaro

CoreComponents is 100% my junk drawer and I’m ok with it… :slight_smile:

That’s not to say we don’t have ClientComponents, AcaComponents, WhateverFeatureComponents

But I don’t think having a junk drawer is bad until it overflows and then you start organizing.

amym114

amym114

I’ve been trying to figure this out too and have come up with the following guidelines for my own projects. Anyone see anything I’m not thinking of here?

  • Core Components – aka components that the generators rely on – live within components/core_components.ex
    - These are imported into view_helpers in the whatever_web.ex file so are accessible through any view, live component, or component by default
    - Aside: My main concern with customizing core_components.ex is having there be a major update that it’s then tedious to update the styles per component. I also think for this reason that this file should only have the functions it ships with within it.
  • Other global components, such typography or additional form elements, should be set up as modules in /components/ directory and imported into view_helpers
  • ALL other non-live components should live within /components/ too, but if they’re specific to certain views, they should only be imported as needed
  • Live components, such as a form component that you’re using in one specific liveview, should be colocated in the same directory as the corresponding liveview and template files and imported only into files as needed
aiwaiwa

aiwaiwa

My 2 cents (feeling adventures)

flash_components.ex:

defmodule ProjectWeb.FlashComponents do
  @parent __MODULE__ |> Module.split() |> Enum.drop(-1) |> Module.concat()
  @core Module.concat(@parent, CoreComponents)

  defdelegate flash(assigns), to: @core
  defdelegate flash_group(assigns), to: @core
end

form_components.ex:

defmodule ProjectWeb.FormComponents do
  @parent __MODULE__ |> Module.split() |> Enum.drop(-1) |> Module.concat()
  @core Module.concat(@parent, CoreComponents)

  defdelegate simple_form(assigns), to: @core
  defdelegate button(assigns), to: @core
  defdelegate input(assigns), to: @core
  defdelegate label(assigns), to: @core
end

modal_components.ex:

defmodule ProjectWeb.ModalComponents do
  @parent __MODULE__ |> Module.split() |> Enum.drop(-1) |> Module.concat()
  @core Module.concat(@parent, CoreComponents)

  defdelegate modal(assigns), to: @core

  alias Phoenix.LiveView.JS
  defdelegate show_modal(js \\ %JS{}, id), to: @core
  defdelegate hide_modal(js \\ %JS{}, id), to: @core
end

show_components.ex:

defmodule ProjectWeb.ShowComponents do
  @parent __MODULE__ |> Module.split() |> Enum.drop(-1) |> Module.concat()
  @core Module.concat(@parent, CoreComponents)

  defdelegate header(assigns), to: @core
  defdelegate table(assigns), to: @core
  defdelegate list(assigns), to: @core
  defdelegate back(assigns), to: @core

  alias Phoenix.LiveView.JS
  defdelegate show(js \\ %JS{}, selector), to: @core
  defdelegate hide(js \\ %JS{}, selector), to: @core
end

error_components.ex:

defmodule ProjectWeb.ErrorComponents do
  @parent __MODULE__ |> Module.split() |> Enum.drop(-1) |> Module.concat()
  @core Module.concat(@parent, CoreComponents)

  defdelegate error(assigns), to: @core

  defdelegate translate_error(params_tuple), to: @core
  defdelegate translate_errors(errors, field), to: @core
end

And certainly project_web.ex will have to rewire:

  defp html_helpers do
    quote do
      # ...
      # import ProjectWeb.CoreComponents
      import ProjectWeb.FormComponents
      import ProjectWeb.FlashComponents
      import ProjectWeb.ModalComponents
      import ProjectWeb.ShowComponents
      import ProjectWeb.ErrorComponents
cmo

cmo

There are a couple of tools that will show you the diff between versions.

This file is full of examples to be modified by the user. We don’t want another bootstrap situation where all the liveview sites look the same do we :upside_down_face:?

Where Next? Top

Trending in Discussions Top

AstonJ
As the title says, please share what you’ve been up to with Elixir. Whether that’s been learning it, looking into it, making stuff with i...
2977 94592 917
New
cblavier
Hey there, It’s been more than a year since we started using LiveView as our main UI library and building a whole library of UI componen...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
heathen
Quite interesting article Google brought me. Didn’t find any mentions about it here. What do you think in general? Would you use togethe...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
AstonJ
Since we have deprecated our Erlang sections (as we have dedicated Erlang Forums now) let’s add this thread for those who’d like to post ...
New
Null-logic-0
What IDE or editor are you using for Elixir development? Personally, I use Zed, and I really like it, but sometimes I wish there were a ...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews