have you considered an RangedEventList that is backed by a DB Table:
i.e. a number of records are transferred to the client, of that a
range is displayed. If the user moves the range beyond the already
downloaded records a request retrieves the next batch of records.
How would you suggest implementing this?
Thanks for your help,
Matthias
Matthias
That won't work in a asynchronous environment and do you really want a
List.get(index) to block while a XmlHttpRequest is made to get an
object instance representing a table row? (GWT's RPC doesn't provide a
blocking RPC which would make implementing that very hard.) The List
methods aren't designed for asynchronous usage so you need a way to
bridge the synchronous and asynchronous gap.
I'm dealing with with the same basic problem. Below is about what I'm
doing. If you like it adapt it to your needs and style.
My model classes are designed so they can be lazily initialized. So
when List.get(123) is called, a empty, stub of an instance is
returned. I then check to see if the values of that instance have been
fetched from the server and if not I schedule a RPC call that will
populate it with the correct values. How you indicate an uninitialized
instance is up to you. I tend to use null as the uninitialized value
but you can also use a boolean flag.
Now to make that lazy initialization work I need two things. (This is
a basic minimum, you can get more clever than this if you need.)
1. Either a list of stub objects fetched via RPC or a list of unique
ids (primary keys work well here) that you can use to create the stub
instance on demand with a clever EventList implementations. (I'm
currently using using the latter because it makes the RPC payload
smaller to send a List/array of numbers or strings than it takes to
send a List/array of stub objects. I may add the ability to split that
list of uids into chunks across a few RPC calls if even that proves to
be too large.)
2. A method to apply updates to your stub objects and tell your view
that model has changed so the view can be updated. (I like to use the
observable model pattern so I use the emulated
java.beans.PropertyChangeSupport provided by the GWTx project. You can
roll your own observable pattern if you want. There are memory leak
risks you need to be careful of when you do this. A less memory risky
way is to have a global repaint() method that knows how to tell the
view to update itself and call that after you've updated the model.)
Example:
I'm a library and I want to display a table of all the Books in the library.
class Book {
private final String id; // either a primary key or maybe an isbn number
// these are lazily fetched when first needed
private String title = null;
private Author author = null;
private DewyDecimial dewy = null;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public Book(String id) {
this.id = id;
}
public String getId() {
return id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
Object old = getTitle();
this.title = title;
pcs.firePropertyChange("title", old, title);
}
// getters/setters for the rest of the properties.
// Observable pattern hooks
public void addChangeListener(PropertyChangeListener l) {
pcs.addChangeListener(l);
}
public void removeChangeListener(PropertyChangeListener l) {
pcs.removeChangeListener(l);
}
}
Author and DewyDecimial may be similar classes or small POJOs I send
directly via the RPC.
Then I may have a BookIdEventList that is constructed with an array of
ids. When the first call to BookIdEventList.get(3) is made a new
Book(ids[3]) is created, saved for future calls to get(3) and
returned. Maybe you want to test that it needs it's data filled in so
you may set up the RPC call to request and then set the missing
properties. I tend not to do this here because different parts of my
app use need different properties. eg: I don't want to download a book
summary paragraph if I'm only displaying title and authors.
Now we need a view that knows how to update itself once the model's
data has been requested.
public class TitleLabel extends Composite {
private final Label label = new Label();
private final PropertyChangeListener changeListener = new
BookChangeListener();
private final Book book;
public TitleLabel(Book book) {
this.book = book;
initWidget(label);
addStyleName("library-BookTitle");
book.addPropertyChangeListener(changeListener);
updateTitle();
}
private void updateTitle() {
// if the title is loaded
if (book.getTitle() != null) {
// update the label text
label.setText(book.getTitle());
// remove the circular reference from the model back to the view
// that could cause a memory leak
book.removePropertyChangeListener(changeListener);
}
}
private class BookChangeListener implements PropertyChangeListener {
public void propertyChange(final PropertyChangeEvent event) {
updateTitle();
}
}
}
This assumes that the title doesn't change after it's been set. if you
need a truly dynamic title property you need more complicated logic
but the code is rather similar, just add/remove the change listeners
in onAttach and onDetach methods.
I hope I explained enough of that to get you thinking about a solution
that can work for you. There is lots of room for optimizations and
flexibility to adapt to your needs in that general design. It is a bit
more code than a straight up brute force solution but once you get the
feel for it you can write the code quite quickly without too much
thought and it will likely save you time later in the project.
Optimization points include batching RPC calls somehow to reduce the
number of HTTP requests, prefetching data you anticipate the user
wanting, more aggressive lazily initialization, chunking of data
results, and probably many more.
HTH, only you know what's best for your project's needs.
--
Sandy McArthur
"He who dares not offend cannot be honest."
- Thomas Paine