bucklescript-tea icon indicating copy to clipboard operation
bucklescript-tea copied to clipboard

request: support for ocaml-vdom's ui api

Open martindemello opened this issue 6 years ago • 3 comments

Lexifi's ocaml-vdom has a nice API for declaring UIs, similar to elm's but nicer in a bunch of small ways. It'd be great to have that as an alternative API for bucklescript-tea, especially since that would let people trivially experiment with both frameworks while sharing UI code.

(I might have a go at doing this myself, if I get the time, but I figured I'd put it up here first to see if people were receptive to the idea, and if someone with more bandwidth wants to get to it first :))

martindemello avatar Oct 04 '17 16:10 martindemello

A few things I notice about it:

Each node in the VDOM tree has a "key" string field. By default, the key corresponds to the tag name for elements but it can be overriden. The key is used by the synchronization algorithm as follows: when synchronizing the old and new children of an element, the children are first grouped by key. Two children with different keys are never synchronized, and the sequence of old and new children with a given key are synchronized in a pairwise way (first old child with key K against first new child with key K; etc...), adding or removing extra/missing children if needed. Children are also reordered in the DOM, if needed, to match the new ordering.

Similar though I have the name and key be separate fields, along with a unique key for fast swaps (and my key is more like a 'quick skip' for efficiency).

Event handlers are not attached on DOM nodes created when a VDOM tree is rendered. Instead, we attach fixed event handlers on the root container, and rely on event delegation. The handler corresponding to a given element and responsible for a given kind of event is searched directly in the VDOM. The rationale for this design choice is that comparing functional values is not well-defined in OCaml, so it would not be clear, when the "old" and "new" VDOMs are diffed, if the event handler on the DOM node should be refreshed.

Egads this makes events in the DOM slow!!! Plus not all bubble, especially when webcomponents come in the picture! How could they ever think this would work well?! o.O?!

A "bridge" structure in created in Vdom_blit to represent the correspondence between VDOM and DOM nodes. This structure mimics the shape of both trees and avoids having to query the concrete DOM to navigate in the tree.

This is still slower than what I do where I compare an old and new vdom, without needing to query the physical dom (only walk children linearly, which is very quick, never even needing to access properties, and even this is not absolutely necessary). I have a plan to speed this up even more as well, but what that describe sounds rather inefficient (though need to look at the code to confirm)...

No data structure is created to represent the "diff" between old and new VDOMs. Instead, the synchronization algorithm detects VDOM changes and apply them on the fly to the corresponding DOM node.

Likewise, I just patch from one to the other in real-time (though making a patch structure could be very useful for server<->client communication, I'm considering that as an option later). Also how does it 'detects VDOM changes' without walking the VDOM's anyway (like I'm doing) unless it is not an immutable VDOM (as mine is so that time-travel debugging will be possible later).

There is some special support for the "value" property. When this property is explicitly bound in the VDOM (typically on an input field), the value is forced on the element: whenever the DOM value changes, the event is potentially dispatched to an event handler, and the new VDOM property is forced on the DOM element. In particular, if the internal state is not updated by the event handler, the field becomes in practice read-only.

This will work on things like a textbox, but it will fail pretty hard on DOM Custom Elements. You cannot make such assumptions about nodes without knowing specifically what a node is (like making a pure textbox or so node, but even that could become a custom element via an is= declaration, so you still cannot make assumptions).

Some special VDOM node attributes are provided to present "superficial state changes" that are not reflected in the proper functional state (currently: giving focus to an element, or ensuring an element is visible by y-scrolling its parent). These attributes produce the corresponding DOM action when they are first put on an element (which is not completely well-defined, since this depends on the synchronization algorithm).

I've had ideas for things like this to perform some update on something once when it's key changes (or it starts existing), though Cmd's tend to be the proper way to perform 'effects', hence why I've been hesitant (plus it would not follow the Elm api at all).

As for an alternative API, I'm unsure what it brings from a cursory search in it. What features does it have that the VDOM here does not include (or is planned soon like managed elements, though I'm not seeing that it has that capability either on a first glance)? Changing the UI code would break the ELM API further as well (though it is an option for when I make an extended version, though I plan even more different things than what it does so as to gain a good bit of efficiency that the Elm API has built in as a needless cost).

In essence though, what does it bring that this version does not already contain?

OvermindDL1 avatar Oct 04 '17 17:10 OvermindDL1

I was talking more about the superficial aspects than the engine. The thing I really want is labels rather than positional arguments when constructing html elements, the rest is a bunch of small things that are probably just a matter of my being more used to reading js-of-ocaml code, but I figured if you were going to add an alternative Html module anyway it might as well be consistent with vdom. (I definitely didn't mean to change the UI code, just add an alternative frontend to it, so to speak).

martindemello avatar Oct 04 '17 17:10 martindemello

I was talking more about the superficial aspects than the engine. The thing I really want is labels rather than positional arguments when constructing html elements, the rest is a bunch of small things that are probably just a matter of my being more used to reading js-of-ocaml code, but I figured if you were going to add an alternative Html module anyway it might as well be consistent with vdom. (I definitely didn't mean to change the UI code, just add an alternative frontend to it, so to speak).

Can you give an example of the labels? Currently the Elm API does not use or even have such a concept so I will not be able to use them in the base set but can always use them in extended options that have proper fallbacks.

Changing out the front-end engine with it seems to be a lot more inefficient, and I'm not sure it is fully immutable based on the descriptions (need to read through the code though) and it seems to be lacking features that mine has so it would not be a transparent change. Also it seems to be built for js_of_ocaml rather than bucklescript, and sadly those two have very different foreign interfaces so I'm not sure how a drop-in change would work... :-/

OvermindDL1 avatar Oct 04 '17 18:10 OvermindDL1