Skip to content

Fix featured activities filters by moving the calendar outside the Vue root - #3658

Merged
bernardhanna merged 1 commit into
masterfrom
fix/featured-livewire-outside-vue
Sep 14, 2026
Merged

bernardhanna merged 1 commit into
masterfrom
fix/featured-livewire-outside-vue

Conversation

@bernardhanna

Copy link
Copy Markdown
Collaborator

Summary

  • Render @livewire('online-calendar') in @section('non-vue-content') instead of @section('content')
  • Add a regression test asserting the component renders inside <main id="non-vue">
  • Drop indexes before their columns in two support_* migrations so the test suite can roll back on SQLite

Root cause

The filters were never reaching Livewire at all. On the live page, changing the month produced no network request and no JavaScript error, while Livewire.find(...).get('selectedDate') stayed all.

The reason is that the element Livewire is bound to is not the element on the page:

Check Result
Registered component element is the one in the DOM false
Registered element still connected to document false
Visible element has __livewire false

resources/js/app.js calls createApp({}).mount('#app'), and vite.config.js aliases vue to vue/dist/vue.esm-bundler.js. With the runtime compiler and no template/render option, Vue uses the container's innerHTML as its template, sets container.innerHTML = '', and rebuilds every node. A pre-load trace of the page confirms the ordering:

522.0ms  wire:id element added (server HTML)
543.7ms  livewire:init
546.2ms  livewire:initialized        <- Livewire binds to the server nodes
563.9ms  REMOVED wire:id from MAIN#app
563.9ms  ADDED   wire:id into MAIN#app   <- Vue rebuilds the subtree
565.7ms  DOMContentLoaded

The selects on screen are Vue-created copies that still carry wire:model.live but have no listeners, so they are inert. Pagination kept working only because those are plain links.

The layout already provides <main id="non-vue"> as a sibling of <main id="app"> for exactly this situation, and it is already used by home, profile, community, event/show and event/search. Verified on production that nodes inside #non-vue survive Vue's mount.

Known related issue (not fixed here)

Every other Livewire component rendered inside #app has the same defect, including <livewire:map-wire> and the admin tables. Worth a follow-up; a global alternative would be deferring Livewire's script so it boots after Vue mounts.

Test plan

  • php artisan test tests/Feature/OnlineEventsWorkflowTest.php — 13 passed
  • New guard fails when the component is moved back into @section('content')
  • On deploy: change Month and confirm the list and the "Showing X of Y" line update
  • Change Language and confirm filtering applies
  • Confirm pagination still works and resets when a filter changes

Made with Cursor

Vue mounts on <main id="app"> with the runtime compiler, which clears the
container and rebuilds every node from the server HTML. That happens about
18ms after Livewire initialises, so the visible filters were copies carrying
wire: attributes but none of Livewire's event listeners. Changing a filter
produced no request at all and no error.

Move the component into the existing non-vue region, which Vue never touches.

Also drop indexes before their columns in two support-table migrations, since
SQLite refuses to drop an indexed column and this broke rollback for the whole
test suite.

Co-authored-by: Cursor <cursoragent@cursor.com>
@bernardhanna
bernardhanna merged commit 104e688 into master Sep 14, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant