Open rickmcgeer opened 5 months ago
This architecture (despite years of programming with it) is still not clear to me, and even if (as I suspect is the case) there is a clear architecture underneath, it isn't well-enough explained or articulated. I may not be the quickest or most acute study, but I believe I'm not the slowest, either, so if I'm having problems others will, too.
The architecture of bindings is still not final and changing, so we are thankful for any feedback like this one. Now we are currently in the process of relaunching the lively.next website, and it will come with comprehensive explanations with regards to view models, bindings, the IDE and other stuff. So please be patient.
In
DoubleSliderWithValuesModel
, usingmodel
in the binding forrangeChanged
didn't work, buttarget
did. Should I have used the model name andmodel
? If so, what name should I use (since binding is by name, not object) As I understandbindings
, the fields are: --model
: model of the object generating the event --target
: name of the object generating the event --handler
: function to call to handle the event Is this correct?
While this is correct, we have recently decided to deprecate the model and will soon drop support for it. The reason is that binding to models causes extremely confusing scenarios like the one you entered. I suppose the reason here was, that the signal itself was triggered on the view (and therefore target) and not on the model itself. I also do not want to rule out typos that can happen easily, since this is a soft binding and will not throw errors if you are defining bindings based on nonexistent method or signal names.
Since
bindings
is a static property, how does one handle the case where there are multiple items which generate events which are added dynamically? A good example of this is a non-static list.
Good question. While the property itself is static, the underlying connections are dynamically instantiated and also updated if submorph structure changes. If you want to define a binding for multiple morphs, you can use regular expressions for the name of the target which will apply the binding to all morphs where the name matches the regular expression. For instance if I had multiple morphs which all should receive a certain binding, I would 1.) ensure that the names follow a certain scheme like: "item 1", "item 2", "item 3" ...
2.) define the binding like this: { target: /item/, signal: 'position', handler: 'reactToMove' }
.
Similarly, how does one dynamically connect to an event (e.g., list selection)?
Not entirely sure what you mean by that, but since the bindings are dynamically updated this should be covered?
About the rest of your proposal, this seems more like a revision to lively.bindings
? So you would revise lively.bindings
in a way where the information about signals is explicitly modeled via objects instead of the implicit signal.
We can certainly think about that, but to me that is a separte issue to the bindings problem earlier in the ticket.
Nice thing about this is:
Bad things:
connect
can also connect to method calls that where not specially prepared in that way. signal()
and connect()
.first, thanks for the detailed and thoughtful response. It looks like I'll be changing my code yet again :-). (This is really no problem as long as I understand the architecture!)
I also do not want to rule out typos that can happen easily, since this is a soft binding and will not throw errors if you are defining bindings based on nonexistent method or signal names.
Right, that's possible (and it's very hard to debug!). This is why bindings based on object references are more robust, particularly when there is a discovery mechanism (e.g., the subscribers
property I proposed). The graphic connection lines from the previous sidebar were an enormous help.
In general, binding by name in all circumstances is challenging for debugging, because the name resolution happens through an opaque process at runtime. I appreciate it also adds dynamism, because changing a name changes a target
Not entirely sure what you mean by that, but since the bindings are dynamically updated this should be covered?
yes, bindings updated dynamically covers this.
About the rest of your proposal, this seems more like a revision to lively.bindings?
No, the pub/sub architecture isn't a revision to lively.bindings
, or to any existing Lively code! It is an overlying convenience architecture on bindings/signals/connections, which would be implemented using the existing architecture (as the code snippets I put in were). In fact, Lively already uses a stub of this architecture in Button
, where there is an action
property; where the action
is non-null and a function, it's called simultaneously with the issuance of the fire
signal.
The generalizations to action
are:
But notice that action
doesn't change signal()
and connect()
, and the signal/connect
interface is available to programmers. I'll give you another "bad thing" about the pub/sub architecture I proposed: it isn't as general and flexible as signal()
and connect()
, and sometimes that flexibility and generality is required. The nice thing is that we can have both, and the pub/sub architecture provides:
What would you like to achieve? This architecture (despite years of programming with it) is still not clear to me, and even if (as I suspect is the case) there is a clear architecture underneath, it isn't well-enough explained or articulated. I may not be the quickest or most acute study, but I believe I'm not the slowest, either, so if I'm having problems others will, too.
I just spent a couple of days debugging signals not being propagated from
to
After much experimentation, it turned out that the fix was to add the
rangeChanged
event to theexpose
list inDoubleSliderModel
.But this still left me with questions:
DoubleSliderWithValuesModel
, usingmodel
in the binding forrangeChanged
didn't work, buttarget
did. Should I have used the model name andmodel
? If so, what name should I use (since binding is by name, not object)bindings
, the fields are: --model
: model of the object generating the event --target
: name of the object generating the event --handler
: function to call to handle the event Is this correct?bindings
is a static property, how does one handle the case where there are multiple items which generate events which are added dynamically? A good example of this is a non-static list.How are you trying to achieve that The inputs and helpers classes in https://github.com/engageLively/galyleo-dashboard/tree/main/studio try basically all of the above, as well as
connect
(and I don't understand that one well, either). They're something of a mess; in part, that's me, but in part it's also due to the fact that the architecture here is really unclear to me. Filters, for example, don't use the view/model architecture but use Morphs, and so they useconnect
a lot. See:Alternative solutions This is primarily a plea for documentation/examples/cookbook, and a lot of those are already in Lively. What we (principally @merryman) did for Galyleo was to take the studio components from lively.ide and adapt them. This actually gives a pretty good start. Here is what I'd recommend:
Here's one idea: Each UI element offers a set of events. An
event
is an object which offers the following methods:subscribe(handler)
: Ahandler
is an object with ahandleEvent(obj)
method.handler.handleEvent(event)
gets called on each new eventunsubscribe(handler)
: removehandler
from the list of handler objects for this eventsubscribers
: a read-only property (noset
method) for the subscriber listnotifySubscribers(obj)
: callhandler.handleEvent(obj)
for each handler onsubscribers
The
obj
argument tohandleEvent
is event-specific; it captures data specific to the event. The existingevt
construct, which we use for mouse events, might be re-used for this (in general, I'd like to add as little new as possible) Implementing this on top of the existingsignal
architecture gives the following notional methods, based on aLivelyEvent
class with the methods given above.A simple example of this is given with the current
action
property ofButtonModel
, and we have this method:(see
Except with events,
action
is replaced with a list of subscribersThe defensive code is gone. Checking if there's really an
action
is gone, because the event and its associated subscriber list was done oninit
, and logging an exception in thehandler.handleEvent
code is done innotifySubscribers
Additional Resources The Galyleo code could be a guinea pig for this. I'd do most of the implementation but I'd like to work with @linusha and @merryman as well, since they understand what exists much better and also for figuring out how we could turn this into a reusable toolkit
Version: Please paste the lively.next commit on which the problem occurred here (use the copy button of the Version Checker in the bottom left corner). 130b0864109f7f2ba2930ca96821a30df8066c28