Issue 1544 -- Building Widgets around existing Elements

3 views
Skip to first unread message

Joel Webber

unread,
Apr 15, 2008, 11:06:53 AM4/15/08
to Google Web Toolkit Contributors
All,

The issues brought up in 1544 have been bugging people for some time, and we've put off dealing with it because it's pretty thorny in the general case.

I just added the following comment to the issue:
A modest proposal: Rather than making it possible to simply pass an Element into Widgets' ctor's, add a specialized method for creating them from an existing element's id. 
The reason for this is that we have no guarantee that a raw Element object is attached to the DOM, which can lead to unpredictable behavior and memory leaks. If the interface requires that the Element be findable via $doc.getElementById(), then we *can* make this guarantee and avoid these issues altogether.

Basically, if we do this, the problem becomes much more easily tractable, with few if any error cases. Would this deal with the problems that everyone is having hooking into existing pages/forms on pages? Are there other (important) use-cases I may be missing?

Side note: I do realize that we still have an issue with missing Anchor, "raw" RadioButton, and "raw" CheckBox classes that form part of this problem as well. I'm about to enter a separate issue for these cases.

Thanks,
joel.

Scott Blum

unread,
Apr 15, 2008, 11:15:38 AM4/15/08
to Google-Web-Tool...@googlegroups.com
SGTM.  I think textarea, button, and div-as-panel would cover a lot of cases anyway.

Tree might be tricky. :P

Joel Webber

unread,
Apr 15, 2008, 11:16:58 AM4/15/08
to Google Web Toolkit Contributors
One more thing: I just entered issue 2289 to deal with the missing "raw" widgets. If anyone can think of cases I may have missed (i.e. other than RadioButton, CheckBox, and Hyperlink), please let me know.

On Tue, Apr 15, 2008 at 11:06 AM, Joel Webber <j...@google.com> wrote:

Joel Webber

unread,
Apr 15, 2008, 11:17:57 AM4/15/08
to Google-Web-Tool...@googlegroups.com
Agreed -- the point would be to do this *only* for those Widgets that naturally wrap a single element. Things like Tree, TabPanel, and the like would be entirely inappropriate (we don't even guarantee that they generate the same HTML structure on all browsers!).

John Tamplin

unread,
Apr 15, 2008, 11:32:21 AM4/15/08
to Google-Web-Tool...@googlegroups.com
On Tue, Apr 15, 2008 at 11:06 AM, Joel Webber <j...@google.com> wrote:
Basically, if we do this, the problem becomes much more easily tractable, with few if any error cases. Would this deal with the problems that everyone is having hooking into existing pages/forms on pages? Are there other (important) use-cases I may be missing?

If you are sending down HTML from an existing server process it might be tricky to get IDs in it, but you should be able to add the IDs client-side after putting it in some innerHTML and before attaching it to GWT, right?

--
John A. Tamplin
Software Engineer, Google

Emily Crutcher

unread,
Apr 15, 2008, 11:32:08 AM4/15/08
to Google-Web-Tool...@googlegroups.com
It seems like we are removing the ability to subclass with this solution, which can be very useful at times. 


Can you explain why we need to know if the element is attached or not? As to avoid memory leaks we currently attach and detach when the Widget is removed from its parent, how would the new system work?



On Tue, Apr 15, 2008 at 11:06 AM, Joel Webber <j...@google.com> wrote:



--
"There are only 10 types of people in the world: Those who understand binary, and those who don't"

Joel Webber

unread,
Apr 15, 2008, 11:47:18 AM4/15/08
to Google-Web-Tool...@googlegroups.com
But you *can* put id's into your HTML, and it is pretty common practice to do so when you want to 'attach' DHTML-style behaviors to static HTML. And yes, you can allocate id's for HTML strings, create it using innerHTML, and as long as it's attached to the Document, this will work fine as well.

Joel Webber

unread,
Apr 15, 2008, 11:53:02 AM4/15/08
to Google-Web-Tool...@googlegroups.com
On Tue, Apr 15, 2008 at 11:32 AM, Emily Crutcher <e...@google.com> wrote:
It seems like we are removing the ability to subclass with this solution, which can be very useful at times. 

How so? If I subclass one of these widgets, I can still create my own ctors that delegate both cases (by-id or default) to the super ctors. The only thing we'd be doing is formalizing the already-true assumption that these widgets use a single element type and impose no extra structure. Am I missing something here?

Can you explain why we need to know if the element is attached or not? As to avoid memory leaks we currently attach and detach when the Widget is removed from its parent, how would the new system work?

If we know the element is attached, we can simply call onAttach() directly from the ctor. If we don't, then we'll simply have to *assume* that it's attached, and if it's wrong, then some things will simply not work for obscure and hard-to-debug reasons (e.g. CheckBox and Image behave differently when detached). We'll still have to do something to ensure that onDetach() is called when the page is unloaded, though.

Jason Essington

unread,
Apr 15, 2008, 12:46:07 PM4/15/08
to Google-Web-Tool...@googlegroups.com
Ids are a great way to find a single element on a page, but when you
want to attach a custom behavior to a whole class of elements, Ids
become less ideal.

so if you want to do validation on all input tags with a class name of
date, you'd first have to walk the DOM looking for the elements that
fit your criteria, then add IDs to them then search the DOM again for
those IDs.

It would be particularly handy if you could use the selectors api http://www.w3.org/TR/selectors-api/
in a factory method that would only walk the DOM once, and create
widgets with the found elements.

Collection<TextBox> dateBoxList = TextBox.selectAll("input.date");
// add validation listeners ...

With this solution, you are still guaranteeing that your elements are
attached to DOM, but at the same time you've gained quite a lot of
flexibility in identifying the elements you want to hijack.

there are already several implementations of the selectors api in
javascript, so it can't be that tough to create one for GWT. In fact,
Safari already includes a native implementation.

-jason

Emily Crutcher

unread,
Apr 15, 2008, 1:14:37 PM4/15/08
to Google-Web-Tool...@googlegroups.com
On Tue, Apr 15, 2008 at 11:53 AM, Joel Webber <j...@google.com> wrote:
On Tue, Apr 15, 2008 at 11:32 AM, Emily Crutcher <e...@google.com> wrote:
It seems like we are removing the ability to subclass with this solution, which can be very useful at times. 

How so? If I subclass one of these widgets, I can still create my own ctors that delegate both cases (by-id or default) to the super ctors. The only thing we'd be doing is formalizing the already-true assumption that these widgets use a single element type and impose no extra structure. Am I missing something here?

I think maybe I mis-understood as when you said:
    Rather than making it possible to simply pass an Element into Widgets' ctor's, add a specialized method for creating them from an existing element's id.

I interpreted "method" as a method rather than a constructor, if what you meant is add a specialized constructor for creating them from an existing element's id, I withdraw my objection.


Can you explain why we need to know if the element is attached or not? As to avoid memory leaks we currently attach and detach when the Widget is removed from its parent, how would the new system work?

If we know the element is attached, we can simply call onAttach() directly from the ctor. If we don't, then we'll simply have to *assume* that it's attached, and if it's wrong, then some things will simply not work for obscure and hard-to-debug reasons (e.g. CheckBox and Image behave differently when detached). We'll still have to do something to ensure that onDetach() is called when the page is unloaded, though.


It seems like using this algorithm once one of these widgets is introduced, there is no easy way to garbage collect its listener.  I'm worried we will end up with a protocol which introduce  memory leaks into our users programs without an easy way to back it out.






 

On Tue, Apr 15, 2008 at 11:06 AM, Joel Webber <j...@google.com> wrote:
All,

The issues brought up in 1544 have been bugging people for some time, and we've put off dealing with it because it's pretty thorny in the general case.

I just added the following comment to the issue:
A modest proposal: Rather than making it possible to simply pass an Element into Widgets' ctor's, add a specialized method for creating them from an existing element's id. 
The reason for this is that we have no guarantee that a raw Element object is attached to the DOM, which can lead to unpredictable behavior and memory leaks. If the interface requires that the Element be findable via $doc.getElementById(), then we *can* make this guarantee and avoid these issues altogether.

Basically, if we do this, the problem becomes much more easily tractable, with few if any error cases. Would this deal with the problems that everyone is having hooking into existing pages/forms on pages? Are there other (important) use-cases I may be missing?

Side note: I do realize that we still have an issue with missing Anchor, "raw" RadioButton, and "raw" CheckBox classes that form part of this problem as well. I'm about to enter a separate issue for these cases.

Thanks,
joel.





--
"There are only 10 types of people in the world: Those who understand binary, and those who don't"





Joel Webber

unread,
Apr 15, 2008, 2:25:58 PM4/15/08
to Google-Web-Tool...@googlegroups.com
On Tue, Apr 15, 2008 at 1:14 PM, Emily Crutcher <e...@google.com> wrote:


On Tue, Apr 15, 2008 at 11:53 AM, Joel Webber <j...@google.com> wrote:
On Tue, Apr 15, 2008 at 11:32 AM, Emily Crutcher <e...@google.com> wrote:
It seems like we are removing the ability to subclass with this solution, which can be very useful at times. 

How so? If I subclass one of these widgets, I can still create my own ctors that delegate both cases (by-id or default) to the super ctors. The only thing we'd be doing is formalizing the already-true assumption that these widgets use a single element type and impose no extra structure. Am I missing something here?

I think maybe I mis-understood as when you said:
    Rather than making it possible to simply pass an Element into Widgets' ctor's, add a specialized method for creating them from an existing element's id.

Sorry, I didn't make that clear (I got natural-language-method mixed up with java-language-method in my brain!).
 
I interpreted "method" as a method rather than a constructor, if what you meant is add a specialized constructor for creating them from an existing element's id, I withdraw my objection. 


Can you explain why we need to know if the element is attached or not? As to avoid memory leaks we currently attach and detach when the Widget is removed from its parent, how would the new system work?

If we know the element is attached, we can simply call onAttach() directly from the ctor. If we don't, then we'll simply have to *assume* that it's attached, and if it's wrong, then some things will simply not work for obscure and hard-to-debug reasons (e.g. CheckBox and Image behave differently when detached). We'll still have to do something to ensure that onDetach() is called when the page is unloaded, though.


It seems like using this algorithm once one of these widgets is introduced, there is no easy way to garbage collect its listener.  I'm worried we will end up with a protocol which introduce  memory leaks into our users programs without an easy way to back it out.


This is why I pointed out that we would need to be sure that onDetach() is called when the page is unloaded. This is all that is required  to make sure event handlers get cleaned up and memory leaks avoided. Also, I'm not really talking about adding new widgets here (except to the extent that we will need simpler checkbox and radiobutton widgets), but just simplifying an existing use pattern that many people are already hacking their way through currently (just being able to specify the elements that are used for form-type widgets). The current use patterns are *definitely* introducing leaks.

Ray Cromwell

unread,
Apr 15, 2008, 2:52:44 PM4/15/08
to Google-Web-Tool...@googlegroups.com

Joel Webber

unread,
Apr 15, 2008, 2:56:39 PM4/15/08
to Google-Web-Tool...@googlegroups.com
Exactly! Ray, I'll let you solve the really hard problems, and I'll just concentrate on clearing roadblocks in the widget library :)

Peter Blazejewicz

unread,
Apr 15, 2008, 4:08:18 PM4/15/08
to Google Web Toolkit Contributors
hi Joel,
I've the exact problem:
I'm able to:
- get eleement(s) from existing html markup (no issues)
- ensure they are "attached" by calling "onAttach"
- use them as UIObject/Widget or concrete implementation (or as
elements)
I'm fail to:
- ensure that listeners are cleared
When I'm using my own implementation I'm forced to stop at point where
modifying (patching) containers is required because they adopt and
track children,

As to missing "raw" widgets:
#1
that could be limiting if we concentrate of existing GWT UI widgets
implementation. Most frequently I don't have to use GWT UI raw widget
class directly (e.g. say "Button" widget) because existing html markup
implements functionality of "button" but it is build differently:
anchor/img tags, div+backgroundImage, span+divs, anchor
+backgroundImage to name few implementation I frequently see
#2
I would really want is to be able to subclass concrete UIObject/Widget
without patching it (to access private only members/methods) that way
I could attach it to existing html markup and ALSO be able to remove/
detach such sniffed widget(s) to also sniffed implementations of Panel
class. For example DropList is build that way it cannot be sub-classed
because of access to member methods/fields implementation: I cannot
call super.checkIndex(index) because that's privet method nor I can
implement it differently. I think that partially answer Emily question
about subclassing (though in indirect way): because existing markup
can be structured a little differently then that found in GWT UI
implementation a subtle changes should be implemented in subclass,

regards,
Peter

On Apr 15, 8:25 pm, "Joel Webber" <j...@google.com> wrote:
> On Tue, Apr 15, 2008 at 1:14 PM, Emily Crutcher <e...@google.com> wrote:
>
> > On Tue, Apr 15, 2008 at 11:53 AM, Joel Webber <j...@google.com> wrote:
>
> > > On Tue, Apr 15, 2008 at 11:32 AM, Emily Crutcher <e...@google.com> wrote:
>
> > > > It seems like we are removing the ability to subclass with this
> > > > solution, which can be very useful at times.
>
> > > How so? If I subclass one of these widgets, I can still create my own
> > > ctors that delegate both cases (by-id or default) to the super ctors. The
> > > only thing we'd be doing is formalizing the already-true assumption that
> > > these widgets use a single element type and impose no extra structure. Am I
> > > missing something here?
>
> > I think maybe I mis-understood as when you said:
> >     *Rather than making it possible to simply pass an Element into
> > Widgets' ctor's, add a specialized method for creating them from an existing
> > element's id.
> > *

Emily Crutcher

unread,
Apr 15, 2008, 5:30:32 PM4/15/08
to Google-Web-Tool...@googlegroups.com
This is why I pointed out that we would need to be sure that onDetach() is called when the page is unloaded. This is all that is required  to make sure event handlers get cleaned up and memory leaks avoided. Also, I'm not really talking about adding new widgets here (except to the extent that we will need simpler checkbox and radiobutton widgets), but just simplifying an existing use pattern that many people are already hacking their way through currently (just being able to specify the elements that are used for form-type widgets). The current use patterns are *definitely* introducing leaks.

What happens when your app would naturally update the widgets (For instance in a PagingGrid). When previously the widgets would be correctly cleared, won't they stay in memory forever if we attach an event listener on load?





 


Joel Webber

unread,
Apr 16, 2008, 1:50:50 PM4/16/08
to Google-Web-Tool...@googlegroups.com
On Tue, Apr 15, 2008 at 4:08 PM, Peter Blazejewicz <peter.bl...@gmail.com> wrote:

hi Joel,
I've the exact problem:
I'm able to:
- get eleement(s) from existing html markup (no issues)
- ensure they are "attached" by calling "onAttach"
- use them as UIObject/Widget or concrete implementation (or as
elements)
I'm fail to:
- ensure that listeners are cleared

This is basically what I'm proposing to simplify, and to ensure that it is always done correctly, event listeners are cleaned up, and the like.

When I'm using my own implementation I'm forced to stop at point where
modifying (patching) containers is required because they adopt and
track children,
As to missing "raw" widgets:
#1
that could be limiting if we concentrate of existing GWT UI widgets
implementation. Most frequently I don't have to use GWT UI raw widget
class directly (e.g. say "Button" widget) because existing html markup
implements functionality of "button" but it is build differently:
anchor/img tags, div+backgroundImage, span+divs, anchor
+backgroundImage to name few implementation I frequently see
#2
I would really want is to be able to subclass concrete UIObject/Widget
without patching it (to access private only members/methods) that way
I could attach it to existing html markup and ALSO be able to remove/
detach such sniffed widget(s) to also sniffed implementations of Panel
class. For example DropList is build that way it cannot be sub-classed
because of access to member methods/fields implementation: I cannot
call super.checkIndex(index) because that's privet method nor I can
implement it differently. I think that partially answer Emily question
about subclassing (though in indirect way): because existing markup
can be structured a little differently then that found in GWT UI
implementation a subtle changes should be implemented in subclass,


I'm very specifically *not* suggesting that we make it possible to wrap any HTML structure more complicated than a "naturally single-element" widget. These include:

FileUpload: <input type='file'>

Button: <button>/<input type='submit|reset'>

ListBox: <select>

TextBox: <input type='text'>

PasswordTextBox: <input type='password'>

Frame: <iframe>

Hidden: <input type='hidden'>

Image: <img>

Label: <div>/<span>

HTML: <div>/<span>

RawRadioButton (name open for debate): <input type='radio'>

RawCheckBox (ditto): <input type='checkbox'>

Link (ditto again): <a>


Note that there are no cases here where the HTML structure for the widget is debatable -- it's always exactly one element. The proposed change is also not intended to help anyone get access to provide members of superclasses. This is an entirely different problem (not to say it's not important at times, just different).

 

joel.

Joel Webber

unread,
Apr 16, 2008, 1:53:59 PM4/16/08
to Google-Web-Tool...@googlegroups.com
On Tue, Apr 15, 2008 at 5:30 PM, Emily Crutcher <e...@google.com> wrote:

 
This is why I pointed out that we would need to be sure that onDetach() is called when the page is unloaded. This is all that is required  to make sure event handlers get cleaned up and memory leaks avoided. Also, I'm not really talking about adding new widgets here (except to the extent that we will need simpler checkbox and radiobutton widgets), but just simplifying an existing use pattern that many people are already hacking their way through currently (just being able to specify the elements that are used for form-type widgets). The current use patterns are *definitely* introducing leaks.

What happens when your app would naturally update the widgets (For instance in a PagingGrid). When previously the widgets would be correctly cleared, won't they stay in memory forever if we attach an event listener on load?


Please see my previous post for the kinds of Widgets/elements we're actually talking about. Something like a PagingGrid would definitely *not* allow itself to be wrapped around existing static HTML. All Widgets constructed in the way that I'm proposing would effectively have no parent panel (they'd be more or less siblings to RootPanels). Thus it would not be possible to remove them from their parent panels. Even if one were to be removed from the document, it would still remain in the cleanup list -- but just wouldn't be cleaned up until page unload. This is ok though, because their number is bounded by the total number of static HTML elements pre-existing in the document.

Emily Crutcher

unread,
Apr 16, 2008, 2:03:56 PM4/16/08
to Google-Web-Tool...@googlegroups.com
I'm referring to widgets in a PagingGrid, not the paging grid itself.

So for example:
   Client gets server-side table contents.
   Client displays contents from server-side table.
   Client creates widgets from elements to make the server side contents respond to events.

In this case, it seems like we'd see the sort of memory leaks that tend to make everyone have to close and restart their web-mail programs once every couple of days.

Joel Webber

unread,
Apr 16, 2008, 3:40:45 PM4/16/08
to Google-Web-Tool...@googlegroups.com
On Wed, Apr 16, 2008 at 2:03 PM, Emily Crutcher <e...@google.com> wrote:
I'm referring to widgets in a PagingGrid, not the paging grid itself.

So for example:
   Client gets server-side table contents.
   Client displays contents from server-side table.
   Client creates widgets from elements to make the server side contents respond to events.

In this case, it seems like we'd see the sort of memory leaks that tend to make everyone have to close and restart their web-mail programs once every couple of days.

For this to happen, you'd have to
- start off with a statically-generated HTML table
- add widgets to individual sub-elements, and
- write extra (non-widget) code to replace the static table with a dynamic one.

I do recognize that this would cause a transient memory leak (i.e. it won't get cleaned up until the app unloads). However, it's pretty beyond-the-pale as a real use case (IMHO) -- and it will *always* be possible to leak memory if you try really hard at it :)

I'm more concerned about dealing with the (apparently quite common) case where people are simply trying to attach simple behaviors and validation logic to existing form elements. People are doing this whether we like it or not (often through a lot of hackery), and we can either provide them with the tools to do it correctly or let them continue to generate code of questionable correctness.

Emily Crutcher

unread,
Apr 16, 2008, 4:05:43 PM4/16/08
to Google-Web-Tool...@googlegroups.com
I've have some pretty sharp developers ask me for this exact use case, so I think you might end up a bit surprised, as users want to be able to quickly display large tables with updates and want a way to hook widgets up in this case, where using HTMLTable is simply not practical.    On the other hand, those same developers might prefer to have a memory leak then no way to do it at all...

     Cheers,

            Emily

Joel Webber

unread,
Apr 16, 2008, 4:14:55 PM4/16/08
to Google-Web-Tool...@googlegroups.com
I agree that some people want to be able to create tables starting with static HTML, and I'm not even saying that it's crazy to do so, given the right widget for the job (one that's capable of slurping up an existing table and providing a dynamic interface to it).

However, to take this some static form element inside this table (that you intend to dynamic-ify using said widget), wrap get it by id, wrap it in a widget, then blow it all away using the aforementioned table widget, is not what I'd call a common use case. For this to be a problem, you'd have to do all these things together.

Ray Cromwell

unread,
Apr 16, 2008, 4:27:10 PM4/16/08
to Google-Web-Tool...@googlegroups.com
I think ideally, they may want to write code like this:

$("table#myresults td").each(new Function() {
public void exec(Element e) {
$(e).wrap(Label.class).addClickListener(...);
}
});

:) The issue is how to do it, do it fast, and not leak memory. :)

-Ray

Ian Petersen

unread,
Apr 16, 2008, 4:32:43 PM4/16/08
to Google-Web-Tool...@googlegroups.com
On Wed, Apr 16, 2008 at 4:27 PM, Ray Cromwell <cromw...@gmail.com> wrote:
> I think ideally, they may want to write code like this:
>
> $("table#myresults td").each(new Function() {
> public void exec(Element e) {
> $(e).wrap(Label.class).addClickListener(...);
> }
> });

That is some sexy looking code! Too bad Java doesn't have closures....

By the way, could you make $(String) return an Iterable<Element> so
you could do this:

for (Element e : $("table#myresults td")) {
$(e).wrap(Label.class).addClickListener(...);
}

?

Ian

--
Tired of pop-ups, security holes, and spyware?
Try Firefox: http://www.getfirefox.com

Ray Cromwell

unread,
Apr 16, 2008, 5:07:39 PM4/16/08
to Google-Web-Tool...@googlegroups.com
I suppose I could. The jQuery pattern is a chaining pattern in the
sense that most methods return another jQuery object, I could make
GQuery implements Iterable<Element>, the issue might be that the
generated code with Java's for-each on an Iterator might not be as
tight as a regular for-loop that is used in the each() method. But
what the hey, support both methods I say, provide both external
(Iterable) and internal (each) iteration models.

Thanks for the idea. How many people are interested in firming up all
the missing methods in GQuery and adding some comparable plugins once
I release it? :) I'm kinda busy, so I'm only going to take it as far
as CSS3 selector support and JQuery core.

-Ray

Reply all
Reply to author
Forward
0 new messages