putyourlightson / craft-sprig

A reactive Twig component framework for Craft CMS.
https://putyourlightson.com/plugins/sprig
MIT License
129 stars 9 forks source link

htmx error using s-replace #302

Closed rogerdawkins closed 1 year ago

rogerdawkins commented 1 year ago

Hi Ben

I'm creating an accommodation listing page with multiple filters for property type, facilities, etc. It's very much along the lines of your Sprig Filters video example and I've tried to recreate what you have done as closely as possible.

For my example code below I have simplified it as much as possible to simulate the issue. For filters I have a text box which filters on the title field and two property type checkboxes (proptypes[]). The results just display the top 25 entries based on these filters again to keep it simple.

When I run the code without using s-replace it works fine, however I then get the issue that you have shown in your videos where the filters get updated as well and it gets messy especially typing text as typed text gets overridden with incoming filter text.

I then added the s-replace attribute so only the results area gets updated which initially appears to work fine. If I click a checkbox filter and let the results update and then click another checkbox and let the results update it all works fine. But if I click multiple checkboxes in quick succession I then start seeing TypeError: Cannot read properties of null (reading 'querySelector') and htmx:swapError messages in the console. It's quicker to see it if you switch throttling on in Chrome.

I tried running your Sprig Filters demo with throttling on and your demo works fine, but I think your whole component is getting rendered and therefore you are not using s-replace. I'm curious to know whether you would have the same issue if you were to add s-replace on your Sprig Filters demo and quickly click on different styles and colours.

Below is my very stripped down component code.

{% set prop_name = prop_name ?? '' %}
{% set proptypes = proptypes ?? [] %}

{% set entryQuery = craft.entries.section('accommodation')
    .with(['images'])
    .limit(25) %}

{% if prop_name|length %}
    {% do entryQuery.search('*' ~ prop_name ~ '*') %}
{% endif %}

{% if proptypes|length %}
    {% do entryQuery.propertyType(proptypes) %}
{% endif %}

{% set entries = entryQuery.all() %}

<div class="inner flex pt-6 px-4 pb-0">
    <div id="filters" s-replace="#results" class="w-[250px] min-w-[250px]">
        Filter By:<br>
        <p class="font-bold mt-2">Property name</p>
        <input sprig s-trigger="keyup changed delay:200ms" type="text" id="prop_name" name="prop_name" value="{{ prop_name }}">

        <p class="font-bold mt-3">Property type</p>
        {% apply spaceless %}

        <input sprig type="checkbox" name="proptypes[]" id="prop_room" value="room" {{ 'room' in proptypes ? 'checked' : '' }}>
        <label for="prop_room" class="pl-2">Room</label><br>

        <input sprig type="checkbox" name="proptypes[]" id="prop_entire" value="entire" {{ 'entire' in proptypes ? 'checked' : '' }}>
        <label for="prop_entire" class="pl-2">Entire Property</label><br>

        {% endapply %}
    </div>

    <div id="results" class="content md:pl-8 w-full">
        Properties found: {{ entries|length }}<br>
        {% for entry in entries %}
            <h2 class="font-bold"><a href="{{ entry.url }}">{{ entry.title }}{% if entry.town|length %} - {{ entry.town }}{% endif %}</a></h2>
            {{ entry.propertyType.label }}<br>
        {% else %}
            <p>No properties found.</p>
        {% endfor %}
    </div>
</div>
bencroker commented 1 year ago

So everything works fine, except when you activate filtering too fast?

rogerdawkins commented 1 year ago

Yes, when I wait for the reload to finish it's fine. If I happen to click a checkbox option (there are a lot more on the full component) before the previous load has finished then I see the errors and it breaks the page.

Interestingly I have just tried removing the s-replace and going for s-target on each checkbox and wrapping the filter area in a sprig.isRequest and that looks to be working. So the issue seems to be specific to s-replace.

bencroker commented 1 year ago

s-replace="#results" is just shorthand for s-select="#results" s-target="#results" s-swap="outerHTML". Perhaps using sprig.isRequest is making the response quicker and therefore less prone to fail? Either way, if this is a bug then it must be with htmx, so perhaps you can ask in one of its support channels.