Renerick / htmx-signalr

htmx extension for interacting with ASP.NET Core SignalR connections directly from HTML
MIT License
39 stars 5 forks source link

oob swap via extension fails if parent had [hx-swap]="outerHTML" #6

Open anpin opened 1 year ago

anpin commented 1 year ago

Moving issue here from https://github.com/bigskysoftware/htmx/issues/1419

I'm using htmx 1.9.2 with htmx-signalr and it fails on this line https://github.com/bigskysoftware/htmx/blob/990cdd61a110bd497cb50d2286407ac6b7b64183/src/htmx.js#L948 with

023-04-29T12:36:15.320Z] Error: A callback for the method 'component-update' threw error 'TypeError: Cannot read properties of null (reading 'firstChild')'.

If element received via signalr had hx-swap-oob="outerHTML" and some parent of the target had hx-swap="outerHTML"

For example with such layout:

<div hx-ext="signalr" signalr-connect="/hub" > 
 <div id="second-level" hx-swap="outerHTML"> 
   <div id="component" signalr-subscribe="component-update" hx-target="this" > <div/>
  <div/>
<div/>

When extension receives

   <div id="component" signalr-subscribe="component-update" hx-target="this" hx-swap-oob="outerHTML" >Some new content here<div/>

It replaces the content correctly, but then throws above exception after which any further updates would be ignored.

The workaround is to set hx-swap="innerhtml" on the target node in the original layout [1], so call to getSwapSpecification here returns innerhtml.

[1] Workaround

<div hx-ext="signalr" signalr-connect="/hub" > 
 <div id="second-level" hx-swap="outerHTML"> 
   <div id="component" signalr-subscribe="component-update" hx-target="this" hx-swap="innerhtml"> <div/>
  <div/>
<div/>
Renerick commented 1 year ago

Do you by any chance use web components or some JS lib? I'm completely unable to reproduce Error: A callback for the method 'component-update'

As for subscription not working after first swap, this is a legitimate bug, but it doesn't seem to be related with the error message you observe

Disregard everything above. For some reason I did not see error message in the console so my initial analysis was completely wrong

Renerick commented 1 year ago

Basically, the following happens

  1. Message is received
  2. Target is extracted from hx-target
  3. Swapping begins
  4. OOB swapping replaces the target
  5. The old target is unmounted from the DOM but the reference to it is still alive within the swapping method
  6. The normal swapping begins
  7. The element is unmounted so it has no siblings and no parent
  8. Error is raised

Workaround with innerHTML is working because there is no attempt to access parent or sibling of the target, and there is a lot of guards for empty values, so when it's unmounted from the DOM, no errors happen.

I'll have to do more triagin, for I suspect that this may affect any cases when OOB swap replaces original target. Maybe the issue would have to be moved back to htmx repo

As a general tip, I would advise against using OOB swaps on existing targets. OOB is intended to be used for updates somewhere else on the page, whereas in your case you are handling the same target. Simply returning [1] without hx-swap-oob from the hub works as expected

[1]

<div id="component" signalr-subscribe="echo" hx-target="this">Some new content here<div/>