imacrayon / alpine-ajax

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

Filterable content + infinite scroll: how to set x-merge attribute #71

Closed n355 closed 4 months ago

n355 commented 4 months ago

I am combining both filterable content and infinite scroll (based on your examples, thanks for providing them). I don't understand how to set the x-merge attributes, as I need to append the next pages and replace the filtered posts. Here is a reduced test case; some parts are written in Laravel Blade, so I apologize if it's confusing. I'm not sure if this is related to https://github.com/imacrayon/alpine-ajax/issues/68 because in my scenario, I have one target and two different merge strategies.

<main x-data="{ nextPage: 2, lastPage: {{ $posts->lastPage() }} }">

    {{-- Filter --}}
    <form action="/posts"
        x-target="posts">
        @csrf
        <select name="filter"
            x-on:change="$el.form.requestSubmit()">
            <option value="">All</option>
            <option value="Active">Active</option>
            <option value="Inactive">Inactive</option>
        </select>
    </form>

    {{-- Posts --}}
    <div class="grid grid-cols-2 gap-4 p-4"
        id="posts"
        x-merge="replace">
        @foreach ($posts as $post)
            <article class="h-[50vh] border">
                <p>{{ $post->title }}</p>
            </article>
        @endforeach
    </div>

    {{-- Pagination --}}
    <div id="pagination"
        x-show="lastPage && nextPage <= lastPage"
        x-intersect="$ajax(`/posts?page=${nextPage}`); nextPage++"
        x-target="posts pagination">
        Loading...
    </div>

</main>

Thanks

imacrayon commented 4 months ago

You could try splitting the two merge strategies across nested containers like this:

<form>…</form>
<div id="posts" x-merge="replace">
   <div id="pages" x-merge="append" class="grid…">
        @foreach ($posts as $post)
            <article class="h-[50vh] border">
                <p>{{ $post->title }}</p>
            </article>
        @endforeach
    </div>
    <div id="pagination">…</div>
</div>

Your filter form would target posts and the pagination would target pages pagination.

n355 commented 4 months ago

Great, it works. Thanks for the quick reply and the example. Here's the result:

<main>

    {{-- Filter --}}
    <form action="/posts"
        x-target="posts">
        @csrf
        <select name="filter"
            x-on:change="$el.form.requestSubmit()">
            <option value="">All</option>
            <option value="Active">Active</option>
            <option value="Inactive">Inactive</option>
        </select>
    </form>

    {{-- Posts --}}
    <div id="posts"
        x-merge="replace">

        {{-- Pages --}}
        <div id="pages"
            x-merge="append">
            @foreach ($posts as $post)
                <article>
                    <p>{{ $post->title }}</p>
                </article>
            @endforeach
        </div>

        {{-- Pagination --}}
        @if ($posts->currentPage() < $posts->lastPage())
            <div id="pagination"
                x-intersect="$ajax(`/posts?filter={{ $filter }}&page={{ $posts->currentPage() + 1 }}`)"
                x-target="pages pagination">
                Loading...
            </div>
        @endif

    </div>

</main>
imacrayon commented 4 months ago

Awesome! Thanks for reaching out. If you have any ideas how we might be able to make x-merge more clear in the docs I’d be open to a PR.