It’s pretty much to avoid mentioning my “WhateverWeb” module name and making it universal for any user to simply copy-paste into their module between do
and end
. It’s taking a current module name, splitting it so that you can get a parent module name and then concatenating it with a CoreComponents
atom.
How do you add it to html_helpers/0 ? I am trying to make it work by adding it as:
use MyAppWeb.Components
but then I get a
== Compilation error in file lib/my_app_web/controllers/page_html.ex ==
** (CompileError) lib/my_app_web/controllers/page_html.ex:2: module MyAppWeb.Components.CustomComponents.MyButton is not loaded and could not be found. This may be happening because the module you are trying to load directly or indirectly depends on the current module
expanding macro: MyAppWeb.Components.__using__/1
Code for further reference:
#components.ex
defmodule MyAppWeb.Components do
defmacro __using__(_) do
quote do
import unquote(__MODULE__).CustomComponents.{
MyButton
# ....
}
end
end
end
And the directory looks like this:
my_app_web/
|-- components/
| |-- custom_components/
| | |-- my_button.ex
| | |-- other_component1.ex
| |-- components.ex
We would need to see the code in my_button.ex
to know for sure but it looks like you just don’t have the MyButon
module properly defined. It must be:
defmodule MyAppWeb.Components.CustomComponents.MyButton do
# ...
end
Saw this thread pop up and thought I’d share how I’ve ended up doing this.
I mostly work with umbrella applications, where we usually have a separate application which handles assets and components. Depending on the project, it’s been called either DesignSystem
, WebAssets
, CommonWeb
or similar. In the most recent one it’s called DesignSystem
so I’ll go with that one for the examples. All the components are in separate files and the folder structure is something along the lines of:
apps/
|-- design_system/
||-- assets/
|||-- css/
|||-- js/
||-- lib/
|||-- design_system/
||||-- components/
|||||-- form/
||||||-- error.ex
||||||-- input.ex
|||||-- button.ex
|||||-- table.ex
|||-- design_system.ex
...
Component’s module naming follows the folder structure.
defmodule DesignSystem.Components.Button do
def button(assigns) do
...
end
end
In design_system.ex
I have a defdelegate
for every component, which allows me to call them without specifying the full module name.
defmodule DesignSystem do
defdelegate button(), to: __MODULE__.Button
defdelegate input(), to: __MODULE__.Form.Input
...
end
Verbose code is more to my liking, and it also allows easy code completion, so I don’t mind my .heex
files looking like this:
<DesignSystem.box>
<DesignSystem.button>
Click me!
</DesignSystem.button>
</DesignSystem.box>
To deal with typing out the DesignSystem
part every time, I would either import or alias the module:
defmodule ExampleWeb do
def live_view() do
quote do
use Phoenix.LiveView,
layout: {ExampleWeb.Layouts, :live}
# Either this
import DesignSystem
# Or this
alias DesignSystem, as: DS
unquote(html_helpers())
end
end
end
The reason I don’t do this is because in my view it’s easier for beginners to clearly see where the function is defined at. Also this one time I made a component called Form
and I wanted to call it with <.form>
but it’s already defined in Phoenix.Component
, and I didn’t want to call it .simple_form
or similar. So yeah, a ridiculous reason but I am a ridiculous person.