jorgebucaran/hyperapp

Emitting Custom Elements, Hyperapp appears to entirely discard on* attributes?

hmans opened this issue · 5 comments

hmans commented

Codesandbox for reference:
https://codesandbox.io/s/three-elements-hyperapp-rxhei?file=/index.html

First of all, I'm not talking about writing Custom Elements that are implemented in Hyperapp, but Hyperapp apps that emit Custom Elements -- see the sandbox linked above, where I'm combining Hyperapp with three-elements (a library that provides a Custom Elements-based declarative interface for Three.js.)

I'm emitting a <three-mesh> tag which can have an onupdate attribute. three-elements is designed to both accept this as an attribute (a string representation of a function) or a property on the Element object itself (where you would typically just directly assign a function). Neither appear to be working from within HyperApp, and it does indeed look like it is entirely discarding the onupdate attribute.

I'm not entirely sure what the heuristics at work are here, neither am I terribly familiar with Hyperapp -- does it implement its own synthetic event system? Does it generally not forward attributes that start with "on*" to the emitted DOM?

Any insights would be highly appreciated. Thanks! ❤️

PS.: Maybe Hyperapp could have a section on https://custom-elements-everywhere.com/?

@hmans I checked out your example and, while I did not entirely understand what is supposed to happen (so I couldn't make it work) it seems like you are unaware of or misunderstood some hyperapp fundamentals. I'll try my best to give brief explanation as it applies to your questions.

Hyperapp treats all "onxyz" properties of nodes in the same way. It doesn't matter if they are custom elements or regular native elements. Hyperapp is a virtual dom engine and state-management engine (similar in concept, but dissimilar in implementation, to Redux) wired_together via the event handlers of the dom.

This means that any element that gets created (or as you say "emitted") using the hyperapp.h function should have any onwhatever properties assigned to "actions". Actions are functions of the form (most typically): (currentState, eventObject) => nextState.

Hyperapp uses addEventListener on the elements it creates to bind dispatch(action, eventObject) to the event under the hood. When a user interacts with the element, the action is dispatched which means: 1) the next state is calculated using the given action, 2) the new view is calculated for the new state, in the form of a virtual dom, and 3) The real dom is patched to match the new vdom. This is how hyperapp closes the ui-loop.

The above should answer most of your questions about why your example isn't working.

Why is onupdate "swallowed" ? It isn't actually: it's there but you can't tell by inspecting the element tree in dev tools for example, because we are using addEventListener it does not appear as an attribute.

Why isn't it working? Well like I said above, whatever you assign to onupdate will be passed to hyperapp's internal dispatch function, so for example if you assign it to a string, you will be calling dispatch("(dt)=> this.object.rotation....", event). The effect of that will be to simply assign the app's new state to: "(dt) => this.object.rotation..." and cause a rerender. After the first time that happens, the state will be the same and that means no more patching of the dom will occur.

Furthermore actions won't have the correct this available inside them anyway. You need to use eventObject.target. Even then, in principle actions should be pure functions so you should not mutate dom elements directly in actions. There are other ways to do that.

To that point: I am not sure when you expect onupdate to be triggered. I am guessing maybe somehow the parent-component <three-game> is doing it? At least it has a property autorender: true which tells me that the <three-game> component expects to take over managing & rendering of the children, which will conflict with the app.

If you want to design a system where an element manages the updating of child elements, you should not be declaring the child elements as part of the main view (as you are doing) because the two engines will conflict with eachother.

hmans commented

For context, three-elements is a library that exposes a set of Custom Elements which wrap Three.js primitives. The "rendering" it does -- since you mentioned autorender -- is not related to DOM functionality. Elements created by it expose onupdate as both a property and an attribute, and it allows the user of the library to hook into the library's per-frame ticker code. None of this is related to DOM input events.

All in all, I assumed something like what you described was happening, with me simply running into HyperApp's own handling of events. I was merely surprised that it was happening on an attribute called onupdate (which does not map to a standard DOM event attribute), and I was merely wondering if HyperApp simply assumes that all attributes that start with on are to be considered event handlers, or if onupdate itself had a specific meaning in the framework.

Thanks for clearing it up, I'll update the caveat listed in three-elements' README accordingly.

Having hyperapp represented on custom-elements-everywhere would be a great idea! It was talked about before, but Hyperapp was in an early stage with a changing api so I think the consensus was to hold off for a bit, until it is officially released – which still hasn't happened, it is still considered "In beta".

But the API is pretty stable now so maybe it can't hurt to start looking at implementing those tests even if they probably won't be submitted until Hyperapp is done. Might be a fun project :)

I was merely wondering if HyperApp simply assumes that all attributes that start with on are to be considered event handlers

Yup you are spot on. That's exactly how it works :)

@hmans I made a fork of custom elements everywhere and added hyperapp tests. (https://github.com/zaceno/custom-elements-everywhere/tree/hyperapp) Haven't submitted the PR yet so it's not reviewed but the good news is that Hyperapp has 100% score, passes all tests.