Renerick / htmx-signalr

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

Adding additional event on connection error #18

Open xxnickles opened 1 month ago

xxnickles commented 1 month ago

First, let me thank you for taking time creating this extension, especially when the use case is so narrow (it is rare for signal r to be used outside of dotnet, ant htmx is for sure not the most popular technologies out there)

While trying to make sense of the event emitted as was not been able to understand why I 'htmx:signalr:reconnecting' nor 'htmx:signalr:reconnected' were being triggered (made sense of this after reading the code) I realized that the connection errors on the start are not being managed as there is not catch block for the start promise. Would you consider creating an additional event for when a connection error happens?

Here is a little snippet of what I did (note I put bunch of console.log to understand when events are triggered) that can be used as reference

        // Create a new HubConnection and event handlers
        /** @type {HubConnection} */
        var hubConnection = htmx.createHubConnection(signalrHubUrl);        

        hubConnection.onreconnecting(function (error) {
            console.log('Triggering htmx:signalr:reconnecting from onreconnecting');
            api.triggerEvent(hubElt, 'htmx:signalr:reconnecting', { error: error });
        });
        hubConnection.onreconnected(function (connectionId) {
            console.log('Triggering htmx:signalr:reconnected from onreconnected');
            api.triggerEvent(hubElt, 'htmx:signalr:reconnected', { connectionId: connectionId });
        });
        hubConnection.onclose(function (error) {
            console.log('Triggering htmx:signalr:close from onclose');
            api.triggerEvent(hubElt, 'htmx:signalr:close', { error: error });
        });
        hubConnection.start().then(function () {
            console.log('Triggering htmx:signalr:start from start');
            api.triggerEvent(hubElt, 'htmx:signalr:start', { connectionId: hubConnection.connectionId })
        }).catch(function(ex) {
            console.log(ex);
            console.log('Triggering htmx:signalr:connection-error from start error');
            api.triggerEvent(hubElt, 'htmx:signalr:connection-error', { message:ex.message, errorType:ex.errorType })
        });
Renerick commented 1 month ago

Hello, thank you for the kind words and the feature request. I pushed a version with your proposed changes into dev branch. I'll merge it master after a few days, feel free to try it out in the mean time

xxnickles commented 1 month ago

Thansk so much! Tested and working!. Btw, I have added another event to your script to address a particular scenario of mine as I am using browser storage to keep the last hub status

 var hubConnection = htmx.createHubConnection(signalrHubUrl);
        api.triggerEvent(hubElt, 'htmx:signalr:connection-starting');

I don't know if that is a worth addition for anybody else, but I left it here for reference. This is the way I am using the events (bear in mind I usse AlpineJS instead the htmx helper in the documentation)

<div x-data="{
            status:'connecting',
            init() {
                if (localStorage.getItem('last-hub-status')) {
                    this.status = localStorage.getItem('last-hub-status');
                }
            },
            updateStatus(status) {
                this.status = status;
                localStorage.setItem('last-hub-status', this.status);
            }
        }"
     @htmx:signalr:connection-starting.window="updateStatus('connecting')"
     @htmx:signalr:reconnecting.window="updateStatus($event.detail.error ? 'error' : 'reconnecting')"
     @htmx:signalr:reconnected.window="updateStatus($event.detail.error ? 'error' : 'connected')"
     @htmx:signalr:start-error.window="updateStatus('error')"
     @htmx:signalr:start.window="updateStatus('connected')">
    <h5 class="text-sm">
        <b class="hidden sm:contents">Hub Status:</b>
        <span class="badge badge-info badge-sm ml-1" x-show="status === 'connecting'">Connecting</span>
        <span class="badge badge-success badge-sm ml-1" x-show="status === 'connected'" x-cloak>Connected</span>
        <span class="badge badge-info badge-sm ml-1" x-show="status === 'reconnecting'" x-cloak>Reconnecting</span>
        <span class="badge badge-error badge-sm ml-1" x-show="status === 'error'" x-cloak>Error</span>
    </h5>

</div>

Thanks again for taking time maintaining this lib!

Renerick commented 1 month ago

Just a fun story, when I got an idea for this extension I had to refactor htmx 1.0 core and internal extensions API which eventually led to me joining the team as SSE and WS maintainer!

As for htmx:signalr:connection-starting, I'll think about it. Will keep this issue open for a couple of days then

xxnickles commented 1 month ago

Just a fun story, when I got an idea for this extension I had to refactor htmx 1.0 core and internal extensions API which eventually led to me joining the team as SSE and WS maintainer!

Well, you went all-in into the rabbit hole! I hope more people get to know these extensions. The ability to push "HTML events" is a piece of beauty people is missing. I am using your extension + Azure SignalR service pushing rendered blazor components from a functions app, it is just mind blowing how trivial becomes to send notifications to the client. I did something similar in Webassembly and was a hole load of pain just at the serialization/deserialization part. I still need to get the group working, but that is more a function of my own mess lol

Renerick commented 1 month ago

Just a heads up, I added htmx:signalr:staring event and updated the docs with the new events in the dev branch. I'll again keep it in dev for a couple of days (or about a week apparently) and THEN definitely merge to master

Apologies for a bit of a delay