biff

Nik 2024-08-24T00:49:16.369909Z

I'm building a web app for mobile screen where goal is to look like typical mobile app while adhering to web standards wherever possible. I've a separate page for editing or creating entity (eg a task). The layout is

[:body 
  [:header 
     [:button#first ...]
     [:button#upsert {:hx-include "next form" :hx-put "/path"}]]
  [:main ...]
  ]
I want to use HTTP PUT when the intent is updating existing entity and POST when creating new when clicking :button#upsert then redirect user to appropriate page. Right now I use {:hx-include "next form" :hx-put "/path"} together with {:status 303 :headers {"hx-location" "/new-path"}} Everything works as intended on screen but I get following error. My assumption is this the listener which takes care htmx swap functionality but since we did full page refresh the websocket object reference is outdated (and un-needed).
TypeError: Cannot read properties of undefined (reading 'close')
    at maybeCloseWebSocketSource (ws.js:420:38)
    at socket.onclose (ws.js:260:11)
I just wanted to know if 1. What I'm doing is acceptable way to handle this use case 2. Any alternatives where this error can be avoided (or the use case where error cause is irrelevant). One thing I can think of is to swap the entire body with new content; make it standard way of navigation through out the app instead of full page refresh

2024-08-24T16:33:32.957799Z

What you're currently doing seems like a reasonable approach to me. Note that the https://htmx.org/headers/hx-location/ say: > This response header can be used to trigger a client side redirection without reloading the whole page. Instead of changing the page’s location it will act like following a hx-boosthttps://htmx.org/attributes/hx-boost/, creating a new history entry, issuing an ajax request to the value of the header and pushing the path into history. i.e. it's already doing what you describe in #2. https://github.com/bigskysoftware/htmx/blob/f38e07d4be8145a39e2bb477ec9fcc56bdd2d16d/src/ext/ws.js#L420 is the line that's throwing the exception (assuming you're on v1.9.12). Not immediately obvious to me why webSocket would be null... I wonder if it's a bug. If you wanted to dig into it, you could copy that file to e.g. resources/public/js/ws.js, and update https://github.com/jacobobryant/biff/blob/28afb31cfcafc59f8e60ad32066ac0bb58691f7c/starter/src/com/example/ui.clj#L39 in ui.clj to point to that local file. Then you could add some log statements and try to poke around; maybe do something like this to start:

function maybeCloseWebSocketSource(elt) {
		if (!api.bodyContains(elt)) {
      console.log(elt);
      console.log(api.getInternalData(elt));
			api.getInternalData(elt).webSocket.close();
			return true;
		}
		return false;
	}
See also https://github.com/bigskysoftware/htmx/blob/f38e07d4be8145a39e2bb477ec9fcc56bdd2d16d/src/ext/ws.js#L156 which is where the webSocket property gets set. So the question is "why is maybeCloseWebSocketSource getting called with an element that doesn't have api.getInternalData(elt).webSocket set?" is it getting called with the wrong element somehow? That being said, if your page's functionality is working correctly, it's probably fine to just ignore the error if you don't feel like messing around with it 🤷

Nik 2024-08-26T10:41:47.638909Z

@foo You are correct, seeing the network panel, it doesn't seem like full refresh is happening and in fact <html> is being swapped in. I'll put this in backlog for now. Thanks for sharing the starting points to debug. I've bookmarked them and will dig around when I've time.

👍 1