How to safely interleave alpine within heex?

I was following Build a performant autocomplete using Phoenix LiveView and Alpine.js — benvp— benvp blog.
I have been able to make it work, for the most part.

Down into the section where setFocus function has to be called with the index of the item, it fails.

  <%= for {option, index} <- Enum.with_index(@options) do %>
          <li
            id={"option-#{index}"}
            x-ref={"option-#{index}"}
            x-on:click="close"
            @mouseenter="setFocus(#{index})"
            :class='{"focus": focus === "#{index}"}'
            phx-click="select"
            phx-target={@myself}
            phx-value-id={"#{option.id}"}
            phx-value-name={"#{option.name}"}
            class="option relative cursor-default select-none py-2 pl-3 pr-9 text-gray-900"
            >

value of @mouseenter attribute is not treated as a template to be rendered. Instead it appears as follows:

<li 
id="option-0" 
x-ref="option-0" 
x-on:click="close" 
@mouseenter="setFocus(#{index})" 
:class="{&quot;focus&quot;: 
focus === &quot;#{index}&quot;}" 
phx-click="select" 
phx-target="1" 
phx-value-id="1" 
phx-value-name="Awesome Inc" 
class="option relative cursor-default select-none py-2 pl-3 pr-9 text-gray-900">

the value of index does not get replaced.
Is there a workaround for this?

Thanks.

It seems the integration code from the docs is a little bit different from the article you linked, make sure this is not impacting your code:

Article

let liveSocket = new LiveSocket('/live', Socket, {
  dom: {
    // make LiveView work nicely with alpinejs
    onBeforeElUpdated(from, to) {
      if (from.__x) {
        window.Alpine.clone(from.__x, to);
      }
    },
  },
  params: {
    _csrf_token: csrfToken,
  },
});

Docs

let liveSocket = new LiveSocket("/live", Socket, {
  ...,
  dom: {
    onBeforeElUpdated(from, to){
      if(from._x_dataStack){ window.Alpine.clone(from, to) }
    }
  },
})

BTW, shouldn’t this: @mouseenter="setFocus(#{index})", actually be @mouseenter={"setFocus(#{index})"}, since your are using elixir code (string interpolation) inside the template?

The integration code did cause trouble. Found the solution for that in a github issue.

Thanks for the help on mouse enter.
Still facing the same issue on :class attribute, though.

Okay. Found a solution.

            @mouseenter={"setFocus(#{index})"}
            x-bind:class={"focus === #{index}? 'bg-gray-500' : ''"}

Leaving this here.

1 Like