-
-
Notifications
You must be signed in to change notification settings - Fork 36
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
Proliferation of redundant containers #283
Comments
I would add to the above, it is not just that example where something specifically breaks; I looked at the dom produced and it is littered with containers upon containers, divs with nothing in them, or nothing but a style and a script, or a span, nested deep inside what should have been user dom. |
It would be great to have this more predictable ;) |
Great! i poked around in the code a bit and at least from the julia side roughly understand what is happening. As a first step i would replace the span with a div in the generic observable jsrender, and add an explicit class to any utility dom that is inserted from the julia side. Im not sure the entire process is clear to me though, esp from the js side where i had less opportunity to look at the code. as far as i understand at a high level, and please correct me - the basic flow is that wherever there is some observable depending leaf in the dom tree generated by julia, a container C with a piece of js code is inserted instead, that pulls a binary blob with some dom that replaces the contents of C, and sets up a subscription to further updates. Then it seems like what happens is that julia creates a special container to be rendered in a “subsession”, which - im guessing a bit here since that would explain the behavior - sets up a temporary container into which it injects the html to be rendered, then depopulates it and sends it back to julia, which sends it back where it is inserted into the “main” subcontainer, with a subscription to repeat this process whenever the observable changes on the julia side. Is that the actual cause of the grandparent “bonito fragment”, parent and uncle “data-jscall-id” containers with empty uncle, child as the ided root container created in the beginning of the observable jsrender method, and grandchild finally the contents? As noted in MakieOrg/Makie.jl#1318 I found kind of a workaround. That essentially forced one to use in a mode where there is a single top container managing everything instead of each piece having its own connection etc. This is mostly a usage pattern change so that wouldn’t be that much to do in terms of code changes, though i still would recommend better defaults per above. What do you think of such a usage pattern? I can see there is potential for poor performance in redrawing. How do you think other interactions would be affected? |
I hacked around something like this, for a generic container that updates on its contents and a generic input element. Seem to work reasonably well. What do you think? struct View{T}
dom::T
id::String
args::Observable{Any}
end
function view(dom,args...;kwargs...)
ID=:id in keys(kwargs) ? kwargs.id : string(uuid4())
View(dom,ID,Observable{Any}((args,kwargs)))
end
function set!(d::View,args...;kwargs...)
d.args[]=(args,kwargs)
end
function Bonito.jsrender(session::Session,d::View)
ID=d.id
node=@lift d.dom($(d.args)[1]...;id=ID,class="view",$(d.args)[2]...)
onjs(session,node,js"""(v)=>{
el=document.getElementById($ID);
console.log("Updating:",el,v);
el.replaceWith(v);
}""")
return Bonito.jsrender(session, node[])
end
struct Value{T}
dom::T
id::String
args
value::Observable{Any}
_value::Observable{Any}
end
function value(dom,val,args...;kwargs...)
ID=:id in keys(kwargs) ? kwargs.id : string(uuid4())
value=Observable{Any}(val)
_value=Observable{Any}(val)
v=Value(dom,ID,(args,kwargs),_value,value)
end
function set!(d::Value,val,args...;kwargs...)
d.args=(args,kwargs)
d._value[]=val
end
Bonito.is_widget(::Value) = true
Bonito.update_value!(v::Value, val) = (@info "updating" v.value[] val; v._value[] = val)
function Bonito.jsrender(session::Session,v::Value)
ID=v.id
node=v.dom(v.args[1]...,
;id=ID
,class="value"
,value=v.value[]
,v.args[2]...
)
t=string(now())
attached=get!(session.session_objects,ID,false)
Bonito.onload(session,node,js"""function onload(nd) {
el=document.getElementById($ID);
console.log($t,'Adding listeners to',el,el?.listening);
// Shit seems to run twice
if (!el) {
console.log("hasn't loaded yet");
return;
}
if (el?.listening) {
console.log("already listening");
return;
}
el.addEventListener('input', function (evt) {
val=this.value;
console.log("Value Input:", el, evt, val);
$(v.value).notify(val);
});
el.addEventListener('change', function (evt) {
el=document.getElementById($ID);
val=this.value;
console.log("Value change:", el, evt, val);
$(v.value).notify(val);
});
el.listening=true;
$(v.value).on(function set_value(val) {
el=document.getElementById($ID);
console.log("Got a value update:",el,val, $t);
el.value=val;
});
}
""")
return Bonito.jsrender(session, node)
end |
Hello,
I recently discovered bonito and have been really enjoying the general approach. However;
When playing around with Bonito, especially considering it for various projects, I'm especially wary of past experiences with various frameworks in regard to visual layout, and particularly in browsers, where it was always a hot mess. Considering myself a non-expert in layout design, I still think it is possible to get reasonably precise layout fairly easily for a range of use cases in many scenarios, if whatever UI kit I'm using does not make it cumbersome.
In this regard, the primary hindrance IMHO to simple design is unexpected containers, especially anonymous classless ones, since every additional container is an opportunity for styling to break. This especially when mixing in other framework that rely on specific assumptions regarding their layout, and in particular to anything that uses Canvas such as WGLMakie (but likewise for Plotly and others) where sizing to parent container can break.
As a simple example, consider
In my browser this does not resize to the full height of the page, despite my expectations that it should in all three cases. This may be a bit contrived since supposedly one could/should make a specific type with overloaded
jsrender
to wrap the observable. However, if even frameworks that are deeply integrated with Bonito can't do this right, this is a bad sign.I propose to reduce to a bare minimum any containers that Bonito creates beyond those that the user explicitly specifies (specifically in regards to Observables, but any other types that do not have an explicit jsrender methos). For those that are absolutely necessary, i propose to make them classed for simple styling, and to explicitly enumerate cases where they appear to reduce the element of surprise. I would happily participate in making the changes, as I really like Bonito otherwise and would find it useful - but I might need some guidance.
Is there support for this?
The text was updated successfully, but these errors were encountered: