RR: Data aware widgets

163 views
Skip to first unread message

Emily Crutcher

unread,
Dec 28, 2006, 11:50:35 AM12/28/06
to Google Web Toolkit Contributors

One of the tougher aspects of programming a well functioning AJAX application is the asynchronous population of data into the application. The problem is that loading all data at initialization leads to poor performance due to long start-up times, while trying to juggle the population of the widgets over time can be complex.    Detailed below is one possible solution.

 

The questions I want the most feedback on are

 

1) Is it worth doing?

Will we get enough bang for the buck by providing data widgets to justify the hassle of creating and maintaining another API?

 

2) Is there a better approach?

Below we propose a single solution to the problem. Are there better solutions out there?  Are there server-side frameworks that should be kept in mind while designing our client-side solution?

 

---------------Design Goals----------------

 

Server agnostic

The widgets themselves should not care about the actual server technology or how it represents its data.

 

Seamless user experience

The UI should be responsive even while data is being requested from the server.

 

Easy to use

It should be relatively easy for a user to figure out how to use a data enabled widget.

 

Resuable Components

As the data management code will be somewhat complex ideally the same code could be reused by all the data enabled widgets.   

 

 ---------------Design Overview---------------------

 

Each data-aware widget must generate unique data requests that are handed to a data broker for processing.

 

The data-aware widgets are responsible to creating the data request and processing the response objects associated with that data request.

 

The data broker is responsible for batching, fetching, creating a response object, and caching data requests.

 

To use a data-aware widget, you must supply it an object conforming to the data broker interface, detailed below.   For convenience, GWT will supply an abstract data broker which will manage caching and batching of requests, leaving only the actual server communication for user implementation.

 

 

 

 

------------Proposed Widget List----------------

 

Data Tree:

A tree populated via data from a data broker.

 

Data Table:

A scrollable table populated via data from a data broker.

 

Data ListBox:

Creates a list of options via data from a data broker.

 

Suggest Popup:

A selected suggest popup where suggestions are populated from the server based on text strings.

 

Suggest Box:

Composite widget using the suggest popup and a single text box to create a Google style suggest box.

 

 

 

-------------- Proposed Data API ----------------

 

 

Data Broker:

Each Data widget is associated with a single Data Broker. The Data broker is responsible for gathering and processing data requests and using the returned information to populate data widgets.   It knows how to take a string payload from the server and translate it into a response object. 

 

The Data Broker is data-agnostic. It should only be able to communicate with the server, cache data responses from the server, and re-assemble server side response.  It may use whatever technology is most convenient for the user, RPC, JSON, HTTPRequests, etc.   Only the server response and the request object should be aware of the underlying data.

 

It is the also the responsibility of the Data Broker to batch requests from the client, and cache the responses, in order to reduce the number of round trips required.  

 

  

/**

  * DataBroker interface used by data- aware widgets.

  */

public interface DataBroker {

  /**

   * Request object to be sent to the data broker.

   */

  interface Request {

    /**

     * Fills the current widget with the data from a response. Each request may

     * have multiple response objects associated with it.

     */

    void fill(Response response);

 

    /**

     * Request data.

     */

    String getRequestData();

  }

 

  /**

   * Response object to be given to the <code>fill </code> method of the

   * request object.

   */

  interface Response {

    /**

     * Gets the index of this response. For example, if this is the third

     * response to a query, then the index would be 2.

     */

    int getIndex();

 

    List getValues();

  }

 

  /**

   * Registers a <code>Request</code> object with this data broker.

   */

  void get(Request q);

}

 

 

 

 

DataBroker.Request:

The request object is sent to the data broker by the data widget. The request should have a widget specific fill method. Bellow is a sample request that might be used to fill a data table row.

 

/**

  * Data aware table

  */

public class DataTable extends FlexTable {

 

  class RowRequest implements Request {

    int row ;

    int col ;

 

    RowRequest( int row) {

      this.row = row;

    }

 

    public void fill(Response results) {

      List values = results.getValues ();

      int end = col + values.size();

      for (int i = col; i < end; i++) {

        String value = (String) values.get(i);

        setHTML( row, i, value);

      }

      col = end;

    }

 

    public String getRequestData() {

      return DataTable. this.getName() + ".Row." +Integer.toString(row );

    }

  }

 

Data Response:

The response object is created by the data broker to be passed into the fill method of the request object. Each request may have multiple response objects returned by the server. It is the responsibility of the data broker to ensure that the fill method is called sequentially on all returned responses.   Each response object contains a list of values and an index. So, for example, if a request generates four response objects, then the response objects will have indexes 1 through 4.

 

 

 

Typical Control flow for a single data request:

 

Data-aware widget:

    Creates Request object.

    Passes Request object to DataBroker.

 

DataBroker:

    Collates Request objects together.

    Sends Requests to server.

    Gets results from server.

    Creates Result objects from server response.

    Pass Results objects to Request object in index order.

 

Request

   Uses Results object to fill data in data-aware widget

 

 

P.S.

  Thanks to Henry and Dan for all their help on this!

 



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

Miroslav Pokorny

unread,
Dec 28, 2006, 5:58:56 PM12/28/06
to Google-Web-Tool...@googlegroups.com
Class design
Wouldnt it be better not to extend but rather use composition. There is no reason to couple a DataTable with a FlexTable. Composition will work just as well and allows flexibility in allowing one to substitute a different table impl in the future. Eg by using inheritence one is forced to use a FlexTable when other table widgets may come along eg a sortable table, a table where one can reorder columns, a scrollable table etc. Im pretty sure there will be more table variations in the future.
--
mP

Scott Stirling

unread,
Dec 28, 2006, 7:54:44 PM12/28/06
to Google-Web-Tool...@googlegroups.com
It'd be nice if there was not a proliferation of widgets for this and
that. I am sometimes miffed at the parsimonious nature of GWT's
out-of-the-box widgets. Sorting and data binding could be core
features as far as I am concerned. The Java->Javascript compiler
could, in theory, elide or optimize unused code/features to no-ops at
compile-time.

Scott Stirling
Framingham, MA

Miroslav Pokorny

unread,
Dec 29, 2006, 1:29:49 AM12/29/06
to Google-Web-Tool...@googlegroups.com
I wouldnt agree. Its better that you have small blocks which you can assemble in whatever way you want rather than just having once choice - ie one big monolithic thing that only works one way. After all thats the point of interfaces.

Emily Crutcher

unread,
Dec 29, 2006, 7:38:55 AM12/29/06
to Google-Web-Tool...@googlegroups.com
Yes, you are correct we definitely would want to make it easy to add and extend. I should have made it more clear that the sample row request was not meant to indicate a real data table design, rather just a sample of how a request object should look.

The exact details of how data table would be structured would be worked out after the overall data-aware widget design is settled,  but the top two design goals would be
   a) Ease of use
   b) Ease of extension

 
Thanks for the feedback! As including that sample in the way I did was definitely confusing. I'd also love feedback on the big question of "is it worth doing?" As that is the one I am most concerned with right now.






On 12/28/06, Miroslav Pokorny <miroslav...@gmail.com> wrote:

Scott Stirling

unread,
Dec 29, 2006, 10:34:35 AM12/29/06
to Google-Web-Tool...@googlegroups.com
I like the choice (so far) by the GWT authors to encapsulate
cross-browser Javascript subtleties and idiosyncracies into the core
widgets. Miroslav, I think the logic of your argument would support
breaking out cross-browser functionality into browser-specific
widgets, which is a step backward. The small blocks I want to
assemble in GWT are things like Button, Flextable/Grid, Label, etc.

Put another way, there should be a core set of widgets in GWT that a
developer can use to build an average e-commerce app, with an eye
toward the basic fact that GWT is a way for me to code Javascript and
DOM in Java. There's no pretense of preserving interfaces post-compile
time.

Let me reiterate my point: I don't want a proliferation of GWT widgets
for every little feature. I want the core widgets to be powerful out
of the box. Sorting and data binding seem like reasonable core
features to me. I'm not looking to build a business coding GWT
widgets. I want to be able to build a decent web app with GWT alone.

Scott Stirling
Framingham, MA

Konstantin.Scheglov

unread,
Dec 29, 2006, 12:00:24 PM12/29/06
to Google Web Toolkit Contributors

> Let me reiterate my point: I don't want a proliferation of GWT widgets
> for every little feature. I want the core widgets to be powerful out
> of the box. Sorting and data binding seem like reasonable core
> features to me. I'm not looking to build a business coding GWT
> widgets. I want to be able to build a decent web app with GWT alone.

Do you remember Delphi/C++Builder? There was set of just GUI
component, for manual operations. And there was set of subclasses that
add data-aware features. So, don't modify core widgets. If you need
data binding, create additional library.

Scott Stirling

unread,
Dec 29, 2006, 1:38:05 PM12/29/06
to Google-Web-Tool...@googlegroups.com
From a GWT user's perspective, does this argument of inheritance vs.
composition matter? I think it's a "rathole" I want to be able to pass
by, truly.

On the other hand, there are GWT internal implementation design
choices to be made. You may have a point there. I don't actually have
an opinion I want to argue for at the moment to that point.

I am thinking about Emily's question of whether data binding is worth
it. I am thinking my answer is, why not try it? Create a branch if
necessary, or commit it to head as an optional feature.

Scott S.
Framingham, MA

Dan Morrill

unread,
Dec 29, 2006, 2:12:01 PM12/29/06
to Google-Web-Tool...@googlegroups.com

I think a big issue here is one-size-fits-all-ness.  I also think this is what Emily is aiming at, when she asks whether anyone thinks this would even be useful.

Let me just point out the elephant in the room:  there are a million and one ways to manipulate your data on your server.  You might be using an O-R tool like Hibernate, or you might be using a stream or feed oriented mechanism like Google's GData API, or you might be using straight-up SQL-style transactions, and so on.  Each of these million and one server techniques will conceivably imply or demand concessions from the client side widget implementation, in this case GWT.

So the question is:  is it even possible to build an "intelligent widget" system for GWT that will meet a suitably large portion of users' needs?  Remember that we are server-agnostic, so we don't have the option of specifying a particular server technology and binding ourselves to it.

To put some hypothetical numbers to my question, suppose it takes 200 person-hours to implement such a widget system.  What percentage of GWT users will even care at all?  Will this feature be used by 50% of GWT users, or only 5%?  Beyond that, if only 33% of GWT users are even interested in the functionality, it could very well be that the solution proposed here is only practically useful to a third of *those* users, meaning that we could very easily spend 200 person-hours implementing a solution that is only useful to 10% of GWT users.  The question of course isn't whether those 10% of users are "worth" 200 person-hours, but rather whether we could have spent those 200 person-hours on a feature which benefits a larger percentage of users.

So, those are the questions on my mind.  To summarize them:
- Do we even have a hope of successfully building a generally-useful, server-agnostic intelligent widget mechanism?
- If we do, is Emily's proposal headed in the right direction? Why/why-not?

- Dan

On 12/29/06, Scott Stirling <scotts...@gmail.com> wrote:

Miroslav Pokorny

unread,
Dec 29, 2006, 3:48:50 PM12/29/06
to Google-Web-Tool...@googlegroups.com
Well breaking things up into browser specific version is definitely a mistake. Im pretty sure we all agree on that. Like everything else you have to be sensible and not take everything literally:) My only point was to have two separate components - the table widget and the data provider. With Emily's last comment thats not a worry :)

Probably off the point but why is there no api to set headers eventually creating a TH with text or widget ( setHeader( column, widget/text) for any of the Table based widgets eg Grid, HTMLTable, FlexTable instead of doing it thru DOM methods  or JNSI?

Joel Webber

unread,
Dec 29, 2006, 5:06:13 PM12/29/06
to Google-Web-Tool...@googlegroups.com
I think Konstantin hit the nail on the head with the Delphi/C++ builder reference.  This is a very common design pattern: each layer of a library should hoist you up one level of abstraction, without mixing them unnecessarily.  In addition to the Delphi example, I would also point out that SWT/JFace is a good example of this -- SWT gets widgets on the screen, period.  JFace adds MVC framework stuff on top of that, but there's really no good reason to mix the two, because in many cases I don't *want* to use JFace, so I'd rather not have to stare at its added complexity when I'm just trying to use the underlying widgets.

In GWT, we've tried to maintain a similar dichotomy.  The core widgets should give you what you need to get stuff on the screen and handle basic events.  There's no notion of MVC patterns, no attempt to enforce any particular design patterns, in fact.  The intention was for them to give you the minimum set of functionality, on which you can build more complex frameworks to your heart's content.

In this particular case, I think it's very appropriate to build data binding as a framework that builds on top of the basic widget set, and could be easily used with additional widgets from other libraries.  I seriously doubt anyone will ever find "one data binding framework to rule them all", and even if they did, there would still be people who don't need one at all.

Overall, I like this proposal -- but it would be really nice to hear if anyone has any suggestions that would make it better?  Think it sucks?  Even better, if you have real-world use-cases you've run into it would be great to hear how well this proposal would or would not apply to them.

Thanks,
joel.

Scott Stirling

unread,
Dec 29, 2006, 6:00:22 PM12/29/06
to Google-Web-Tool...@googlegroups.com
On 12/29/06, Miroslav Pokorny <miroslav...@gmail.com> wrote:
> Probably off the point but why is there no api to set headers eventually
> creating a TH with text or widget ( setHeader( column, widget/text) for any
> of the Table based widgets eg Grid, HTMLTable, FlexTable instead of doing it
> thru DOM methods or JNSI?

I wondered the same thing myself! - Scott

Emily Crutcher

unread,
Dec 29, 2006, 9:24:22 PM12/29/06
to Google-Web-Tool...@googlegroups.com
Are adding headers something that would be helpful to our users?  It seems like a reasonable extension to me, can anyone think of a reason it should/should not be added to HTMLTable?


 
On 12/29/06, Scott Stirling <scotts...@gmail.com> wrote:

Miroslav Pokorny

unread,
Dec 30, 2006, 12:01:53 AM12/30/06
to Google-Web-Tool...@googlegroups.com
I think it should.

Real tables rather than tables for layout, with columns always have headers which is probably why the distinction was made to include data cells (TD's) and table headers (TH's).
--
mP

space...@gmail.com

unread,
Dec 30, 2006, 9:51:25 AM12/30/06
to Google Web Toolkit Contributors

I've actually been working on doing just this for the last week. The
problem isn't as trivial as one might expect, particularly due to the
assumptions under which the current HTMLTable were written ie. always
TBODY.

I will gladly write up my current issues and concerns and put them up
for discussion. (As well as what I have so far in implementation).


On Dec 30, 12:01 am, "Miroslav Pokorny" <miroslav.poko...@gmail.com>
wrote:


> I think it should.
>
> Real tables rather than tables for layout, with columns always have headers
> which is probably why the distinction was made to include data cells (TD's)
> and table headers (TH's).
>
> On 12/30/06, Emily Crutcher <e...@google.com> wrote:
>
>
>
>
>
> > Are adding headers something that would be helpful to our users? It seems
> > like a reasonable extension to me, can anyone think of a reason it
> > should/should not be added to HTMLTable?
>

> > On 12/29/06, Scott Stirling <scottstirl...@gmail.com> wrote:

ijuma

unread,
Dec 30, 2006, 11:57:35 AM12/30/06
to Google Web Toolkit Contributors
On Dec 29, 12:38 pm, "Emily Crutcher" <e...@google.com> wrote:
> I'd also love feedback on the big question of "is it
> worth doing?" As that is the one I am most concerned with right now.

In my opinion, yes. I think the SWT/JFace analogy from Joel is a good
one. It's a good design to follow, but it's important to note that
JFace is provided by eclipse.org and makes the API a lot easier to use
in most cases. So, out of the box you have the choice between the
low-level SWT or higher level JFace (in most cases I would choose the
latter).

In addition, a lot has been talked about data binding in the Java GUI
world in the recent past. JFace included an experimental data binding
API in eclipse 3.2 and they've been working on the final version for
Eclipse 3.3. For Swing, there's the Beans Binding JSR[1]. So, it seems
to me that people have found the third-party binding libraries like
JGoodies useful enough that the toolkits decided to have it out of the
box. I've used the JGoodies bindings library in the past and it was
very useful for maintaining the data in sync between widgets and data
source.

[1] http://www.jcp.org/en/jsr/detail?id=295

Emily Crutcher

unread,
Dec 31, 2006, 12:07:52 AM12/31/06
to Google-Web-Tool...@googlegroups.com
That would be great ;-)
 
          Thanks,
 
                 Emily

 

Sandy McArthur

unread,
Jan 1, 2007, 3:17:54 AM1/1/07
to Google Web Toolkit Contributors
I've written a table implementation that uses the full HTML 4 enhanced
table model (row groupings: thead, tfoot, tbody) and while it's not
fully tested yet and still has a little more evolving to do, two
implementation details I've encountered are:

Opera: while the order of the thead and the tfoot elements should not
matter with respect to tbody elements they do for Opera if they are
manipulated via JavaScript. (Order doesn't matter in raw HTML source
for opera.) For Opera to render thead and tfoot correctly after
updating the DOM with JavaScript they must be the first and last childs
of the table element.

Safari: With some DOM inserts to the middle of the table Safari doesn't
re-layout the table correctly and visually tbody elements will appear
on top of thead elements. The quick hack I found around this is to
quickly add and them remove an empty caption element to the table
element after your updates and Safari should re-render the table
correctly.

I'm not sure if those bugs will affect GWT's HTMLTable implementations
because they work with the expectations of the basic HTML 4 table model
plus one tbody element. But if they do show up, hopefully that will
save you a number of hours of debugging. The source may have bug work
arounds I cannot recall off the top of my head too.

HTML 4 Tables:
http://www.w3.org/TR/html4/struct/tables.html

GWT-Stuff:
http://code.google.com/p/gwt-stuff/
A table demo is linked on the right side.

Emily Crutcher

unread,
Jan 1, 2007, 10:24:16 AM1/1/07
to Google-Web-Tool...@googlegroups.com
Thanks Sandy!  Do you know if legal ever got your contributors agreement? I know you had talked about it with Scott, but I don't know what the resolution was.

John Tamplin

unread,
Jan 1, 2007, 10:53:26 AM1/1/07
to Google-Web-Tool...@googlegroups.com
On 1/1/07, Emily Crutcher <e...@google.com> wrote:
Thanks Sandy!  Do you know if legal ever got your contributors agreement? I know you had talked about it with Scott, but I don't know what the resolution was.

Yes, you can see in the CONTRIBUTORS file.

--
John A. Tamplin
Software Engineer, Google

space...@gmail.com

unread,
Jan 1, 2007, 7:12:40 PM1/1/07
to Google Web Toolkit Contributors
Sandy,

I have also almost completed an implementation of the full 4.01 table
spec. Can I ask how you handled events?

I haven't traced through removal of individual cells as of yet, but I
have about a mid-maturity cut that makes the table structural sections
explicit (and gets around the Safari and Opera limitations by ignoring
the row indexes unless in a specific section) but tends to break bits
of backward compatibility.

The following document was to be my post discussing my implementation:

http://docs.google.com/View?docid=dhtrqvrz_8fhxrnw

Also, the patches are sort of mid-lifespan (and may have some widget
event holes as well as various small formatting bugs), but usable in
general. I can gladly send them to anyone willing to try them out or
look at them.

Thanks!
David

On Jan 1, 10:53 am, "John Tamplin" <j...@google.com> wrote:
> On 1/1/07, Emily Crutcher <e...@google.com> wrote:
>
>
>
> > Thanks Sandy! Do you know if legal ever got your contributors agreement?
> > I know you had talked about it with Scott, but I don't know what the

> > resolution was.Yes, you can see in the
> CONTRIBUTORS<http://google-web-toolkit.googlecode.com/svn/trunk/CONTRIBUTORS>file.

Sandy McArthur

unread,
Jan 1, 2007, 10:33:52 PM1/1/07
to Google Web Toolkit Contributors
Some Definitions:
* "Advanced HTML Table model" I mean the use of table row groups
(thead, tfoot, tbody) introduced in HTML 4.
* "Simple HTML Table model" I mean the use of tables where the table
row (tr) elements are children of the table element. Modern browsers
promote this to advanced model by introducing one tbody to hold all the
tr elements.

These terms aren't official but it's how they were described when I
first read about them and they stuck in my brain.


I chose not to start with the GWT provided table widgets. HTMLTable and
it's implementations are good at being intuitive for the programmer
thinking about a grid but their design doesn't work for the advanced
HTML Table model and efficient DOM manipulation.

On Jan 1, 7:12 pm, "spaceLe...@gmail.com" <spaceLe...@gmail.com> wrote:
> Can I ask how you handled events?

Each row group and table row is a UIObject instance and in GWT a
UIObject can be the source of events but only Widgets can receive
events. So when an event fires for my table widget it's onBrowserEvent
method redispatches the event to the table row group object's
onBorowserEvent method which then send the event to the table row
object's onBorowserEvent method. The programmer can add event listeners
on each of those object types which can deal with events.

In my table, table cells (td and th) extend SimplePanel so the normal
GWT event system just works there like any other Widget.

> I haven't traced through removal of individual cells as of yet, but I
> have about a mid-maturity cut that makes the table structural sections
> explicit (and gets around the Safari and Opera limitations by ignoring
> the row indexes unless in a specific section) but tends to break bits
> of backward compatibility.

IMO, the HTMLTable in GWT just wasn't designed to have header/footer
rows and it isn't worth trying to cram that feature into it. To
maintain existing behavior for backwards compatibility you need to add
all sorts of logic to deal with corner cases all over the place. This
will hurt performance and takes away from the ease of use Grid and
FlexTable currently have. In the end I think it's a net loss.

If you're going to do header/footer in HTMLTable widgets then you
probably should just fake using special logic it instead of using table
row groups. For example things like row spans work differently once you
introduce row groups. In the simple table model a header cell with a
row span of 99999 would extend down the whole length of the table but
not cause the table to actually have 99999 rows effectively giving you
a header column. In the advance table model the row span doesn't extend
past the row group so your header column won't run down the side of the
table body row groups. If the header and footer are in a different row
group than the body rows then you'll get unexpected behavior for
setRowSpan.

space...@gmail.com

unread,
Jan 1, 2007, 10:59:07 PM1/1/07
to Google Web Toolkit Contributors

On Jan 1, 10:33 pm, "Sandy McArthur" <sandy...@gmail.com> wrote:
> Each row group and table row is a UIObject instance and in GWT a
> UIObject can be the source of events but only Widgets can receive
> events. So when an event fires for my table widget it's onBrowserEvent
> method redispatches the event to the table row group object's
> onBorowserEvent method which then send the event to the table row
> object's onBorowserEvent method. The programmer can add event listeners
> on each of those object types which can deal with events.

...so you cheated ;) I struggled with this problem, what happens to
TableListeners under this model?


> To maintain existing behavior for backwards compatibility you need to add
> all sorts of logic to deal with corner cases all over the place. This
> will hurt performance and takes away from the ease of use Grid and
> FlexTable currently have. In the end I think it's a net loss.

Could you elaborate on this? I don't understand what corner cases you
are talking about that any given table in html doesn't already suffer
from.

Sandy McArthur

unread,
Jan 2, 2007, 2:51:09 AM1/2/07
to Google Web Toolkit Contributors
I should have said that the ObjectListTable in GWT-Stuff isn't as
general purpose as GWT's HTMLTables. ObjectListTable is very much about
rendering rows or groups of rows from data stored in a List. When that
list is manipulated then the table does what it needs to do to reflect
those changes. I think that type of logic is rewritten too many times
when it's external to the table implementation.

space...@gmail.com wrote:
> On Jan 1, 10:33 pm, "Sandy McArthur" <sandy...@gmail.com> wrote:
> > Each row group and table row is a UIObject instance and in GWT a
> > UIObject can be the source of events but only Widgets can receive
> > events. So when an event fires for my table widget it's onBrowserEvent
> > method redispatches the event to the table row group object's
> > onBorowserEvent method which then send the event to the table row
> > object's onBorowserEvent method. The programmer can add event listeners
> > on each of those object types which can deal with events.
>
> ...so you cheated ;) I struggled with this problem, what happens to
> TableListeners under this model?

I don't use TableListener, it doesn't make sense for a couple of
reasons. First, the interface only specifies a row and column, but I
would need a row group parameter too. Since HTMLTable basicly uses one
tbody row group then for TableListener that parameter is implicitly
always zero. Second, HTMLTable is one Object that controls all the
elements from table to td. The ObjectListTable makes use of separate
Objects that manage one element each and you attach a mouse listener
where you want to receive events. If you only want mouse over/out
events fired for every third row you could express that and while I
haven't benchmarked it yet, I think it would perform better than firing
events for every row and ignoring two thirds of them.

> > To maintain existing behavior for backwards compatibility you need to add
> > all sorts of logic to deal with corner cases all over the place. This
> > will hurt performance and takes away from the ease of use Grid and
> > FlexTable currently have. In the end I think it's a net loss.
>
> Could you elaborate on this? I don't understand what corner cases you
> are talking about that any given table in html doesn't already suffer
> from.

You said yourself "The GWT DOM currently accesses rows in a table using
the rows array of a table or the body element passed in from a function
call. This makes the problem of adding headers and footers non-trivial
due to the fact that the tfoot row is always [thead(rows) + 1] or
[tbody index - 1] regardless of its display order." but this isn't true
with Opera, the tfoot has to be at the end else it won't render
correctly. A lot of the corner cases come from optimizing update
performance. For example appendChild is faster than insertChild. To add
a row to the end of the table in everything but Opera you can use
appendChild. In Opera you would want to use appendChild when there
isn't a footer for faster updates. Maybe you won't find across all the
quirks I did trying to get things to work in each browser the same way.

Emily Crutcher

unread,
Jan 2, 2007, 7:13:59 AM1/2/07
to Google-Web-Tool...@googlegroups.com
I agree with Sandy that HTMLTable does not meet the needs of all users. HTMLTable's mandate is to be as easy as possible for people to use while providing 90% of the needed functionality.

I'd be really curious to find out all the problems either of you see/have had with retrofitting HTMLTable with simple headers and footers. Keeping offsets dependent on whether the table has headers and footers for row indexes is certainly one of them. What are the others?

Also, for the HTMLTable scope, can anyone think of a reason where having a simple one row header and footer would not be sufficient?  One mantra we use for  HTMLTable is to keep it as simple as possible, and setHeader(col)/setFooter(col) seems, right now, the simplest API.

David Evans

unread,
Jan 2, 2007, 8:30:47 AM1/2/07
to Google-Web-Tool...@googlegroups.com
I got around monitoring row offsets by scrapping that approach. DOM spec shows that row groups respond to the rows[] array the same way as a top level table. I found that the behavior of a table's rows[] array in various browsers was inconsistent, some index by display order, but have the row respond to rowIndex with their structural location, but others do the exact opposite. I found that since rowSections' rows arrays are defined to be absolute with respect to their section, it was easier to simply replace all references to the table at large with specific sections and ignore the absolute indexes in the table.

Since HTMLTable was written assuming body as the target for all operations, it was simply a matter of modifying the code to pass a row group section to define what row/col you are referring to. This semi-breaks and semi-maintains backward compatibility, primarily due to the fact that prepareCell and prepareRow have to be modified in subclasses, but all other operations can be re-implemented in HTMLTable as functional passthroughs to the table's tbody - this way anyone not interested in headers and footers doesn't really need to think about them at all.

Emily Crutcher

unread,
Jan 2, 2007, 11:02:34 AM1/2/07
to Google-Web-Tool...@googlegroups.com
There is some shared functionality between preparing a cell for a body cell and a header cell, but probably not enough to collapse the methods together. With Grids, for instance, we save some round trips to the DOM by simply consulting our cached row and column size, which is worth it for body rows, but would probably not be worth maintaining for headers or footers, as most headers and footers are not set that often. Also, modifying prepareCell to include row group is a breaking API change, and those are reserved for the direst of emergencies ;-).
 
What about the approach of creating another inner class of HTMLTable for footers and headers, and put the prepare/set/get code for headers there? It follows the established pattern of tucking advanced functionality away in subclasses and it allows protected prepare methods for headers/footers to be naturally created and grouped with their corresponding functionality.
 
Cheers,
 
     Emily

David Evans

unread,
Jan 2, 2007, 11:06:42 AM1/2/07
to Google-Web-Tool...@googlegroups.com
I like that idea!

I have no problem removing the prepareCell overrride problem, it was
something that bothered me. I should be able to make sample
modifications to my implementation within the next day or two to try
this out and see if it makes sense.

The other problem that I see (that is actually pretty huge) is a clean
way to handle table events and determine which section is described in
the event.

Not to mention the Opera limitation that Sandy mentioned earlier...

Emily Crutcher

unread,
Jan 2, 2007, 1:19:21 PM1/2/07
to Google-Web-Tool...@googlegroups.com
Events are going to be interesting to retrofit.  We've known we've boxed ourselves in a corner with our event model for a while, fixing it is a harder problem.  One solution we have explored is to introduce "Handler"  event classes to supersede the listener classes, check out FormHandler as an example. The handler model creates a event object to submit to the user, which allows us to add more fields to an event without breaking the currently existing code. We could use this opportunity to add a TableHandler and CellClickedEvent (or multiple cell clicked events if it's much more efficient) to HTMLTable.
 
I'm hoping Sandy will post more details about some of the problems he encounted.  For instance, is there a problem in Opera with append child even if you are restricting yourself to a single body element and appending directly to that element?
 
If there are still Opera or other quirks we must deal with, then the solution will probably be to bite the bullet and create a HTMLTableImpl in order to specialize methods per browser.  Check out TextBoxImpl for an example.

David Evans

unread,
Jan 2, 2007, 1:55:56 PM1/2/07
to Google-Web-Tool...@googlegroups.com
I would love to be testing this out right now (stupid job always
getting in the way of fun!) I have been struggling with this decision
for events for a while, which is why i asked about events from Sandy.
I made changes to SourcesTableEvents such that an implementation is
expected to maintain the section from which the event was generated
(which actually allows subclasses to have clearing capability or event
routing).

I am also interested in the Handler pattern, I was actually thinking
earlier that my proposed solution deals with Tables as UI elements and
a logical grouping of Elements, but Sandy's is a little more like a
Form element, where the Table itself is just a logical distinction not
a 'physical' element - tables contain actual visible, eventable things
not the table itself. It seems like Sandy has a better handle on this
than I do, so perhaps he can speak to this.

On Jan 2, 2:51 am, "Sandy McArthur" <sandy...@gmail.com> wrote:
> For example appendChild is faster than insertChild. To add
> a row to the end of the table in everything but Opera you can use
> appendChild.

I dont really understand how append child is any faster regardless of
location in the physical table unless its always being appended to the
end. Shouldn't we be attempting at least to not blatantly violate the
"TFOOT is always before TBODY" structural rule in the DOM where
possible? If one references the sections and appends to them instead
of a very large tbody, then the append cost is as bad as it is
currently in tbody, but negligible in tfoot and thead sections.

Sandy McArthur

unread,
Jan 2, 2007, 2:39:28 PM1/2/07
to Google Web Toolkit Contributors
Emily Crutcher wrote:
> I'm hoping Sandy will post more details about some of the problems he
> encounted. For instance, is there a problem in Opera with append child even
> if you are restricting yourself to a single body element and appending
> directly to that element?

I dunno. I wrote my table so the table row group is created and
populated with rows and then cells before it's attached to the DOM.
(The goal being that I help the browser do fewest incremental
re-layouts while updating a table widget until my code is done.
Honestly I'm not sure I actually achieved anything there because I
haven't tested ordering DOM updates the other way around.) So I've only
every add/removed/moved table row group elements.

I would suspect that if you are adding tr elements to a single tbody
element then since it's at a different level of the DOM it shouldn't
cause a problem. eg: DOM.appendChild(aTable.theTbody, aTr)

But if you use a tfoot row group for the footer then you'll probably
can get away with it being the last element of the table element for
all browser despite what the HTML spec says.

(BTW: does anyone know of a place to submit bug reports for Opera? I
tried to find one when I first stumbled across this quirk but didn't
see a place for that.)


The Safari rendering glitch I don't understand as much. It seemed to
only happen when you inserted row groups near the top of the table.
Sometimes the inserted rows would be rendered on top of the existing
header rows as if the header rows didn't exist. I think the problem
didn't happen if you were only appending row groups but I'm 100% sure.
When I figured out that removing a child element of the table element
caused it to be rendered correctly I added the hack to add/remove an
empty caption element and didn't explore this quirk further.

> If there are still Opera or other quirks we must deal with, then the
> solution will probably be to bite the bullet and create a HTMLTableImpl in
> order to specialize methods per browser. Check out TextBoxImpl for an
> example.

That is what I did.

David Evans

unread,
Jan 2, 2007, 3:05:02 PM1/2/07
to Google-Web-Tool...@googlegroups.com
I just ran a quick test in Opera (win), Safari, Firefox (mac & win) and IE 7

Made a table with a handful of rows in a tbody, as well as a header
and footer layed out according to spec. Ran some code to dig until the
tfoot section was found and checked the locations that the DOM
expected the footer to be located in and changed the HTML therein;
interestingly, I found that Opera is fine (at least 9.10) when using
the row index directly (even tho its the structural not the display
index), but Safari is off by 1 every time. The Firefoxes (2.x) and IE7
were also all fine.

Why did I bother? Well, I got to the rowIndex by asking the tfoot
section for rows[0].rowIndex, so the absolute value within the group
holds the correct location in the overall table everywhere but in
Safari apparently.

Why do I care? If one is designing headers and footers by monitoring
row offsets structurally, it creates a special case for Safari, but no
others. If one uses row groups with absolute row indexes within the
groups, the special case disappears. This is particularly convenient
in the current HTMLTable model because the table itself is treated as
one giant tbody row group and only indexed by table in two places.

On 1/2/07, Sandy McArthur <sand...@gmail.com> wrote:
>

Emily Crutcher

unread,
Jan 2, 2007, 3:07:55 PM1/2/07
to Google-Web-Tool...@googlegroups.com
For any browser bug that causes a GWT problem, if there is conceivably a way to program around it, then we would want to file a GWT bug report for it. I believe you can also report Opera bugs to Opera here https://bugs.opera.com/wizard/. I'll keep an eye out for the Safari behavior, thanks!
 
          Emily
 
 
On 1/2/07, Sandy McArthur <sand...@gmail.com> wrote:

Emily Crutcher

unread,
Jan 2, 2007, 3:13:08 PM1/2/07
to Google-Web-Tool...@googlegroups.com
Which two places use table instead of tbody for indexing rows? As that is probably a bug.

On 1/2/07, David Evans <space...@gmail.com> wrote:

David Evans

unread,
Jan 2, 2007, 3:16:15 PM1/2/07
to Google-Web-Tool...@googlegroups.com
getCellElement and getDOMCellCount

really only getCellElement appears to be table based, and
getDOMCellCount is just being used that way.

On 1/2/07, Emily Crutcher <e...@google.com> wrote:

space...@gmail.com

unread,
Jan 2, 2007, 3:22:21 PM1/2/07
to Google Web Toolkit Contributors
I take that back, all calls appear to be relative to the tbody,
getCellElement just has some misleading parameter names. My apologies.


On Jan 2, 3:16 pm, "David Evans" <spacele...@gmail.com> wrote:
> getCellElement and getDOMCellCount
>
> really only getCellElement appears to be table based, and
> getDOMCellCount is just being used that way.
>
> On 1/2/07, Emily Crutcher <e...@google.com> wrote:
>
> > Which two places use table instead of tbody for indexing rows? As that is
> > probably a bug.
>

> > On 1/2/07, David Evans <spacele...@gmail.com> wrote:
>
> > > I just ran a quick test in Opera (win), Safari, Firefox (mac & win) and IE
> > 7
>
> > > Made a table with a handful of rows in a tbody, as well as a header
> > > and footer layed out according to spec. Ran some code to dig until the
> > > tfoot section was found and checked the locations that the DOM
> > > expected the footer to be located in and changed the HTML therein;
> > > interestingly, I found that Opera is fine (at least 9.10) when using
> > > the row index directly (even tho its the structural not the display
> > > index), but Safari is off by 1 every time. The Firefoxes (2.x) and IE7
> > > were also all fine.
>
> > > Why did I bother? Well, I got to the rowIndex by asking the tfoot
> > > section for rows[0].rowIndex, so the absolute value within the group
> > > holds the correct location in the overall table everywhere but in
> > > Safari apparently.
>
> > > Why do I care? If one is designing headers and footers by monitoring
> > > row offsets structurally, it creates a special case for Safari, but no
> > > others. If one uses row groups with absolute row indexes within the
> > > groups, the special case disappears. This is particularly convenient
> > > in the current HTMLTable model because the table itself is treated as
> > > one giant tbody row group and only indexed by table in two places.
>

Sandy McArthur

unread,
Jan 2, 2007, 4:05:47 PM1/2/07
to Google Web Toolkit Contributors

Emily Crutcher wrote:
> For any browser bug that causes a GWT problem, if there is conceivably a way
> to program around it, then we would want to file a GWT bug report for it. I
> believe you can also report Opera bugs to Opera here
> https://bugs.opera.com/wizard/.

I couldn't reproduce the opera problem I came across before. Maybe 9.10
fixed or maybe I was smoking crack and didn't know it. I'll recheck it
out later.

> I'll keep an eye out for the Safari
> behavior, thanks!

I compiled a version that triggers the overlap bug in Safari. Load
http://sandy.mcarthur.org/gwt-stuff/bugs/safari/overlap/TestTable.html
in Safari and click "Add Person" a few times until the bug happens and
you'll see something like:
http://sandy.mcarthur.org/gwt-stuff/bugs/safari/overlap.png .

Scott Stirling

unread,
Jan 2, 2007, 8:46:00 PM1/2/07
to Google-Web-Tool...@googlegroups.com
Sorry for the late reply. My answer is yes, absolutely, it would be
helpful to GWT users.

Thank you,
Scott Stirling
Framingham, MA

Ian Bambury

unread,
Jan 3, 2007, 5:31:34 AM1/3/07
to Google-Web-Tool...@googlegroups.com
I've been playing with a system (nowhere near as organised as you are proposing) comprising 4 maim elements:
 
A client-side database (allowing creation of tables, records fields etc with simple SQL such as CREATE TABLE myTable) which lets you add triggers to tables, records, and fields within a specific record.
 
Controls bound to the data
 
A single datarequestor which updates the client-side database as required by the varios areas of the app
 
A server-side element which returns the data, caching requests with a time-to-live
 
The advantage for me is that HTTPRequests don't get queued up, and I've still got the other one for ad-hc requests.
 
Just a thought.
 
Ian

space...@gmail.com

unread,
Jan 3, 2007, 9:03:42 AM1/3/07
to Google Web Toolkit Contributors
I can't seem to get that same bug to appear by inserting a row in
Safari either with insertRow() or with the iterative insertBefore that
GWT uses. Its a really strange rendering bug I agree!

Have you been able to reproduce it with straight javascript?

this brings me to my next point (while on the Table topic); is
insertRow well supported? It seems like the 'digging' approach
currently used could get very slow for large tables. If the browser has
this implemented, shouldn't we be inserting rows using the mechanics of
the browser?

On Jan 2, 4:05 pm, "Sandy McArthur" <sandy...@gmail.com> wrote:
> Icompiled a version that triggers the overlap bug in Safari. Loadhttp://sandy.mcarthur.org/gwt-stuff/bugs/safari/overlap/TestTable.html

Emily Crutcher

unread,
Jan 3, 2007, 9:07:03 AM1/3/07
to Google-Web-Tool...@googlegroups.com
Can you elaborate what you mean by the "digging" approach? I'm not sure I understand the potential problem.
--
"There are only 10 types of people in the world: Those who understand binary, and those who don't"

David Evans

unread,
Jan 3, 2007, 9:13:24 AM1/3/07
to Google-Web-Tool...@googlegroups.com
Of course (sorry about being so vague):

currently when a row is inserted into a table (by FlexTable in
particular) the following code walks to the parent element (in this
case the tbody element of a flextable)

var count = 0, child = parent.firstChild, before = null;
while (child) {
if (child.nodeType == 1) {
if (count == index) {
before = child;
break;
}
++count;
}
child = child.nextSibling;
}
parent.insertBefore(toAdd, before);

So let's say that the tbody element is to have a new row inserted at
position N, this code essentially runs the tbody row for row until it
gets to the Nth row (or the end if the index is N+1) and then runs
insertBefore. I am wondering if perhaps insertRow(rowIndex) from the
DOM makes more sense specifically for row insertion into tables...

I know Sandy has way more knowledge of javascript table performance
than I do, but it seems like the browser's implementation of insertRow
may (or honestly, may not) be better at this.

Emily Crutcher

unread,
Jan 3, 2007, 9:41:41 AM1/3/07
to Google-Web-Tool...@googlegroups.com
That makes sense, it's another instantiation of the  w3-counts-whitespace-nodes problem, so we have to manually skip over them on standards-based browsers.  I'm not sure how much of a speed problem it is, but I'm also pretty sure that the solution is completely general and should be no slower than the current solution, even for IE.   If you want to take a crack at it and post a diff, I'll take a look at it once your CLA is approved, if it doesn't hold interest to you, give me a holler and I'll put it on my to do list. 
 
If you are interested, in general we test at our desks the latest version of IE, FireFox, Opera, Hosted Mode, and Safari for any UI change.  If you don't have access to any of those browsers, just make sure to include which ones you did not test. We also would probably need a better junit test for insertRow if we change the implementation,FlexTableTest is where that test would live.
 
      Cheers,
 
            Emily

Sandy McArthur

unread,
Jan 3, 2007, 12:07:51 PM1/3/07
to Google Web Toolkit Contributors
David Evans wrote:
> I know Sandy has way more knowledge of javascript table performance
> than I do, but it seems like the browser's implementation of insertRow
> may (or honestly, may not) be better at this.

Maybe it would be faster if I made more use of JSNI but I've currently
limited myself to GWT provided APIs. What I did to address the speed
for large tables is, I have a List of tbody elements and I use the
insertBefore method to manipulate the DOM. This way I don't have to
iterate to find the row I want to insert at. I just look up that row
number in the List and insert a new one.

I did have to write a insertBefore method in JNSI but I hope that
method gets added to GWT's DOM class:
http://code.google.com/p/google-web-toolkit/issues/detail?id=448

I'm really just finishing up the second step in 1) make it work, 2)
make it work right, 3) make it fast. Maybe later I'll look into faster
methods when I have a need to speed things up.


Emily Crutcher wrote:
> That makes sense, it's another instantiation of the
> w3-counts-whitespace-nodes problem, so we have to manually skip over them on
> standards-based browsers.

If GWT code was what built up the table element's DOM, as opposed to
being from HMTL source, how would whitespace nodes get added to the
mix? And if whitespace nodes couldn't be added then wouldn't it be safe
to skip the nodeType check?

Emily Crutcher

unread,
Jan 3, 2007, 12:53:20 PM1/3/07
to Google-Web-Tool...@googlegroups.com
In the next release I'm toying with the ability to construct tables from strings in order to speed initialization of HTMLTables with server-side data, so I'd like to avoid any solution with absolutely relies upon no white space nodes existing, other than that, Sandy you are correct, we could skip the check.
 
       Cheers,
 
                Emily

 

Scott Blum

unread,
Jan 3, 2007, 12:59:38 PM1/3/07
to Google-Web-Tool...@googlegroups.com
FWIW, the code Sandy referenced under gwt-stuff is under Apache 2.0 so I think in general you can look at that sort of thing regardless of whether that person submitted a CLA.

PS: Spankings all around for not starting a new thread to discuss the Table stuff.  Now back to your regularly scheduled databinding discussion... :P

On 1/1/07, John Tamplin <j...@google.com> wrote:
On 1/1/07, Emily Crutcher <e...@google.com> wrote:
Thanks Sandy!  Do you know if legal ever got your contributors agreement? I know you had talked about it with Scott, but I don't know what the resolution was.

Yes, you can see in the CONTRIBUTORS file.

lpod...@gmail.com

unread,
Jan 4, 2007, 2:32:19 PM1/4/07
to Google Web Toolkit Contributors
Returning to the original question about data-aware widgets, I
definitely think this would be a worthwhile thing to have in the
"standard" GWT. While the core library can't provide everything, I
think many typical web apps would benefit from a higher-level table
abstraction, for example. Right now it's a lot of work to get a
dynamic data table working on top of the raw FlexTable class.

I recently spent some time writing an MVC-style table that supports
sorting, paging and filtering, with similar features to what you get
from the extremecomponents.org table widget. It was a lot of work to
get it right.

One thing that I think is important is making sure the design can
support client-side table "decoration" in addition to displaying raw
server data. For example, I needed to allow extra widgets to be added
to a row (like checkboxes to select the row for group operations, or a
link/button to allow the row to be edited or deleted), that are not
strictly part of the server-side data model but are required for
interaction. In my design, there is a decorator layer where you can
augment the server data to add these additional items to the model that
is ultimately used to populate the FlexTable. I wanted to support
inline cell editing as well, but I don't yet know the best way to
design it.

Reply all
Reply to author
Forward
0 new messages