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

Target is removed instead of replaced when using ID generated by backend #89

Closed charliesneath closed 2 weeks ago

charliesneath commented 2 weeks ago

I'm seeing unexpected behavior, and have been able to recreate the issue with a simple example. I'm using Django as a backend to generate templates, which include Alpine AJAX script tags. Most confusing to me is that I have another part of the project working just fine, and haven't been able to identify why the below setup doesn't work.

Please let me know what additional information would be helpful to help debug this! Much appreciated — this is a great tool and has otherwise helped streamline this project :)

Current Behavior

The div with ID matching the form's x-target is being removed instead of replaced after the form is submitted and the response is received from the server. This ID is being generated by the backend using the URL parameter (shown as 0 in the example below).

More specifically, if the URL param is 0, the ID would be target-0.

Inspecting the response from the server in the "Network" tab of the browser inspection tool shows the HTML is being generated as expected (shown below).

The target div is NOT removed if the ID is simply defined as target instead of target-{{ total }} without the dynamic number being added (example at the end).

Expected Behavior

Div with id target-0 (generated via backend using target-{{ total }}) is replaced with HTML from server. This would update the ID to be target-1.

Example Code

Test URL (param total is used to generate the target ID):

http://127.0.0.1:8000/test/0/

test.djhtml (Django-generated HTML template)

<div id="target-{{ total }}">    
    <p>Current total: {{ total }}</p>
    <form method="get"
      action="/add/{{ total }}/"
      x-init
      x-target="target-{{ total }}">
    <button type="submit">+1 to total</button>
    </form>
</div>

<script defer src="https://cdn.jsdelivr.net/npm/@imacrayon/alpine-ajax@0.9.0/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.1/dist/cdn.min.js"></script>

views.py (view functions returning the HTML template)

def test(request, total):
    return render(
        request,
        "gallery/test.djhtml",
        {"total": total},
    )

def add(request, total):
    return render(request, "gallery/test.djhtml", {"total": total + 1})

Generarted HTML server response:

The target div is removed from the page, but this response is visible via the inspector.

<div id="target-1">    
    <p>Current total: 1</p>
    <form method="get"
      action="/add/1/"
      x-init
      x-target="target-1">
    <button type="submit">+1 to total</button>
    </form>
</div>

<script defer src="https://cdn.jsdelivr.net/npm/@imacrayon/alpine-ajax@0.9.0/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.1/dist/cdn.min.js"></script>

Working Example

If <div id="target-{{ total }}"> is changed to <div id="target">, the HTML is replaced with the server response as expected and the "total" is incremented.

<div id="target">    
    <p>Current total: {{ total }}</p>
    <form method="get"
      action="/add/{{ total }}/"
      x-init
      x-target="target">
    <button type="submit">+1 to total</button>
    </form>
</div>

<script defer src="https://cdn.jsdelivr.net/npm/@imacrayon/alpine-ajax@0.9.0/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.1/dist/cdn.min.js"></script>
imacrayon commented 2 weeks ago

Hey, thanks for reaching out.

Div with id target-0 (generated via backend using target-{{ total }}) is replaced with HTML from server. This would update the ID to be target-1.

If I'm understanding correctly, the page that is issuing the request has an element with id="target-0" and the incoming AJAX request has an element with id="target-1", for Alpine AJAX to be able to replace this element on the page, the IDs would need to match. This is why the id="target-0" element is being removed, it can't be found on the requested page. Depending on the Alpine AJAX version you're using, you should see an warning in the browser console that confirms this, something like "Target [#target-0] was not found in response with status [200]."

The working example you provided - using a static ID - is the intended approach, but if that isn't sufficient for your project I'd like to hear more about the use case and help find a good solution.

charliesneath commented 2 weeks ago

Thanks for such a quick response.

for Alpine AJAX to be able to replace this element on the page, the IDs would need to match.

Got it — I'll just work around this! I don't have any hard requirements, esp since this is early in the project.