imacrayon / alpine-ajax

An Alpine.js plugin for building server-powered frontends.
https://alpine-ajax.js.org
MIT License
664 stars 14 forks source link

Target [#form] was not found in current document #105

Closed Jagadish056 closed 4 weeks ago

Jagadish056 commented 1 month ago

Steps to Reproduce:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Laravel Playground</title>
    <script defer src="https://cdn.jsdelivr.net/npm/@imacrayon/alpine-ajax@0.9.1/dist/cdn.min.js"></script>
    <script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/morph@3.x.x/dist/cdn.min.js"></script>
    <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.3/dist/cdn.min.js"></script>
</head>
<body>
    <section id="section-1" x-merge="morph">
        <form x-target="section-1" method="get">
            <nav>
                @foreach (['Tab 1', 'Tab 2', 'Tab 3'] as $tab)
                    <button type="submit" name="tab" value="{{ $tab }}">{{ $tab }}</button>
                @endforeach
            </nav>
        </form>

        @switch(request()->input('tab'))
            @case('Tab 2')
                Default Text 2
                <form id="form-1" method="post" x-target>
                    @csrf
                    {{-- <button type="submit">Button</button> --}}
                </form>
                @break
            @case('Tab 3')
                Default Text 3
                <form id="form-2" method="post" x-target>
                    @csrf
                    <button type="submit">Button</button>
                </form>
                @break
            @default
                Default Text 1
        @endswitch
    </section>
</body>
</html>
imacrayon commented 1 month ago

Hey Jagadish,

Thanks for the easy reproduction! I didn't know about that Laravel Playground either, that's a nice tool.

Unfortunately, this looks like an upstream issue with Alpine Morph. It seems that the Morph algorithm doesn't catch the change between #form-1 and #form-2 and treats both forms as the same element (form-1).

There's two quick fixes for this problem though:

  1. Remove x-merge="morph" and use x-autofocus on the tab buttons to maintain keyboard focus.
  2. Add a key="anything-here" attribute to one of the forms. With a different key on both forms, Morph will work as expected.
Jagadish056 commented 1 month ago

Thanks for the quick fixes! I’ve been waiting all day, and it seems to be working perfectly now.

Jagadish056 commented 1 month ago

Hey @imacrayon, sorry to trouble you again!

Without Morph, all content gets refreshed (not a browser reload 🔄) when switching tabs. What happens if I use all three: Morph, x-autofocus, and key? Could this cause issues in the future?

<section x-merge="morph">
    ...
        <button @if($activeTab) x-autofocus @endif>{{ $tab }}</button>
    ...

    <div key="{{ Str::random(64) }}">
        @switch(true)
            @case(1)
                ...
                @break
            @case(2)
                ...
                @break
            @default
                ...
        @endswitch
    </div>
</section>

Update: There was also an issue with the loading indicator aria-busy="true" while enabling Laravel Debugbar. By default, Laravel Playground has the Laravel Debugbar installed, and you can encounter this issue when switching tabs super fast.

imacrayon commented 4 weeks ago

Using all of those together shouldn't cause problems. I'd file an issue if you find something though. One thing to note: If you're using Morph you probably don't need x-autofocus since Morph should automatically preserve keyboard focus for you.

Jagadish056 commented 4 weeks ago

Alright, I'll keep that in mind, but one question remains: in this video ?v=31pBMi0UdYE&t=1637s, he's explaining the concept of Roving Tab Index. Is this currently possible with Alpine AJAX?

imacrayon commented 3 weeks ago

You can issue the request on focus like this: x-on:focus="$ajax('?tab1', {target: 'tab1_content'})

Alternatively, you could use the x-intersect plugin and issue the same AJAX request when a tab is revealed.