Indentation-based HTML templating language compiling to HEEx/LEEx/EEx?

Hi there! The recent developments w.r.t. Surface and HEEx are very cool!

One thing that would be the cherry on top, would be an indentation-based templating language that compiles down to one of these. If you no longer need to write closing tags, HTML (and any similar XML-like languages) can be expressed much more concisely (without any loss of expressivity).

As an example, you might look at Haml which is a Ruby-based variant (emitting ERb – Embedded Ruby files) of such a language. In Elixir there does exist Calliope which is an Haml parser. Unfortunately, Calliope has been abandoned since 2015.


I would love to write or collaborate on a templating engine like this.
However, where to start? What would be the most appropriate way to create a templating engine that builds on top of HEEx/LEEx/EEx(/Surface)?

I guess the question can be split up as:

  • How to create a templating engine which can be recognized both from a sigil and from a new file extension?
  • How to wrap (H/L)EEx inside another templating engine?
2 Likes

You might want to check out Slime, also with support for phoenix e and liveview, they have pretty advanced in terms of support, maybe is a good go to for contributions or inspiration

4 Likes

Two leads.

  1. https://mobile.twitter.com/josevalim/status/1437786780464463875
    Basically , soon

  2. Similar discussion -
    Formatter for HEEx templates

You could try Temple which allows you to write your templates in Elixir.

1 Like

Thank you very much for your responses!

Slime fits perfect for my use-case. Very nice to see that such a mature library already exists.
I will use it, and focus contributions there if needed.

I was also happy to find it some time ago and also opted for using it (it’s really hard PITA to get back once you got used to haml/slime) in a non-trivial project. Alas, it seems to become abandonware just like all other (?) After kludging around for last two versions of Phoenix, I feel like the need to waste time again and again is no longer worth it. Unless somebody knows a current, maintained fork or another maintained option I’ll end up rewriting lots of slime templates back into [h]eex :frowning:

Any other suggestions?

UPDATE: found a fork working with phx 1.7

2 Likes

Replying here because I’m not allowed yet to create a topic in the Phoenix Forum (dunno why–new here).

I’m using Slim(e) for views as I work through the Phoenix on Rails tutorial. I’ve come to the section on using core component functions and I can not figure out the correct syntax for a component like link which has an inner_block. I’ve searched the web, the docs, looked at the source code for link, but no love so far.

Any help appreciated!

Nobody can post to top level categories. They’re only there to aggregate topics of their subcategories.

I don’t know if that works at all. The new Phoenix 1.7 is using HEEX template language and I suspect this may not be working with either LiveView or Phoenix.Components that use ~H sigil.

I was very happily using Slime too but with the LiveView/HEEX changes now being the default, and also with the formatter that formats HEEX and editors having good support for the syntax, I decided to stop using it. Much people did the same I think.

1 Like

OK. Thanks!

Yep, I did precisely the same for the same reasons. I do still miss the elegance of auto-closing tags :frowning:

1 Like

I can render a custom component function with an inner_block slot:

defmodule PensieveWeb.MemoryHTML do
  use PensieveWeb, :html

  embed_templates "/memory_html/*"
  attr :foo, :string
  slot :inner_block
  def foobar(assigns) do
    ~H"""
    <div class="italic mb-4">
      Foo is <%= @foo %>
      <%= render_slot(@inner_block) %>
    </div>
    """
  end
end
= foobar(%{foo: "this is foo"})
  h1.text-red-500.mb-6 The slot

~~This works as expected, so POC is good… ~~Now I just need to understand how to get the Core Components to work in the Slim templates.

Nope. I looked at the source and “The Slot” is not rendered inside the div.

Summary

This text will be hidden

populimited’s fork of Slime and Phoenix-Slime shows support for components using a colon prefix

https://github.com/populimited/slime/tree/no-compile#heex-support

:greet user=@current_user.name
  | Hello there!

Transforms into

<.greet user={@current_user.name}>Hello there!</.greet>

The README says that,
“When using slime with Phoenix, the phoenix_slime package will call precompile_heex/2 and pass the resulting valid HEEx to EEx with Phoenix.LiveView.HTMLEngine as the engine: option. This will produce the final html.”

Unfortunately, the following error occurs:

** (Slime.TemplateSyntaxError) I found a HEEx component, but this is not compiling to a HEEx file

As populimited has turned off issues, I can’t ask him/her about this.

@silverdr Do you have any insight?

Got this solved. Long story short, it was a combination of configuration and file ending.

config :phoenix, :template_engines,
  slim: PhoenixSlime.Engine,
  slime: PhoenixSlime.Engine,
  sheex: PhoenixSlime.LiveView.HTMLEngine
  # slimleex: PhoenixSlime.LiveViewEngine # If you want to use LiveView

I didn’t have sheex originally, as slimleex had been generated. Populimited’s code had sheex in config with PhoenixSlime.LiveView.HTMLEngine as the value. Once I added that and changed the file ending from slim to sheex, my components worked as expected.

For example in my index.html.sheex file:

:link href=~p"/memories/new" class="text-sky-500 hover:underline"
  'new memory

Now works. It is rendered in the brower as

<!-- <Phoenix.Component.link> lib/phoenix_component.ex:2670 -->
<a href="/memories/new" class="text-sky-500 hover:underline">new memory </a>
<!-- </Phoenix.Component.link> -->`
4 Likes

Here’s a more complicated example using the table component.

HEEX version:

<.table id="memories" rows={@memories}>
  <:col :let={memory} label="Title"><%= memory.title %></:col>
  <:col :let={memory} label="Added"><%= memory.inserted_at %></:col>
  <:action :let={memory}>
    <.link href={~p"/memories/#{memory}"} class="text-sky-500 hover:underline">View</.link>
    ·
    <.link href={~p"/memories/#{memory}/edit"} class="text-sky-500 hover:underline">Edit</.link>
    ·
    <.link href={~p"/memories/#{memory}"} method="delete" class="text-sky-500 hover:underline">Delete</.link>
  </:action>

Equivalent SHEEX version:

:table id="memories" rows=@memories
  ::col :let=memory label="Title" = memory.title
  ::col :let=memory label="Added" = memory.inserted_at
  ::action :let=memory
    :link href=~p"/memories/#{memory}" class="text-sky-500 hover:underline" View
    |&nbsp; · &nbsp;
    :link href=~p"/memories/#{memory}/edit" class="text-sky-500 hover:underline" Edit
    |&nbsp; · &nbsp;
    :link href=~p"/memories/#{memory}" method="delete" class="text-sky-500 hover:underline" Delete

And the SHEEX (Slim) result.

Good news for all you who still want to use Slim(e) in your Phoenix 1.7.x apps.

2 Likes

@arrowsmith

1 Like

to be fair, the plain HEEX version now looks more readable than the Slime version. One of the benefits of the original Slim was that it reduced the cognitive load, you just used tags with the name and didn’t have to worry about < or > or closing them, and now you have additional syntax that’s using single : or double :: and it just looks worse to me.

From where I sit, the same argument works against HEEX: now you have additional syntax that’s using <. or <:.

The Slime drops <>s, <%s, {}s, and end tags, which is still significantly less noise and I’m sure worth it to many for the cognitive load of remembering :component and ::named_slot.

1 Like

Exactly how I feel. Too much noise in HEEX and ERB templates.

True, that’s why I like most Surface UI of these all, in terms of syntax-wise it’s just tags. Similar: React.