View Source Components

In this guide, we will start writing the UI. From this point onwards, we will build a chat room app and add things to it in every guide.

In Tamnoon, the UI is made up of components. Components are reusable elements written in Tamnoon HEEx - an extension of HEEx (HTML + Elixir). Meaning, every HEEx code is valid Tamnoon HEEx code. Components can either be elixir modules that implement the Tamnoon.Component behaviour, or .html.heex files located inside the lib/components directory. There is no difference between the two besides modules being able to utilize all of Elixir's features.

Note: We will get back to Tamnoon HEEx later. For now, think of it as normal HEEx.

When a user retrieves the page, the "root" layout gets parsed and is returned. The root layout is a component module just like any other, and this is (partially) how it looks:

defmodule Tamnoon.Components.Root do
  @behaviour Tamnoon.Component

  @impl true
  def heex do
    ~s"""
    <!DOCTYPE html>
    <head>
      ...
      <meta name="tmnn_wsaddress" content="<%= @ws_address %>"/>
    </head>

    <body>
        <div class="app-container">
            <%= r.("app.html.heex") %>
        </div>
    </body>
    </html>
    """
  end
end

As you can see, it simply contains one function named "heex" that returns the Tamnoon HEEx. Note the <%= r.("app.html.heex") %>: This is an EEx block, that renders a component (In this case "app.html.heex"). Inside components you can use the r.() syntax to invoke Tamnoon.Compiler.render_component_dyn/1, which is the function that renders a component and can pass assigns to it.

Let's make the "app" component. Run mix tamnoon.make_dirs, which will generate a components directory and an output directory. Now, make a file named "app.html.heex" inside of your components directory (located at "lib/components"). Let's test that it works by writing the following inside of it:

<p> 1 + 1 = <%= 1 + 1 %> </p>

Run mix run --no-halt, visit http://localhost:8000, and you should see a blank page with the text 1 + 1 = 2. Our Elixir block got parsed and our app is working as intended!

Making More Components

Let's make another component that will read the time from an assign, and will display a chat message. This time, let's make it a module. Inside your components directory, make a file named message_box.ex and write the following in it:

defmodule TamnoonChatroom.Components.MessageBox do
  @behaviour Tamnoon.Component
  
  @impl true
  def heex do
    ~s"""
    <div>
      <h4> <%= h.(@user_name) %> </h4>
      <p> <%= h.(@content) %> </p>
    </div>
    """
  end
end

In the component, we are rendering the @user_name and @content assigns. We are also using the h.() function, which is a shortcut for Tamnoon.Compiler.escape_html/1 similar to r.(). This will prevent HTML injections to the page by escaping the necessary characters.

Now let's use it inside "app.html.heex". Add the following to the file, restart the server and reload the page.

<%= r.([TamnoonChatroom.Components.MessageBox, %{user_name: "user1", content: "<h1> this is my message </h1>"}]) %>

You should now see "user1" displayed in bold text and the content of the message displayed under it, including the <h1> tags.

At this point, you have enough knowledge to create static pages. In the next guide we will go over the state and methods, which are how Tamnoon provides real-time updates.