How is an HTML component LOCAL?

I don’t understand this documentation: https://hexdocs.pm/phoenix/views.html#html-components

The last feature provided by HEEx is the idea of components. Components are pure functions that can be either local (same module) or remote (external module).

Local to what “same module”?

I actually had a bit of a hard time understanding this wording at first myself.

Heex “html elements” are just syntactic sugar for a function call, so “local” means the module that you are currently rendering the element in.

For example:

# Components
def FooWeb.Components do
  use Phoenix.Component

  def thing(assigns) do
    ~H"""
    <div class="thing">I'm a thing</div>
    """
  end
end

# LiveView
def FooWeb.FooLive do
  use FooWeb, :live_view

  import FooWeb.Components
  # 👆 This brings in the function `thing` into the current (local) module

  def render(assigns) do
    ~H"""
    <.thing />
    """
  end
end

And without import:

def FooWeb.FooLive do
  use FooWeb, :live_view

  def render(assigns) do
    ~H"""
    <FooWeb.Components.thing />
    """
  end
end
2 Likes

This is an BEAM term - it refers to the two ways of calling a function. In code:

defmodule FunctionExample do
  def foo do
    IO.puts "foo"
  end

  def bar
    foo() # <= this is a "local" call
  end
end

defmodule SomeOtherModule do
  def other do
    FunctionExample.foo() # <= this is a "remote" call
  end
end
4 Likes

Thank you. That helps.

Where would thing/1 have to be defined so I could use <.thing /> in my templates (.heex files)? Also, your example is for a live view. Would it be the same for a regular view?

Regular views (and their related templates) are also modules. Given components are just plain old functions the difference between <Module.component /> and <.component /> is the same as needing to use Module.some_function() or some_function() in regular elixir code. The latter is only available for local functions or imported ones.

3 Likes

Should be the same for a regular views though honestly, my entire experience with Phoenix has been LiveView and the only reg view I’ve ever worked with is from phx_gen_auth which I don’t touch much other than the templates.

Ah, so @LostKobrakai, it is incorrect to say that an imported function is now local? I’ve actually been curious how that works under the hood. I assumed that the functions got compiled into the module—is that not the case? As I type that I feel like it must be the case, haha. If so, they are still considered “remote” since they weren’t defined locally?

1 Like

It is not the case. If you do:

import SomeModule, only: [foo: 1]

# in a function
foo(x)

The “local” looking foo(x) call is expanded to SomeModule.foo(x). It would substantially increase compilation times if functions were copied into any module they were imported into.

5 Likes

Ohhhhh that makes sense! Thanks so much for explaining.