Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: listening to all htmx errors #1529

Open
metametadata opened this issue Jun 30, 2023 · 2 comments
Open

Proposal: listening to all htmx errors #1529

metametadata opened this issue Jun 30, 2023 · 2 comments

Comments

@metametadata
Copy link

I want a single function to be called on any error event from htmx. Currently I have something like this:

const errorEventTypes = ['error', 'htmx:historyCacheError', 'htmx:historyCacheMissError', 'htmx:noSSESourceError',
  'htmx:onLoadError', 'htmx:oobErrorNoTarget', 'htmx:responseError', 'htmx:sendError', 'htmx:sseError',
  'htmx:swapError', 'htmx:targetError', 'unhandledrejection'];

errorEventTypes.forEach((type) => {
  window.addEventListener(type, (_) => {
    // react to any app error
  });
});

But it's unreliable since in future htmx versions more error event types can be added or I can make a typo in the list.

How to listen to all htmx error events in a more reliable way?

Ideas off the top of my head:

  1. Fire a new event before any more specific error event? E.g. htmx:beforeError.
  2. Add a function to listen for any error. E.g. htmx.onError(callback).
  3. Fire a single htmx:error event and add a field to it to distinguish between causes.
@vrzh
Copy link

vrzh commented Jul 6, 2023

I'm by no means a htmx developer, but recently had cause to read some of the source code.

It seems there is a triggerErrorEvent function which is part of the internal API that is called whenever any error event needs to be triggered. The easiest hack would be to serve your own modified version of htmx with an added call to your logging / error processing function.

htmx/src/htmx.js

Line 1986 in e923482

function triggerErrorEvent(elt, eventName, detail) {

In the source code, you will notice that this function sets an error property and the calls triggerEvent:

htmx/src/htmx.js

Line 2021 in e923482

function triggerEvent(elt, eventName, detail) {

In this function, two events are triggered for errors. One general event with type htmx:error and one specific event with a more specific error type, e.g. htmx:responseError.

This means your third point is already implemented (but seems to be undocumented), you can do:

htmx.on("htmx:error", function(evt) {
  // print the error message
  console.log(evt.detail.errorInfo.error);
});

Unfortunately, the specific error, e.g. htmx:responseError is not present in this general event object.

If you really do need to catch the specific error, then looks like you can implement it in a htmx extension which you apply to the body:

<!doctype html>
<html>

<head>
    <meta charset="utf-8">
</head>

<body hx-ext="catch">

    <!-- force a 'htmx:responseError' event -->
    <form hx-post="/non-existent-url" method="POST">
        <button type="submit">Send</button>
    </form>

    <script src="https://unpkg.com/htmx.org@1.9.2"></script>

    <script>
        htmx.defineExtension('catch', {
            onEvent: function (name, evt) {
                // interested only in errors
                if(evt.detail?.errorInfo?.error) {
                    // the general 'htmx:error' event
                    console.log({
                        type:evt.type, 
                        info: evt.detail.errorInfo?.error
                    });
                }
                if(evt.detail?.error) {
                    // the specific event, e.g. 'htmx:responseError'
                    console.log({
                        type: evt.type,
                        info: evt.detail.error
                    });
                }
            }
        });
    </script>

</body>

</html>

Again, looking at the source code, htmx will always traverse the DOM tree upwards so you are always guaranteed to receive the event.

So overall, all 3 of your points are sort of already implemented, just need some tweaks to make it more efficient :)

@metametadata
Copy link
Author

Thank you for detailed response!

So far I've settled for catching the undocumented htmx:error event in order to integrate htmx with Sentry error reporting:

class HtmxError extends Error {
  constructor(message, errorInfo) {
    super(message);
    this.name = this.constructor.name;
    this.errorInfo = errorInfo; // Reporter is expected to support non-native Error attributes
  }
}

// Turn htmx error events into usual uncaught errors which can be listened for in window (and thus will be reported).
// HACK: htmx:error is undocumented, see https://github.com/bigskysoftware/htmx/issues/1529.
window.addEventListener('htmx:error', (e) => {
  throw new HtmxError(e.detail.errorInfo.error, e.detail.errorInfo);
});

Unfortunately, the specific error, e.g. htmx:responseError is not present in this general event object.

Yes, it looks like it's impossible to programmatically figure out the exact error type on htmx:error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants