Components
Components from other modules
Read time: 6 minutes
This guide is a direct continuation of the previous guide
git clone https://github.com/adopt-liveview/v2-myapp.git --branch documenting-components-done.
When we create a component as a function in a LiveView we gain access to it in our HEEx using <.function_name>. However if you need to use it in another LiveView you need to write the module name too. Open and edit OtherLive module:
defmodule MyappWeb.OtherPageLive do
use MyappWeb, :live_view
def render(assigns) do
~H"""
<h1>OtherPageLive</h1>
<MyappWeb.PageLive.my_button color="blue">
My other button
</MyappWeb.PageLive.my_button>
"""
end
end
This is not elegant at all. We could, perhaps, use alias to make our HEEx less verbose:
defmodule MyappWeb.OtherPageLive do
use MyappWeb, :live_view
alias MyappWeb.PageLive
def render(assigns) do
~H"""
<h1>OtherPageLive</h1>
<PageLive.my_button color="blue">
My other button
</PageLive.my_button>
"""
end
end
alias
MyApp.Deep.ModuleName.function you can do alias MyApp.Deep.ModuleName so later you can write ModuleName.function instead.
#Getting to know CoreComponents
In Phoenix projects when we have certain components that are useful in various parts of our system we choose to share them in a module called CoreComponents and import them. Since CoreComponents already exists we will be creating MyCoreComponents for learning purposes. Create it at lib/myapp_web/components/my_core_components.ex:
defmodule MyappWeb.MyCoreComponents do
use MyappWeb, :verified_routes
use Phoenix.Component
slot :inner_block, required: true
def hero(assigns) do
~H"""
<div class="bg-gray-800 text-white py-20">
<div class="container mx-auto text-center">
<h1 class="text-4xl font-bold">{render_slot(@inner_block)}</h1>
<p class="mt-4 text-lg">My personal website</p>
<.link
class="mt-8 bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded mr-2"
navigate={~p"/"}
>
Homepage
</.link>
<.link
class="mt-8 bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded"
navigate={~p"/other"}
>
Get Started
</.link>
</div>
</div>
"""
end
@doc """
Renders a button.
## Examples
<.my_button>Save data</.my_button>
<.my_button type="submit" class="text-blue-500">Save data</.my_button>
<.my_button type="submit" color="red">Delete account</.my_button>
"""
attr :color, :string, default: "blue", examples: ~w(blue red yellow green)
attr :class, :string, default: nil
attr :rest, :global, default: %{type: "button"}, include: ~w(type style)
slot :inner_block, required: true
def my_button(assigns) do
~H"""
<button
class={[
"text-white bg-#{@color}-700 hover:bg-#{@color}-800 focus:ring-4 focus:ring-#{@color}-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-#{@color}-600 dark:hover:bg-#{@color}-700 focus:outline-none dark:focus:ring-#{@color}-800",
@class
]}
{@rest}
>
{render_slot(@inner_block)}
</button>
"""
end
end
Then update both PageLive and OtherLive like so:
defmodule MyappWeb.PageLive do
use MyappWeb, :live_view
import MyappWeb.MyCoreComponents
def render(assigns) do
~H"""
<.hero>PageLive</.hero>
<.my_button type="submit" style="color: red">Default</.my_button>
"""
end
end
defmodule MyappWeb.OtherPageLive do
use MyappWeb, :live_view
import MyappWeb.MyCoreComponents
def render(assigns) do
~H"""
<.hero>OtherPageLive</.hero>
<my_button color="blue">
My other button
</my_button>
"""
end
end
This time we also sneaked in a <.hero> component that was used to create pretty titles for each page. In each of our LiveViews we manually use import MyappWeb.MyCoreComponents. Just in case you're wondering we use Phoenix.Component so we have access to HEEx related stuff like the sigil_H. We also use MyappWeb, :verified_routes so we can use sigil_p to write routes like ~p"/" and ~p"/other" in our <.hero> component.
#Meeting MyappWeb.ex
The whole point of CoreComponents (and in our case MyCoreComponents) is that they're components so important to the whole application that they're imported by default. Let's do it for our module just like Phoenix does for its own. Head out to lib/myapp_web.ex.
This module is magical. It uses secret incantations known only to the ancient ones as macros. Jokes aside, this module uses Elixir mechanism of defining a secret macro called __using__ so whenever someone use MyappWeb they're calling __using__ behind the scenes. Not that it matters to you though because all you actually need to understand is that use MyappWeb, :something will execute whatever it is inside MyappWeb.something at compile-time!
-
use MyappWeb, :static_pathsmaps todef static_paths -
use MyappWeb, :routermaps todef router -
use MyappWeb, :channelmaps todef channel -
use MyappWeb, :controllermaps todef controller -
use MyappWeb, :live_viewmaps todef live_view -
use MyappWeb, :live_componentmaps todef live_component -
use MyappWeb, :htmlmaps todef html -
use MyappWeb, :verified_routesmaps todef verified_routes
As you might already have noticed we do use MyappWeb, :live_view in all our LiveViews and inside it it will add html_helpers/0 code at compile-time. Let's add MyCoreComponents to html_helpers/0:
defp html_helpers do
quote do
# Translation
use Gettext, backend: MyappWeb.Gettext
# HTML escaping functionality
import Phoenix.HTML
# Core UI components
import MyappWeb.CoreComponents
import MyappWeb.MyCoreComponents
# Common modules used in templates
alias Phoenix.LiveView.JS
alias MyappWeb.Layouts
# Routes generation with the ~p sigil
unquote(verified_routes())
end
end
We added import MyappWeb.MyCoreComponents just below the actual import MyappWeb.CoreComponents. Don't forget to remove import MyappWeb.MyCoreComponents from PageLive and OtherPageLive too.
#Recap!
-
You can use components from other modules using the
<MyappWeb.ModuleName.component_name>syntax. -
You can use aliases to shorten module function calls like
alias MyappWeb.ModuleNameto use<ModuleName.component_name>syntax. -
If the component in question is used in a lot of places in the application consider placing it in your
CoreComponents. -
Behind the scenes
use MyappWeb, :somethingjust inserts code fromMyappWeb'sdef something() dointo your module so you have access to all tools needed to do your work.
Feedback
Got any feedback about this page? Let us know!