JuliaGizmos / WebIO.jl

A bridge between Julia and the Web.
https://juliagizmos.github.io/WebIO.jl/latest/
Other
228 stars 64 forks source link

Fate of Scope #230

Closed twavv closed 5 years ago

twavv commented 5 years ago

@shashi Continuing our conversation here about scopes specifically.

I believe but cannot prove that it makes life easier to have the concept of DOM separated from the concept of a "Component" (which is what Scope with all its undefined meaning provides).

I kind of agree but also, at this point, a scope is just a wrapper around a dom element. A component can just as easily be identified by the top-level dom element without a separate type.

You can store things in this inside onimport where this is a client-side mirror image of a "component". so I can say this.p5canvas = @new p5(f, "p5container");

We could also do this with something like a WebIO.Ref object.

p5canvas = WebIO.Ref()
onmount(my_div, @js do
    $p5canvas = @new p5(...);
end)

The complication with this is garbage collection, though, which I will admit is easier-done with the scopes. Though I do think having refs like this would be a good feature though (maybe we could tie them to the lifespan of a scope). This might be out of scope (ahahaha puns!) for this issue.

I do think that could be cool though.

w = Scope()
# Attach Ref to scope lifecycle for garbage collection.
p5canvas = WebIO.Ref(w)
# etc etc etc

# This can be not necessarily even in the same scope.
display(dom"button"("Re-render", events=Dict(onclick=(@js function ()
    $p5canvas.rerender()
end))))

The other way we could try to track lifecycles is some magic with @js_str but seems less than ideal.

Observables are associated with a scope (this needs to be better defined when an observable is used in multiple places though), so you can access the mirror image this.p5canvas in a handler for the observable (i.e. in onjs example). It's not obvious what would substitute this in your proposal.

I'm not really sure what you're saying. I think dissociating scopes and observables would overall be a good thing, especially to reduce the complexity of synchronizing between different scopes.

We can also take the path of defining Scope more clearly.

I'd be happy with this route. It seems like we'd want something like WebIOWidget <: AbstractWidget but I think @piever is holding Widget close to their chest.

shashi commented 5 years ago

Ok, I think we figured out the fundamental purpose of Scope from this discussion:

it's a carrier for the handlers for observables. Without an observable being associated with a scope, you have to have onjs(ob, jsexpr) do one of these two weird things:

  1. It needs to find the window of the browser and insert itself to a global dictionary (Observable => vector of handlers)
  2. It needs to encode the handler in its JSON representation, but then this is bad because it would be printed everytime ob is interpolated in a @js

As long as we don't want both of these, we need it to be associated with a "thing" so that we can serialize the handler when we serialize the "thing", and set it as a handler when we "render" the thing. This "thing" is scope.

piever commented 5 years ago

I'd be happy with this route. It seems like we'd want something like WebIOWidget <: AbstractWidget but I think @piever is holding Widget close to their chest.

The expression definitely made me smile :)

Somehow I like to think of AbstractWidget as something in general not related to web things but more of a general building block for GUIs: it is an ordered collection of components, with instructions on how to layout them and an output. The specific Widget implementation (even though it has scope as one of the fields as it made implementing Interact much easier) is in theory reasonably independent of WebIO. However if the new scope implementation has components, instruction on the layout of those components and an output it can definitely be a WebIOWidget <: AbstractWidget. When I wrote Widgets it was not the case so at the time I felt it was two distinct concepts.