Double dom event handler using Alpine.js & LiveView

In a nutshell the issue I’m facing is that every time I register alpine’s @ or x-on event handler, two are registered and called when the action is performed.

The issue is solved if I comment liveSocket.connect(), which leads me to believe the problem is in the interoperation of liveSocket and Alpine.

I’ve circled the internet without success. Has anyone had this issue and solved it?

Packages:
Phoenix: 1.6.2
Phoenix Live View: 0.16.4
Surface: 0.6.1
Alpinejs: 3.3.5 & 3.4.2

Surface component:

defmodule TimetaskApp.WeekCalendar do
  use Surface.LiveComponent

  prop hours, :list, required: true

  prop dates, :list, required: true

  @impl true
  def render(assigns) do
    ~F"""
    <div class="w-full h-full flex flex-col" style="height: calc(100vh - 100px)" id="week-calendar">  {!-- Calendar --}
      <div class="h-full flex-1 flex flex-row relative">
        <div
          class="flex-1 flex"
          id="week-calendar-columns"
          :hook="WeekCalendar"
          x-init="fromHour = +$el.dataset.fromHour; toHour = +$el.dataset.toHour;"
          x-data="week_calendar_data"
          data-from-hour={List.first(@hours)}
          data-to-hour={List.last(@hours)}
        >
          <div
            class="flex-1 flex"
            id="week-calendar-days-container"
            x-ref="days_container"
          >
            {#for date <- @dates}
              <div
                id={"day_col_#{format_date(date)}"}
                data-date={format_date(date)}
                class="flex-1 border-r border-tt-border-calendar relative"
                @mousedown.stop="startNewTimebox($event, $el.dataset.date)"
              >
              </div>
            {/for}
          </div>
        </div>
      </div>
    </div>
    """
  end
end

app.js:

import { Socket } from "phoenix"
import { LiveSocket } from "phoenix_live_view"
import topbar from "topbar"
import Alpine from "alpinejs"
import Hooks from "./_hooks"

import "./app/week_calendar.js"

window.Alpine = Alpine
Alpine.start()

let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {
  params: { _csrf_token: csrfToken },
  hooks: Hooks,
  dom: {
    onBeforeElUpdated(from, to) {
      if (from._x_dataStack) {
        window.Alpine.clone(from, to)
      }
    },
  },
});

topbar.config({ barColors: { 0: "#03ad8c" }, shadowColor: "rgba(0, 0, 0, .3)" })
window.addEventListener("phx:page-loading-start", info => topbar.show())
window.addEventListener("phx:page-loading-stop", info => topbar.hide())

liveSocket.connect()
window.liveSocket = liveSocket

./app/week_calendar.js

document.addEventListener("alpine:init", () => {
  Alpine.data("week_calendar_data", () => ({
    fromHour: 0,
    toHour: 0,
    newTimebox: null,
    startNewTimebox(event, date) {
      const fromTime = this.getTimeFromEvent(event)
      this.newTimebox = {
        date: date,
        fromTime: fromTime,
        toTime: null
      }
      console.log(`Start new timebox ${date} ${fromTime.hour}:${fromTime.minute}`)
    },
  }))
});

Chrome dev tools:

Chrome console:

Does it work if you get rid of Alpine.start()? Importing might automatically start it.

Nothing happens, the event handlers are not present. Alpine is not started in the import.

You got an x-data on that div, or its parent?

Yes there is, I updated my original post to show more context.

what’s the rendered html look like for the bit inside {#for date <- @dates}?

This is the page full html (ps: I made a few updates to the original post)

view-source:127.0.0.1:4000/app/calendar

<div data-phx-main="true" data-phx-session="SFMyNTY.g2gDaAJhBXQAAAAIZAACaWRtAAAAFHBoeC1GclJGMEZlRS1DVVB2UktCZAAMbGl2ZV9zZXNzaW9uaAJkAAdkZWZhdWx0bggAUIScpgUdtBZkAApwYXJlbnRfcGlkZAADbmlsZAAIcm9vdF9waWRkAANuaWxkAAlyb290X3ZpZXdkAB9FbGl4aXIuVGltZXRhc2tBcHAuTGl2ZUNhbGVuZGFyZAAGcm91dGVyZAAZRWxpeGlyLlRpbWV0YXNrV2ViLlJvdXRlcmQAB3Nlc3Npb250AAAAAGQABHZpZXdkAB9FbGl4aXIuVGltZXRhc2tBcHAuTGl2ZUNhbGVuZGFybgYAzjTB6XwBYgABUYA.gQQA8Bf1ScKf-xpokXsCPC48M9lqLWwb6Won9BzCkpA" data-phx-static="SFMyNTY.g2gDaAJhBXQAAAADZAAKYXNzaWduX25ld2wAAAACZAALX19jb250ZXh0X19kAAtfX3N1cmZhY2VfX2pkAAVmbGFzaHQAAAAAZAACaWRtAAAAFHBoeC1GclJGMEZlRS1DVVB2UktCbgYAzjTB6XwBYgABUYA.GBBwOY7Xtgbxw51hEzU0mvpHuSMM9realSNQyzeoL8A" id="phx-FrRF0FeE-CUPvRKB"><!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta content="NWVsPwERIkA3IUFBJwc_FVxgFUh7ZGFPw2YeXgo-AY3vO3kRo1w1LU9w" name="csrf-token">
<title>Calendar</title>
    <link phx-track-static rel="stylesheet" href="/assets/app.css">
    <script defer phx-track-static type="text/javascript" src="/assets/app.js"></script>
  </head>
  <body>
    <div class="flex flex-col min-h-screen max-h-screen select-none overflow-hidden bg-gray-0">
    <nav>
  <div class="px-4">
    <div class="grid grid-cols-6 h-16 items-center">
      <div class="col-span-2">
        <div class="flex items-center">
          <a class="block h-6 w-auto mr-8 ml-2" href="/app/calendar">
            <svg width="100%" height="100%" viewBox="0 0 606 123" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 2; max-height: 100px">
    <g transform="matrix(1,0,0,1,-196,-237)">
        <g id="Layer-1" serif:id="Layer 1">
            <g transform="matrix(1,0,0,1,219.121,343.75)">
                <path d="M0,-37.498L0,-51.124L-19.121,-51.124L-19.121,-36.216C-19.121,-8.689 3.195,13.626 30.722,13.626L33.105,13.626L33.105,-5.22L30.722,-5.22C13.755,-5.22 0,-20.531 0,-37.498" style="fill: rgb(65,65,91); fill-rule: nonzero"></path>
            </g>
            <g transform="matrix(1,0,0,1,0,31.156)">
                <rect x="269.411" y="242.624" width="18.583" height="83.596" style="fill: rgb(65,65,91)"></rect>
            </g>
            <g transform="matrix(1,0,0,1,270.098,244.227)">
                <path d="M0,5.141C2.361,7.504 5.335,8.752 8.604,8.752C12.004,8.752 15.139,7.415 17.433,4.988C19.645,2.645 20.814,-0.329 20.814,-3.611L-3.605,-3.611C-3.605,-0.242 -2.356,2.786 0,5.141" style="fill: rgb(65,65,91); fill-rule: nonzero"></path>
            </g>
            <g transform="matrix(1,0,0,1,530.42,332.174)">
                <path d="M0,-58.394L-0.012,-83.574L-14.114,-83.574L-14.114,-13.575C-14.114,7.833 3.256,25.18 24.664,25.152L24.664,10.962C11.097,10.962 0.094,-0.026 0.076,-13.594L0.042,-35.514L0.013,-35.514L0.008,-44.286L24.664,-44.303L24.664,-58.394L0,-58.394Z" style="fill: rgb(65,65,91); fill-rule: nonzero"></path>
            </g>
            <g transform="matrix(1,0,0,1,621.528,294.74)">
                <path d="M0,41.689C-5.881,47.527 -12.813,50.366 -21.195,50.366C-29.487,50.366 -36.398,47.523 -42.329,41.677C-48.26,35.832 -51.144,28.969 -51.144,20.701C-51.144,12.53 -48.26,5.716 -42.329,-0.131C-36.396,-5.977 -29.484,-8.82 -21.195,-8.82C-12.815,-8.82 -5.883,-5.981 0,-0.143C5.89,5.705 8.753,12.524 8.753,20.701C8.753,28.977 5.89,35.843 0,41.689M8.753,-10.612C6.857,-12.442 4.771,-14.12 2.496,-15.639C-4.651,-20.406 -12.718,-22.822 -21.487,-22.822C-33.529,-22.822 -43.964,-18.528 -52.498,-10.061C-61.023,-1.603 -65.347,8.746 -65.347,20.701C-65.347,32.748 -61.027,43.166 -52.51,51.668C-43.975,60.19 -33.538,64.511 -21.487,64.511C-12.712,64.511 -4.644,62.093 2.496,57.328C4.771,55.809 6.857,54.128 8.753,52.289L8.753,62.636L22.958,62.636L22.958,-20.96L8.753,-20.96L8.753,-10.612Z" style="fill: rgb(65,65,91); fill-rule: nonzero"></path>
            </g>
            <g transform="matrix(1,0,0,1,698.696,318.45)">
                <path d="M0,-5.731L-9.553,-8.785L-23.552,-13.258C-30.075,-15.25 -30.955,-18.171 -30.955,-20.765C-30.955,-23.668 -29.424,-26.28 -26.271,-28.751C-22.831,-31.45 -18.06,-32.819 -12.089,-32.819C-5.425,-32.819 0.452,-30.794 5.877,-26.626L7.401,-25.455L17.594,-35.476L14.274,-37.826C6.11,-43.602 -3.154,-46.532 -13.265,-46.532C-22.449,-46.532 -30.188,-43.959 -36.268,-38.888C-42.581,-33.616 -45.783,-27.42 -45.783,-20.476C-45.783,-15.769 -44.191,-11.5 -41.052,-7.789C-38.021,-4.198 -33.571,-1.561 -27.972,0.008L-20.786,2.26L-4.478,7.372C5.015,10.342 5.015,14.275 5.015,15.755C5.015,16.681 5.015,19.494 0.148,22.954C-3.625,25.638 -8.299,26.943 -14.145,26.943C-29.364,26.943 -38.443,15.016 -38.443,15.016L-48.347,24.754C-48.347,24.754 -41.905,33.542 -31.205,37.707C-25.399,39.967 -19.088,40.801 -12.677,40.801C-3.822,40.801 3.74,38.35 9.802,33.512C16.171,28.433 19.4,22.312 19.4,15.323C19.4,8.351 16.018,-0.964 0,-5.731" style="fill: rgb(65,65,91); fill-rule: nonzero"></path>
            </g>
            <g transform="matrix(1,0,0,1,393.782,354.677)">
                <path d="M0,-80.06C-3.938,-81.834 -8.218,-82.759 -12.833,-82.759L-17.232,-82.759C-24.307,-82.759 -30.728,-80.586 -36.318,-76.298C-37.382,-75.483 -38.359,-74.607 -39.287,-73.698C-40.215,-74.607 -41.192,-75.483 -42.257,-76.298C-47.847,-80.586 -54.268,-82.759 -61.342,-82.759L-65.741,-82.759C-70.356,-82.759 -74.637,-81.834 -78.574,-80.06C-81.942,-78.542 -85.061,-76.411 -87.888,-73.613C-94.04,-67.523 -97.157,-60.079 -97.157,-51.486L-97.157,2.699L-78.577,2.699L-78.577,-50.763C-78.511,-54.483 -77.208,-57.53 -74.475,-60.23C-71.75,-62.923 -68.743,-64.178 -65.014,-64.178L-62.065,-64.178C-58.338,-64.178 -55.333,-62.923 -52.606,-60.228C-49.874,-57.53 -48.57,-54.483 -48.505,-50.763L-48.505,2.699L-30.07,2.699L-30.07,-50.763C-30.004,-54.483 -28.701,-57.53 -25.969,-60.228C-23.241,-62.923 -20.237,-64.178 -16.509,-64.178L-13.56,-64.178C-9.832,-64.178 -6.825,-62.923 -4.1,-60.23C-1.368,-57.53 -0.063,-54.483 0.003,-50.763L0.003,2.699L18.582,2.699L18.582,-51.486C18.582,-60.079 15.465,-67.523 9.313,-73.613C6.487,-76.411 3.367,-78.542 0,-80.06" style="fill: rgb(65,65,91); fill-rule: nonzero"></path>
            </g>
            <g transform="matrix(1,0,0,1,237.775,273.779)">
                <path d="M0,18.846L22.94,18.846L22.94,0L-18.655,0C-18.655,10.408 -10.409,18.846 0,18.846" style="fill: rgb(0,225,171); fill-rule: nonzero"></path>
            </g>
            <g transform="matrix(0,1,1,0,226.143,247.637)">
                <path d="M-7.021,-26.143L7.309,-26.143C17.717,-26.143 26.143,-17.431 26.143,-7.023L-7.021,-7.021L-7.021,-26.143Z" style="fill: rgb(0,173,140); fill-rule: nonzero"></path>
            </g>
            <g transform="matrix(1,0,0,1,759.544,273.773)">
                <path d="M0,34.359L34.099,0L-0.083,34.258L0,34.359Z" style="fill: rgb(65,65,91); fill-rule: nonzero"></path>
            </g>
            <g transform="matrix(1,0,0,1,759.461,297.945)">
                <path d="M0,10.086L34.182,-24.173L14.216,-24.173L-20.299,10.46L-20.299,-49.345L-34.54,-49.345L-34.54,59.431L-20.299,59.431L-20.299,30.725L0.083,10.187L0,10.086Z" style="fill: rgb(65,65,91); fill-rule: nonzero"></path>
            </g>
            <g transform="matrix(1,0,0,1,749.022,318.737)">
                <path d="M0,0.169L0.163,0.001L0.162,0L0,0.169Z" style="fill: rgb(65,65,91); fill-rule: nonzero"></path>
            </g>
            <g transform="matrix(1,0,0,1,800,318.5)">
                <path d="M0,38.876L-31.938,0L-50.581,0L-50.814,0.238L-18.875,38.876L0,38.876Z" style="fill: rgb(65,65,91); fill-rule: nonzero"></path>
            </g>
            <g transform="matrix(1,0,0,1,438.97,324.014)">
                <path d="M0,-16.859L0.038,-16.974C1.391,-21.066 3.689,-24.625 7.065,-27.852C12.295,-32.85 18.392,-35.278 25.7,-35.278C33.114,-35.278 39.285,-32.844 44.563,-27.837C47.959,-24.613 50.253,-21.06 51.577,-16.974L51.614,-16.859L0,-16.859ZM57.09,-39.418C48.954,-47.919 38.395,-52.229 25.704,-52.229C13.472,-52.229 2.975,-47.912 -5.496,-39.4C-13.938,-30.912 -18.22,-20.514 -18.22,-8.498C-18.22,3.593 -13.66,14.057 -4.668,22.604C4.24,31.075 15.157,35.37 27.778,35.37C35.241,35.37 42.318,33.75 48.813,30.556C52.824,28.581 56.545,26.044 59.871,23.014L59.974,22.92L47.704,10.65L47.614,10.561L47.521,10.646C45.533,12.457 43.338,13.931 40.811,15.151C36.772,17.1 32.509,18.047 27.778,18.047C20.221,18.047 13.875,15.63 8.379,10.66C4.913,7.526 2.554,4.108 1.168,0.213L0.795,-0.886L67.731,-0.886L67.755,-0.988C69.816,-9.405 69.249,-17.856 66.117,-25.427C63.912,-30.757 60.875,-35.465 57.09,-39.418" style="fill: rgb(65,65,91); fill-rule: nonzero"></path>
            </g>
        </g>
    </g>
</svg>


          </a>

          <nav class="flex gap-2">
            <a tabindex="-1" href="/app/calendar">
    <button class="inline-flex items-center rounded-md focus:outline-none transition duration-75 whitespace-nowrap disabled:cursor-not-allowed py-2 px-3 text-base gap-2 hover:text-brand-dark font-normal text-brand-regular fill-current" tabindex="-1" type="button">
    
    
              <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" class="fill-current" viewBox="0 0 24 24"><path d="M3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2h-2V2h-2v2H9V2H7v2H5a2 2 0 0 0-2 2zm16 14H5V8h14z"/></svg>
            
  
  </button>

</a>


            <a tabindex="-1" href="/app/projects">
    <button class="inline-flex items-center rounded-md focus:outline-none transition duration-75 whitespace-nowrap disabled:cursor-not-allowed py-2 px-3 text-base gap-2 hover:text-brand-dark font-normal text-tt-text fill-current" tabindex="-1" type="button">
    
    
              <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" class="fill-current" viewBox="0 0 24 24"><path d="M18.5 2h-12C4.57 2 3 3.57 3 5.5V22l7-3.5 7 3.5v-9h5V5.5C22 3.57 20.43 2 18.5 2zM15 18.764l-5-2.5-5 2.5V5.5C5 4.673 5.673 4 6.5 4h8.852A3.451 3.451 0 0 0 15 5.5v13.264zM20 11h-3V5.5c0-.827.673-1.5 1.5-1.5s1.5.673 1.5 1.5V11z"/></svg>
            
  
  </button>

</a>


            <a tabindex="-1" href="/app/reports">
    <button class="inline-flex items-center rounded-md focus:outline-none transition duration-75 whitespace-nowrap disabled:cursor-not-allowed py-2 px-3 text-base gap-2 hover:text-brand-dark font-normal text-tt-text fill-current" tabindex="-1" type="button">
    
    
              <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" class="fill-current" viewBox="0 0 24 24"><path d="M9 6h2v14H9zm4 2h2v12h-2zm4-4h2v16h-2zM5 12h2v8H5z"/></svg>
            
  
  </button>

</a>


          </nav>
        </div>
      </div>
      <div class="col-span-2 flex justify-center">
        
            <a data-phx-link="patch" data-phx-link-state="push" href="/app/calendar?date=today">
      <button class="inline-flex items-center rounded-md focus:outline-none transition duration-75 whitespace-nowrap disabled:cursor-not-allowed py-2 px-3 text-base gap-2 text-tt-text bg-transparent font-medium hover:bg-gray-50 hover:text-tt-text-dark active:text-brand-regular active:bg-transparent fill-current" tabindex="-1" type="button">
    
      <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" class="fill-current" viewBox="0 0 24 24"><path d="M13.293 6.293 7.586 12l5.707 5.707 1.414-1.414L10.414 12l4.293-4.293z"/></svg>
    
  </button>

  </a>

  <a data-phx-link="patch" data-phx-link-state="push" href="/app/calendar?date=today">
      <button class="inline-flex items-center rounded-md focus:outline-none transition duration-75 whitespace-nowrap disabled:cursor-not-allowed py-2 px-3 text-base gap-2 text-tt-text bg-transparent font-medium hover:bg-gray-50 hover:text-tt-text-dark active:text-brand-regular active:bg-transparent font-semibold w-52 justify-center" tabindex="-1" type="button">
    
      8 - 14 November
    
  </button>

  </a>

  <a data-phx-link="patch" data-phx-link-state="push" href="/app/calendar?date=2021-11-18">
      <button class="inline-flex items-center rounded-md focus:outline-none transition duration-75 whitespace-nowrap disabled:cursor-not-allowed py-2 px-3 text-base gap-2 text-tt-text bg-transparent font-medium hover:bg-gray-50 hover:text-tt-text-dark active:text-brand-regular active:bg-transparent fill-current" tabindex="-1" type="button">
    
      <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" class="fill-current" viewBox="0 0 24 24"><path d="M10.707 17.707 16.414 12l-5.707-5.707-1.414 1.414L13.586 12l-4.293 4.293z"/></svg>
    
  </button>

  </a>


        
      </div>
      <div class="col-span-2 flex justify-end">
        <div phx-hook="TimetaskApp.Components.Dropdown#Dropdown" class="relative inline-block text-left z-20" id="nav-dropdown">
    <button phx-click="toggle_dropdown" phx-target="4" class="inline-flex items-center rounded-md focus:outline-none transition duration-75 whitespace-nowrap disabled:cursor-not-allowed py-2 px-3 text-base gap-2 text-tt-text bg-transparent font-medium hover:bg-gray-50 hover:text-tt-text-dark active:text-brand-regular active:bg-transparent fill-current" tabindex="-1" type="button">
    
    
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" class="fill-current" viewBox="0 0 24 24"><path d="M12 16c2.206 0 4-1.794 4-4s-1.794-4-4-4-4 1.794-4 4 1.794 4 4 4zm0-6c1.084 0 2 .916 2 2s-.916 2-2 2-2-.916-2-2 .916-2 2-2z"/><path d="m2.845 16.136 1 1.73c.531.917 1.809 1.261 2.73.73l.529-.306A8.1 8.1 0 0 0 9 19.402V20c0 1.103.897 2 2 2h2c1.103 0 2-.897 2-2v-.598a8.132 8.132 0 0 0 1.896-1.111l.529.306c.923.53 2.198.188 2.731-.731l.999-1.729a2.001 2.001 0 0 0-.731-2.732l-.505-.292a7.718 7.718 0 0 0 0-2.224l.505-.292a2.002 2.002 0 0 0 .731-2.732l-.999-1.729c-.531-.92-1.808-1.265-2.731-.732l-.529.306A8.1 8.1 0 0 0 15 4.598V4c0-1.103-.897-2-2-2h-2c-1.103 0-2 .897-2 2v.598a8.132 8.132 0 0 0-1.896 1.111l-.529-.306c-.924-.531-2.2-.187-2.731.732l-.999 1.729a2.001 2.001 0 0 0 .731 2.732l.505.292a7.683 7.683 0 0 0 0 2.223l-.505.292a2.003 2.003 0 0 0-.731 2.733zm3.326-2.758A5.703 5.703 0 0 1 6 12c0-.462.058-.926.17-1.378a.999.999 0 0 0-.47-1.108l-1.123-.65.998-1.729 1.145.662a.997.997 0 0 0 1.188-.142 6.071 6.071 0 0 1 2.384-1.399A1 1 0 0 0 11 5.3V4h2v1.3a1 1 0 0 0 .708.956 6.083 6.083 0 0 1 2.384 1.399.999.999 0 0 0 1.188.142l1.144-.661 1 1.729-1.124.649a1 1 0 0 0-.47 1.108c.112.452.17.916.17 1.378 0 .461-.058.925-.171 1.378a1 1 0 0 0 .471 1.108l1.123.649-.998 1.729-1.145-.661a.996.996 0 0 0-1.188.142 6.071 6.071 0 0 1-2.384 1.399A1 1 0 0 0 13 18.7l.002 1.3H11v-1.3a1 1 0 0 0-.708-.956 6.083 6.083 0 0 1-2.384-1.399.992.992 0 0 0-1.188-.141l-1.144.662-1-1.729 1.124-.651a1 1 0 0 0 .471-1.108z"/></svg>
          
  
  </button>


  
</div>

      </div>
    </div>
  </div>
</nav>



  <div class="full-width-container">
    <div class="w-full h-full flex flex-col" style="height: calc(100vh - 100px)" id="week-calendar">  
  <div class="flex h-10 w-full">  
    <div class="h-10 calendar-head" style="width: 40px"></div>
    <div class="h-10 flex-1 flex">
      
        <div class="flex-1 flex justify-center items-end pb-2 calendar-head">
          <button class="text-tt-text inline-flex items-center py-2 px-4 rounded-md hover:bg-gray-50 hover:font-semibold focus:outline-none" tabindex="-1">
  Monday 8
</button>

        </div>
      
        <div class="flex-1 flex justify-center items-end pb-2 calendar-head">
          <button class="text-tt-text inline-flex items-center py-2 px-4 rounded-md hover:bg-gray-50 hover:font-semibold focus:outline-none" tabindex="-1">
  Tuesday 9
</button>

        </div>
      
        <div class="flex-1 flex justify-center items-end pb-2 calendar-head">
          <button class="text-tt-text inline-flex items-center py-2 px-4 rounded-md hover:bg-gray-50 hover:font-semibold focus:outline-none" tabindex="-1">
  Wednesday 10
</button>

        </div>
      
        <div class="flex-1 flex justify-center items-end pb-2 calendar-head">
          <button class="text-tt-text inline-flex items-center py-2 px-4 rounded-md hover:bg-gray-50 hover:font-semibold focus:outline-none" tabindex="-1">
  Thursday 11
</button>

        </div>
      
        <div class="flex-1 flex justify-center items-end pb-2 calendar-head">
          <button class="text-tt-text inline-flex items-center py-2 px-4 rounded-md hover:bg-gray-50 hover:font-semibold focus:outline-none" tabindex="-1">
  Friday 12
</button>

        </div>
      
        <div class="flex-1 flex justify-center items-end pb-2 calendar-head">
          <button class="text-tt-text inline-flex items-center py-2 px-4 rounded-md hover:bg-gray-50 hover:font-semibold focus:outline-none" tabindex="-1">
  Saturday 13
</button>

        </div>
      
        <div class="flex-1 flex justify-center items-end pb-2 calendar-head">
          <button class="text-tt-text inline-flex items-center py-2 px-4 rounded-md hover:bg-gray-50 hover:font-semibold focus:outline-none" tabindex="-1">
  Sunday 14
</button>

        </div>
      
    </div>
  </div>

  <div class="h-full flex-1 flex flex-row relative">  
    <div class="flex flex-col border-r border-tt-border-calendar" style="width: 40px">  
      
        <div id="hour_line_7" class="relative h0">
          <span class="absolute right-6 -bottom-2 text-xs text-tt-text-dimmed">7</span>
        </div>
      
        <div id="hour_line_8" class="relative flex-1">
          <span class="absolute right-6 -bottom-2 text-xs text-tt-text-dimmed">8</span>
        </div>
      
        <div id="hour_line_9" class="relative flex-1">
          <span class="absolute right-6 -bottom-2 text-xs text-tt-text-dimmed">9</span>
        </div>
      
        <div id="hour_line_10" class="relative flex-1">
          <span class="absolute right-6 -bottom-2 text-xs text-tt-text-dimmed">10</span>
        </div>
      
        <div id="hour_line_11" class="relative flex-1">
          <span class="absolute right-6 -bottom-2 text-xs text-tt-text-dimmed">11</span>
        </div>
      
        <div id="hour_line_12" class="relative flex-1">
          <span class="absolute right-6 -bottom-2 text-xs text-tt-text-dimmed">12</span>
        </div>
      
        <div id="hour_line_13" class="relative flex-1">
          <span class="absolute right-6 -bottom-2 text-xs text-tt-text-dimmed">13</span>
        </div>
      
        <div id="hour_line_14" class="relative flex-1">
          <span class="absolute right-6 -bottom-2 text-xs text-tt-text-dimmed">14</span>
        </div>
      
        <div id="hour_line_15" class="relative flex-1">
          <span class="absolute right-6 -bottom-2 text-xs text-tt-text-dimmed">15</span>
        </div>
      
        <div id="hour_line_16" class="relative flex-1">
          <span class="absolute right-6 -bottom-2 text-xs text-tt-text-dimmed">16</span>
        </div>
      
        <div id="hour_line_17" class="relative flex-1">
          <span class="absolute right-6 -bottom-2 text-xs text-tt-text-dimmed">17</span>
        </div>
      
        <div id="hour_line_18" class="relative flex-1">
          <span class="absolute right-6 -bottom-2 text-xs text-tt-text-dimmed">18</span>
        </div>
      
        <div id="hour_line_19" class="relative flex-1">
          <span class="absolute right-6 -bottom-2 text-xs text-tt-text-dimmed">19</span>
        </div>
      
        <div id="hour_line_20" class="relative flex-1">
          <span class="absolute right-6 -bottom-2 text-xs text-tt-text-dimmed">20</span>
        </div>
      
    </div>
    <div phx-hook="TimetaskApp.WeekCalendar#WeekCalendar" class="flex-1 flex" id="week-calendar-columns" x-init="fromHour = +$el.dataset.fromHour; toHour = +$el.dataset.toHour;" x-data="week_calendar_data" data-from-hour="7" data-to-hour="20">  
      <div class="w-0 h-full flex flex-col"> 
        
          <div id="hour_7" class="calendar-line flex-1"></div>
        
          <div id="hour_8" class="calendar-line flex-1"></div>
        
          <div id="hour_9" class="calendar-line flex-1"></div>
        
          <div id="hour_10" class="calendar-line flex-1"></div>
        
          <div id="hour_11" class="calendar-line flex-1"></div>
        
          <div id="hour_12" class="calendar-line flex-1"></div>
        
          <div id="hour_13" class="calendar-line flex-1"></div>
        
          <div id="hour_14" class="calendar-line flex-1"></div>
        
          <div id="hour_15" class="calendar-line flex-1"></div>
        
          <div id="hour_16" class="calendar-line flex-1"></div>
        
          <div id="hour_17" class="calendar-line flex-1"></div>
        
          <div id="hour_18" class="calendar-line flex-1"></div>
        
          <div id="hour_19" class="calendar-line flex-1"></div>
        
          <div id="hour_20" class="calendar-line"></div>
        
      </div>
      <div class="flex-1 flex" id="week-calendar-days-container" x-ref="days_container" @mousemove.outside="updateNewTimeboxOutside($event)" @mouseup.stop.outside="stopNewTimebox()" @keydown.escape.window="newTimebox = null"> 
        
          <div id="day_col_2021-11-08" data-date="2021-11-08" class="flex-1 border-r border-tt-border-calendar relative" @mousedown.stop="startNewTimebox($event, $el.dataset.date)" @mousemove.stop="onColumnMouseMouse($event, $el.dataset.date)" " @mouseup.stop="onColumnMouseUp()">
            <div :style="`
                display: ${showNewTimebox(&#39;2021-11-08&#39;) ? &#39;block&#39;: &#39;none&#39;};
                height: ${newTimeboxGeometry().height}px;
                top: ${newTimeboxGeometry().top}px;
              `" class="absolute left-3 right-0 rounded-md bg-gray-100 opacity-50"></div> 

            <div :style="`
                display: ${showMovingTimebox(&#39;2021-11-08&#39;) ? &#39;block&#39;: &#39;none&#39;};
                height: ${movingTimeboxGeometry().height}px;
                top: ${movingTimeboxGeometry().top}px;
                ${movingTimeboxColors()};
              `" class="absolute left-3 right-0 rounded-md bg-gray-100 opacity-100 z-20"></div> 
            

          </div>
        
          <div id="day_col_2021-11-09" data-date="2021-11-09" class="flex-1 border-r border-tt-border-calendar relative" @mousedown.stop="startNewTimebox($event, $el.dataset.date)" @mousemove.stop="onColumnMouseMouse($event, $el.dataset.date)" " @mouseup.stop="onColumnMouseUp()">
            <div :style="`
                display: ${showNewTimebox(&#39;2021-11-09&#39;) ? &#39;block&#39;: &#39;none&#39;};
                height: ${newTimeboxGeometry().height}px;
                top: ${newTimeboxGeometry().top}px;
              `" class="absolute left-3 right-0 rounded-md bg-gray-100 opacity-50"></div> 

            <div :style="`
                display: ${showMovingTimebox(&#39;2021-11-09&#39;) ? &#39;block&#39;: &#39;none&#39;};
                height: ${movingTimeboxGeometry().height}px;
                top: ${movingTimeboxGeometry().top}px;
                ${movingTimeboxColors()};
              `" class="absolute left-3 right-0 rounded-md bg-gray-100 opacity-100 z-20"></div> 
            

          </div>
        
          <div id="day_col_2021-11-10" data-date="2021-11-10" class="flex-1 border-r border-tt-border-calendar relative" @mousedown.stop="startNewTimebox($event, $el.dataset.date)" @mousemove.stop="onColumnMouseMouse($event, $el.dataset.date)" " @mouseup.stop="onColumnMouseUp()">
            <div :style="`
                display: ${showNewTimebox(&#39;2021-11-10&#39;) ? &#39;block&#39;: &#39;none&#39;};
                height: ${newTimeboxGeometry().height}px;
                top: ${newTimeboxGeometry().top}px;
              `" class="absolute left-3 right-0 rounded-md bg-gray-100 opacity-50"></div> 

            <div :style="`
                display: ${showMovingTimebox(&#39;2021-11-10&#39;) ? &#39;block&#39;: &#39;none&#39;};
                height: ${movingTimeboxGeometry().height}px;
                top: ${movingTimeboxGeometry().top}px;
                ${movingTimeboxColors()};
              `" class="absolute left-3 right-0 rounded-md bg-gray-100 opacity-100 z-20"></div> 
            

          </div>
        
          <div id="day_col_2021-11-11" data-date="2021-11-11" class="flex-1 border-r border-tt-border-calendar relative" @mousedown.stop="startNewTimebox($event, $el.dataset.date)" @mousemove.stop="onColumnMouseMouse($event, $el.dataset.date)" " @mouseup.stop="onColumnMouseUp()">
            <div :style="`
                display: ${showNewTimebox(&#39;2021-11-11&#39;) ? &#39;block&#39;: &#39;none&#39;};
                height: ${newTimeboxGeometry().height}px;
                top: ${newTimeboxGeometry().top}px;
              `" class="absolute left-3 right-0 rounded-md bg-gray-100 opacity-50"></div> 

            <div :style="`
                display: ${showMovingTimebox(&#39;2021-11-11&#39;) ? &#39;block&#39;: &#39;none&#39;};
                height: ${movingTimeboxGeometry().height}px;
                top: ${movingTimeboxGeometry().top}px;
                ${movingTimeboxColors()};
              `" class="absolute left-3 right-0 rounded-md bg-gray-100 opacity-100 z-20"></div> 
            

          </div>
        
          <div id="day_col_2021-11-12" data-date="2021-11-12" class="flex-1 border-r border-tt-border-calendar relative" @mousedown.stop="startNewTimebox($event, $el.dataset.date)" @mousemove.stop="onColumnMouseMouse($event, $el.dataset.date)" " @mouseup.stop="onColumnMouseUp()">
            <div :style="`
                display: ${showNewTimebox(&#39;2021-11-12&#39;) ? &#39;block&#39;: &#39;none&#39;};
                height: ${newTimeboxGeometry().height}px;
                top: ${newTimeboxGeometry().top}px;
              `" class="absolute left-3 right-0 rounded-md bg-gray-100 opacity-50"></div> 

            <div :style="`
                display: ${showMovingTimebox(&#39;2021-11-12&#39;) ? &#39;block&#39;: &#39;none&#39;};
                height: ${movingTimeboxGeometry().height}px;
                top: ${movingTimeboxGeometry().top}px;
                ${movingTimeboxColors()};
              `" class="absolute left-3 right-0 rounded-md bg-gray-100 opacity-100 z-20"></div> 
            

          </div>
        
          <div id="day_col_2021-11-13" data-date="2021-11-13" class="flex-1 border-r border-tt-border-calendar relative" @mousedown.stop="startNewTimebox($event, $el.dataset.date)" @mousemove.stop="onColumnMouseMouse($event, $el.dataset.date)" " @mouseup.stop="onColumnMouseUp()">
            <div :style="`
                display: ${showNewTimebox(&#39;2021-11-13&#39;) ? &#39;block&#39;: &#39;none&#39;};
                height: ${newTimeboxGeometry().height}px;
                top: ${newTimeboxGeometry().top}px;
              `" class="absolute left-3 right-0 rounded-md bg-gray-100 opacity-50"></div> 

            <div :style="`
                display: ${showMovingTimebox(&#39;2021-11-13&#39;) ? &#39;block&#39;: &#39;none&#39;};
                height: ${movingTimeboxGeometry().height}px;
                top: ${movingTimeboxGeometry().top}px;
                ${movingTimeboxColors()};
              `" class="absolute left-3 right-0 rounded-md bg-gray-100 opacity-100 z-20"></div> 
            

          </div>
        
          <div id="day_col_2021-11-14" data-date="2021-11-14" class="flex-1 border-r border-tt-border-calendar relative" @mousedown.stop="startNewTimebox($event, $el.dataset.date)" @mousemove.stop="onColumnMouseMouse($event, $el.dataset.date)" " @mouseup.stop="onColumnMouseUp()">
            <div :style="`
                display: ${showNewTimebox(&#39;2021-11-14&#39;) ? &#39;block&#39;: &#39;none&#39;};
                height: ${newTimeboxGeometry().height}px;
                top: ${newTimeboxGeometry().top}px;
              `" class="absolute left-3 right-0 rounded-md bg-gray-100 opacity-50"></div> 

            <div :style="`
                display: ${showMovingTimebox(&#39;2021-11-14&#39;) ? &#39;block&#39;: &#39;none&#39;};
                height: ${movingTimeboxGeometry().height}px;
                top: ${movingTimeboxGeometry().top}px;
                ${movingTimeboxColors()};
              `" class="absolute left-3 right-0 rounded-md bg-gray-100 opacity-100 z-20"></div> 
            

          </div>
        
      </div>
    </div>
  </div>
</div>

  </div>

  


  


    <div x-data>
  
    
  
    
  
</div>



    </div>
  <iframe hidden height="0" width="0" src="/phoenix/live_reload/frame"></iframe></body>
</html>
</div>

What you are asking is inside the element with id week-calendar-days-container

Probably doesn’t matter but you have a random " in the divs with the IDs <div id="day_col_2021-11-09"?

I’ve had to use alpines template tag (wrapped in a Raw tag - I use surface) for somewhere where I generated a list of things which had myriad alpine bindings. Unfortunately, I’ve stepped away from the computer for the day so I can’t show you an example.

By any chance are you back yet? I’m stilll having this issue

Here is a cut down version. Note that I’m using Surface, hence the <#Raw> tags (I assume there is a parallel in plain old liveview) and different syntax. The hook reads attributes from the hidden spans and sends them to Apline. Alpine creates the html for the options. I had a bit of trouble getting this to work and when it worked I stopped touching it and moved on. I hope to revist if/when I get some time :wink:

    <div
      id={"column-filter-#{@field}"}
      :hook="ColumnOptionsFilter"
      x-data="{
        ...
        options: [],
        ...
      }"
      @options-loaded="options = $event.detail.options"
    ...
    >
      <div id={"column-filter-#{@field}-data"} hidden>
        {#for option <- @all_options}
          <span data-option={option} />
        {/for}
      </div>

    ...

        <div id={"column-filter-options-#{@field}"} phx-update="ignore">
          <#Raw>
          <template x-for="(option, index) in options" :key="index">
          </#Raw>
            <div class="flex items-center py-1">
              <input
                x-model="selected"
                :value="option"
                :id="option"
                :name="option"
                type="checkbox"
                :class="{ 'opacity-50': !available[index]}"
                class="h-4 w-4 text-blue-900 focus:ring-blue-900 border-gray-300"
              />
              <label
                :for="option"
                x-text="option"
                :class="{ 'text-gray-400': !available[index], 'text-gray-900': available[index] }"
                class="ml-2 block text-sm"
              />
            </div>
          <#Raw>
          </template>
          </#Raw>
        </div>
     ...
     </div>

Hook:

const ColumnOptionsFilter = {
  mounted() {
    const options = Array.from(
      document.getElementById(this.el.id + "-data").children || []
    ).map(({ dataset: { option, available, selected } }) => {
      return option === undefined ? "[Blanks]" : option;
    });

   ...

    setTimeout(() => {
      if (options.length > 0) {
        var event = new CustomEvent("options-loaded", {
          detail: { options: options, available: available },
        });
        this.el.dispatchEvent(event);
        this.optionsLoaded = true;
      }
    }, 0);
  },
1 Like

:raised_hands: thanks!

Me too!

Solved it!

Solution summary:
Don’t generate LiveView controlled tags inside x-data scope. Use alpine’s templates to render and pass data to x-data.

Do this:

<div x-data={"{elements: #{render_elements(elements)}}"}>
    <#Raw><template x-for="ele in elements"></#Raw>
         <span x-text="ele" @click="console.log('click')"/>
    <#Raw></template></#Raw>
</div>

Not this:

<div x-data>
    {#for ele in elements}
        <span @click="console.log('click')">{ele}</span>
    {/for}
</div>
2 Likes