May 17, 2005

Data Binding Chapter Available

The Developing Rich Internet Clients With Macromedia Flex chapter on my favorite subject, data binding, is now available as a free download!

Posted by mchotin at 07:02 PM | Comments (4) | TrackBack

November 17, 2004

Enabling gzip compression for data services

Brian Deitte on the Flex team wrote up a quick set of classes that can be used to compress your network traffic for all 3 data services (RemoteObject using AMF, HTTPService, and WebService). Dan Schaffer on the QA team then did a number of performance tests to see what kind of improvements we could get. I've been given permission to re-post his results here. This is strictly for experimentation, it is not officially supported in any way.

You can download the source (includes a README for setup which you should read since it has more info than Dan's report) here. Dan's report follows:


Brian wrote a 3 classes to gzip compress the output of flex data services. Configuration is just a matter of adding a filter in web.xml. The player will detect the the header will accept gzip encoding then uncompress the content type. A minimum size may be set in the configuration so only data greater than the minimum is compressed. The implementation is really nice because the code is really independent of the Flex source code so it can easily work with any versions of Flex.

Based on a small study I would only recommend compressing the data service streams when the bottleneck is the network bandwith or very large datasets are common. Also under load even on a high performance server the additional cpu cycles required to compress the data stream may slow down the response time. Also the data must be compressible (ie text). I think most data will contain much repitition and be highly compressed with the gzip algorithm.

- Setup is Easy
compile the 3 classes and copy into WEB-INF/classes of the flex app and add to web.xml:
<filter>
<filter-name>GZIPFilter</filter-name>
<filter-class>filter.GZIPFilter</filter-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>minimumSize</param-name>
<param-value>0</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>GZIPFilter</filter-name>
<servlet-name>AMFGatewayServlet</servlet-name>
</filter-mapping>
<filter-mapping>
<filter-name>GZIPFilter</filter-name>
<servlet-name>FlexProxyServlet</servlet-name>
</filter-mapping>

- Performance
Encoding a data stream is very cpu intensive. Only when using a fairly beefy server and sending more than 100k of data at once produced performance improvements. A slow server and small packet sizes produced slower round trip time than using an uncompressed stream. In cases smaller than 100k especially <50k the performance is worse with gzip compressing enabled.

I wrote a small app to send data via RemoteObject, HTTPService, and WebService data calls from the server to the flash player. The app sends the small xml files for each cases in 10k, 25k, 50k, 100k, 250k, and 500k. I ran a single client 12 times for each data service and size then removed the highest and lowest, averaging the remaining. On my laptop the gzip compression was always slower. Once I moved the webapp to flexload (a decent multi-processor server) only after 100k was the performance better with gzip compression enabled.

FLEXLOAD4 (time in MS)
10k 25k 50k 100k 250k 500k
Remote Object-gzip 138 150 150 167 324 1442
Remote Object 89 124 136 221 613 1407

Web Service-gzip 177 143 166 194 523 1600
Web Service 108 124 177 299 732 1846

HTTP Service-gzip 155 161 182 178 269 454
HTTP Service 88 107 151 239 466 905

T30 LAPTOP
(time in MS)
10k 25k 50k 100k 250k 500k
Remote Object-gzip 137 160 173 207 642 3294
Remote Object 103 102 129 153 554 2298

Web Service-gzip 240 267 240 271 871 3766
Web Service 110 142 158 213 788 3604

HTTP Service-gzip 140 149 149 209 267 352
HTTP Service 100 105 129 161 244 392

The data was very compressed:
uncompressed compressed
10k 1k
25k 2k
50k 2k
100k 3k
250k 7k
500k 13k

Posted by mchotin at 04:11 PM | Comments (2)

November 02, 2004

MAX examples posted

I've uploaded the 1.5 versions of my data management examples, the same ones that I'm showing in my MAX presentation. Grab 'em here and give 'em a whirl. Please check out the README first to make sure you get it installed correctly.

Posted by mchotin at 12:41 PM | Comments (0)

October 26, 2004

Text Filtering through a lot of data

Check out new iteration::two member Daniel Harfleet's post on dynamic text filtering. A really nice example.

Posted by mchotin at 10:43 AM | Comments (0)

October 06, 2004

Effective Enterprise Java

TheServerSide has posted a sample chapter from Effective Enterprise Java about state and data management within your J2EE application. While the book focuses on J2EE a lot of the concepts discussed apply to general application development. You'll also get a preview of some of the things I plan on talking about at MAX in a few weeks. The first section of the chapter talks about saving state in the HTTPSession and some of the limitations; it's practically an advertisement for some of the benefits of RIAs :-) It also discusses data loading techniques coming from a relational database. You'll recognize some of the issues from the large data discussions we've had.

Posted by mchotin at 09:07 PM | Comments (0)

August 10, 2004

RemoteObject Stateful vs. Stateless Classes

Hans asked a while back if I'd discuss stateful vs. stateless classes for the RemoteObject tag. I don't think I have anything definitive or official to say, but I'll go through my own thought process as to how I decide what kind of class I'm going to use. First, the default is stateless, so I try to err on using a stateless class unless something compelling comes along to make the class stateful. With that in mind, some reasons to consider stateful classes (not in any order).

I think I could probably keep building on this list, but you are hopefully seeing a pattern. Like most optimizations, you are making a choice between time and space. You generally save time by having a stateful class which means potentially less network traffic between Flash client and app server and fewer instantiations. However the expense is that you end up using more memory on the server as every RemoteObject and relevant data is stored for each session. Sometimes the choice will be obvious, and sometimes you'll need to run tools like profilers and heap analysts to give you more data to reach a decision.

If anyone has stories of how you've made your own decisions feel free to share. And maybe some other folks who've done more work with the RemoteObject at Macromedia will jump in as well.

Posted by mchotin at 01:15 PM | Comments (2)

Notes on Data Validation

I've been answering some questions for folks internally and externally about validation and thought I'd try to post it here for anyone who's interested. This is a combination of personal opinion as to how validation can work as well as notes on how it does work now...

Validation in Flex is a mechanism for determining if data is valid. That data can be any field of any object, anywhere. The Flex system tries to build on this simple concept and add in the ability for certain data to be automatically validated. And we then take it a step further and if possible display errors at the source of their problem (the red border around a UI component).

Before getting into a few more details I want to share some of the thoughts that went behind the design of our validation framework. First, we always want to encourage a separation of Model and View. The view onto data might look different than the raw representation of data itself (think formatting). Therefore, we built around the principle that it is data that is valid or invalid, not the view onto that data. This is why you validate a Model's field, not the TextInput's text.

The second principle we followed is that validation should not affect data flow. In Flex, data is often passed around using data binding which therefore implies that the source and destination should always be in sync. In fact, there is no way to determine if the source and destination are in sync because it is assumed they always will be (with the notable exception of not syncing an initially undefined/null/empty-string value). Given that binding implies synchronization, it seemed that it'd be wrong for a binding to not take place if the data it was trying to pass was invalid. We decided the data should go to its destination and other parts of the system should prevent further application execution if there is a problem. I've seen differences of opinion on this approach and am open to incorporating other ideas into the system in the future if they make sense.

OK, now some more details on automatic and manual validation. A field is automatically validated if it is the destination of a binding expression. This generally implies that you will validate Models as opposed to UI controls, as it's often safe to assume that data already in Models will be correct while user input may be incorrect. If the field you want to validate is not the destination of a binding (or you want to re-execute validation on something already bound) you need to use the Validator.isValid method (or Validator.isStructureValid). See the ASDoc and chapters for more info.

Now what about making the red borders and error tooltips appear. When a binding is created the compiler looks at the source expression and sees if it can find a UIComponent as part of it. If it does, that becomes the automatic listener for a Validator using the destination as its field. So when we see a binding expression like this:

<mx:Model id="foo">
  <bar>{someInput.text}</bar>
</mx:Model>

we see that the source expression is "someInput.text" and the destination is "foo.bar". A Validator with field="foo.bar" will thus have a default listener of "someInput" (the UIComponent). So there we go, automatic validation listeners. Now if the compiler didn't make the connection correctly or if you want a different component to be your listener, simply reassign the listener on the Validator tag (the docs discuss how to be a validation listener).

Extra notes:

A Validator can be assigned to validate objects not in your document. You can validate components that live in other documents no problem:

<mx:Validator field="someComponent.someOtherComponent.someObject.someField" listener="someComponentOnMyDocument" />

If the Validator is defined in another document but you want to call isValid from another location, the isValid call is ready for that as well.

The method signature for isValid is this: Validator.isValid(document, field), document is the reference to the document where you defined the Validator. field is the string that should exactly match the field declaration on the Validator tag.

So if you're willing to call isValid yourself you don't need to worry about setting up binding for validation to work (and it doesn't need to validate against an mx:Model). And if you want the red borders to appear and you're not using binding, you set up the listener. And if all of that needs to happen in another document, no problem.

Hope this sheds some light on validation. Feel free to make comments on the docs or here if you'd like more info on something.

Posted by mchotin at 01:11 PM | Comments (5)

July 08, 2004

Non-database examples available

I've posted a new zip file for all of the data management examples. You are no longer required to have a database to run them. This should make installing and testing even easier. You can download the new zip file here.

All I did was create a simple (non-robust) parser for the CSV file that I was using to import the data into the database. I wrote a new version of my DAO and had the CensusService use that instead. The database DAO is still in the source and you can see the commented out calls to it in the CensusService. I admit, I was too lazy to create an interface/factory for the DAO, etc. so you'll just have to survive with my quick edits. The only change I made to the MXML was to set the RemoteObject for CensusService to type=stateful-class so that access was more efficient. The logic has not changed at all.

Hope you find this useful!

Posted by mchotin at 10:58 PM | Comments (1)

May 13, 2004

Large Data Sets (3/3)

Now that we have discussed paging and sorting let's talk about getting the experience a little better for your user. Our first step is to add to the paging solution so that the DataProvider looks ahead when the user is approaching the end of a page. After that we'll improve the performance of the DataGrid by turning off the "live scrolling" so that the DataGrid doesn't try to update as the user is navigating. Finally we'll build on that DataGrid to provide context information to the user so they can navigate more accurately. The examples can be found here and are a superset of what we did in articles 1 and 2.

Lookahead

The paging solution introduced in the first article is useful in many situations to reduce the amount of data put on the wire at once and allow the user to interact with your app sooner. However, it's possible to improve things even more by loading a few pages even before the user even needs them. When the DataProvider notices that someone has requested an item within a certain threshold it will go ahead and load the next page. This means that by the time the user actually gets to needing that page it will already exist, having been brought down in the background. This solution is great when a user is moving through a list looking at a few records at a time sequentially. However, if the usage model is based on a lot of random access this technique may not be appropriate as you won't gain much by downloading a page the user is unlikely to see. Example 6 shows the lookahead solution by using a new DataProvider implementation called LookaheadPagingDataProvider. Much of the code is the same as SimplePagingDataProvider, there's just been some refactoring and the addition of lookahead logic. If you run the example you'll see that I log when a page is loaded based on a Request (the user wants to view the data now) or if we're loading based on Lookahead. Lookahead works for both ends of a page, if you're near the top of a page we'll look to load the previous one, the bottom looks for the next page.

Live Scrolling

One issue when using a DataGrid with lots of data is that it may be slow to scroll when using the scrollbar. Remember that every time the DataGrid wants to display data it calls getItemAt on the DataProvider, and if you've looked at the source code you can see that there's a lot more logic than simply returning an element in an array. This means that there's a lot going on as you move the scrollbar "thumb" around, and performance can degrade. If you've played with the Flex layout controls you might have noticed that the DividedBox classes have a property called "liveDragging." When this is true the UI will attempt to re-layout everything as the divider is dragged around. When false, the UI will wait to re-layout until the divider has been released, making the dragging much faster. We can achieve noticeable performance gains if we apply this same logic to the DataGrid. Example 7 is essentially the same as Example 6 except that I've created a subclass of DataGrid (ingeniously called FasterDataGrid) that allows you to disable live scrolling. Note that this implementation is only really possible because I have access to the source code so I could see what I needed to do. It is probably not the best way to achieve this effect, but rest-assured we are looking into adding this capability into our controls in a future release so you don't have to. With that in mind, please note that this is "unsupported" and the underlying code that's relied on may change at any time. So consider yourself warned if you use this in your own code.

ToolTips

The last improvement that I want to make is strictly for usability. When you're dealing with a large amount of data it can be difficult to know where you are in context to the rest of the data. If you've used recent versions of Microsoft Word you may have seen a feature where as you drag the scrollbar it shows you the page number and perhaps a heading to indicate where in the document you would land should you release the scrollbar there. I've tried to apply that idea to my last example. In our case we'll take the field that was used for sorting and create a complete list of values for that field that can be displayed when the user is moving the scrollbar. Since we're only trying to load one field we don't have to worry about creating thousands of complex objects, in our case it's just Strings. In my experiments I was able to bring down all 20,000 values for the sort keys in approximately 8 seconds. That's a lot faster than bringing down complete records, especially if you consider that a user will take a few seconds to think and process what they're seeing before moving the scrollbar. In order to allow user interaction while downloading I page the sorted key list, but I use a much larger page size (7500 worked OK for me on my machine). I also don't wait for the user to need a page from this list, as soon as the first page loads we go ahead and request the next one. Example 8 shows the implementation with SortKeyProvider.as and ToolTipDataGrid.as being the important files. You may want to play with the SortKeyProvider pageSize attribute to see what works best for you as the interface does pause while the keys are being read in.

Conclusion

Many applications use a large amount of data and unfortunately there isn't a magic bullet to making everything ready the moment the user wants it. A common "first-step" to working with large data is to retrieve it in pages. This allows the user to see data immediately and gives you the opportunity to download more in the background or simply wait until the user actually wants to see it. The user will often want to move around the data even if it is not all on the client so it is up to you as the developer to provide appropriate navigation metaphors. One simple example is putting up a tooltip as the user scrolls so they can see what will be loaded when they stop. Remember, the user only needs the information that they can see. If you can deliver visible information quickly you will have the opportunity to download the details later in the process when the user doesn't mind a small delay.

This concludes the current series of discussions on large data sets. The topic will continue to come up regularly since I focus on the data side of Flex, but I do think about other things and would like to discuss them as well. Hope you'll stay with me!

Posted by mchotin at 02:00 PM | Comments (18) | TrackBack

April 12, 2004

Large Data Sets (Part 2 of 3?)

Our second topic for dealing with large data sets is sorting. The problem with sorting is you need to have all of the data available in order to produce an accurate sort. However, as we saw in the last article bringing down all of the data from server to client is incredibly expensive and therefore unrealistic. Even normal web applications rarely load all of their data from the database server to the app server when dealing with a large amount.

Instead of downloading all of the data to the client we'll let the database take care of sorting for us (which is one of the things a database is very good at). Let's build on our examples from the previous article and add sorting capability to the implicit paging solution. The examples can be found here and are a superset of what we did in article 1 (meaning that the old examples are there too).

Sorting

The first part to adding sorting support is in our data service. Remember from the last example we had a ValueListService which was similar to the ValueListHandler. I've now created a subinterface called SortedValueListService. The first method we care about is an extension of getElements called getSortedElements. We'll keep this method simple and only allow sorting on a single field plus a boolean indicating whether the sort should be in ascending or descending order. If you were to build on this you might change the sortField into some form of sort descriptor.

I've implemented this method in the CensusService and added the appropriate method to the CensusDAO. The CensusDAO is now much more closely tied to the MS SQL Server database as it uses a stored procedure to retrieve the data as we need to keep track of row numbers which requires use of temporary tables. The same would not be true in Oracle which supports rownum inherently and I'm not sure about MySQL but I'd bet it's not hard there either. My stored procedure is based on info I found on the 4GuysFromRolla.com site. The premise is the same as the normal getElements SQL, the only difference is that we need to throw sorting in and therefore can't rely on the ID being the same as the row number.

Now I need to update my Flex code to take advantage of sorting. Most of my changes to support sorting go in the SimpleDataProvider. I've added an implementation of the sortItemsBy method which is specified by the DataProvider "interface" but did not implement the sortItems method as that uses a comparator function (which means needing all of the data on the client). I then assume that the data provider will always be retrieving sorted data, so I changed it to always call the getSortedElements method instead of getElements. The SDP then maintains the sortField and sortAscending boolean to pass to the server whenever a new page is needed. When the sort changes we first check to see if all the data is available in which case the sort can occur on the client. However, that rarely happens so we essentially need to start over retrieving data from the server. Therefore all we need to do is set sortField and sortAscending variables, then clear the list and let the DataGrid simply ask for the correct data. You can see this implementation in Example 4.

Maintaining the current selection

If you played with the example you may have noticed that when you change the sorting options the DataGrid maintains its vertical scroll position but does not maintain the selection. This is the default DataGrid behavior and is fine for small amounts of data but might be frustrating if you're looking to use the sort to group similar items near each other starting from your selected item. Example 5 is one attempt at addressing this issue (and I'm not guaranteeing that it's the best). Another method is added to SortedValueListService called getSortedPosition which given the same search criteria as getSortedElements will tell you the 0-based position of the element you pass in. This requires another stored procedure in the database for MS SQL Server but would be similarly straightforward on another database.

Integrating this change into the Flex code is a little more complicated as I didn't want to corrupt the DataProvider itself with the concept of a selected item, as that is dependent on the view into that data. So what I decided to do is control the sorting operations myself instead of letting the DataGrid manage it. Now when the header is pressed I keep track of what the intended sort is, and then go ahead and get the index of the selectedItem and find out what its sorted position would be (this is all before actually modifying the DataProvider). Finding out the new sorted position requires a server call so it's possible that you'd want to use the busy cursor or some other mechanism to indicate that things are happening, but I didn't in the example. Once the new sorted position is known for the selected item we can go ahead and sort the list. Next we scroll the DataGrid to where the selectedItem should show up, and finally once that page has loaded we select the actual item. The reason we need to wait for the page to load before setting selectedIndex is that the DataGrid will clear the selection if it changes the underlying data. If you put a trace in the handleLoad method you'll see that a few pages are loaded after a sort even though they aren't the pages that should be visible. I think I'd probably need to change the DataGrid class to avoid this so we'll ignore that for now despite its minor performance impact. If we were going to generalize this I think we'd want to subclass the DataGrid so that it knows it's working with the pageable data provider along and give it a reference to an object that can provide certain information like the sorted position of an element.

Conclusion

So that's the basic approach for sorting large data sets. If anyone has had experience that might improve upon these by all means let me know. In the next article I'll write some things we can do to improve the perceived performance when dealing with large data, and then I think we'll try to move on to other topics.

Posted by mchotin at 07:46 PM | Comments (13) | TrackBack

March 29, 2004

Large Data Sets (Part 1 of Many)

This is the first of what will most likely be many postings related to data management and Flex. Our opening topic is a brief discussion on large data sets and how to efficiently transfer them from your application server to the client. I'm going to assume you are familiar with MXML and the components and services that are a part of the Flex runtime.

This post will refer to many examples that are found here. Instructions for installing the samples can be found in a README included in the zip.

The Problem

Our problem is simple. We need to display a lot of data, and the user does not want to wait too long for it to appear. The more data a user requests, the longer it takes for the data to display. What we need to do is a find a way to download data in pieces so that the user can view a smaller amount of data immediately and then only wait a brief amount of time as they request additional data. To keep the problem domain simple we are only going to discuss improving the actual retrieval of data. We'll leave discussions on sorting, modification, filtering, etc. for another time.

Example 1 can help you visualize this problem. The data is census information that I got from The UCI KDD Archive and reduced to 20,000 records. Start by downloading a few records, perhaps 10. This doesn't take long at all. If we increase that number to 100 the response is usually a little slower (these numbers can vary depending on your system setup, for example whether the database and application servers are on the same machine in addition to where you run the Flex app). Increase the number to 1000 and you'll see a bigger jump. Finally 5000 and the time is becoming painful. Painful enough that an end-user might give up.

The Solution: Paging

Smarter people than I solved this problem a long time ago by displaying data in pages. A page allows the user to see a discrete number of entries, and then when ready move on to the next batch. A typical example would be a search engine. Go to your favorite search engine and notice that whenever you search for something you only have a small number of results (10 or maybe 20) before you reach the bottom and can move to the next page. Even if a search returned 3000 results I've personally found it unlikely to ever look at anything past the third page. So if that search engine had returned all 3000 results at once to my browser, I'd be waiting forever for it to come down over the wire and render on screen, when really I only cared about those first 30.

The Server Component

Before getting into writing paging support in Flex, let's talk about how you retrieve pages on the server. I've written some very simple classes to retrieve my census data. The first is an interface (just to show that I'm thinking generically), the ValueListService. As you might have guessed, this is a very simple adaptation of the ValueListHandler described at the Sun Core J2EE Patterns site. The original ValueListHandler is treated as an Iterator which means that it must maintain state about its current position. Since my Flex application is going to do that for me, all I need from the ValueListService is the number of elements, and the ability to retrieve an arbitrary number of elements starting from a certain position. Therefore my ValueListService looks like this:

public interface ValueListService
{
    public int getNumElements();
    public List getElements(int begin, int count);
}

I've then implemented this interface using a CensusService which is capable of using a CensusDAO to retrieve the relevant data, which is populated into CensusEntryVO objects. The CensusDAO is currently only tested on MS SQLServer but the SQL is pretty standard. It's also not very robust given that this is sample code; all exceptions are simply caught and printed. So now when the Flex client wishes to view census data it will ask the CensusService for a List of CensusEntryVOs.

The RemoteObject Tag

We're going to use the RemoteObject tag to access the CensusService. Flex offers three data services: HTTPService, WebService, and RemoteObject. HTTPService is useful for getting information from plain XML files or perhaps servlets that serve XML. WebService is obvious, it is used to call web services which return data over the wire as XML. RemoteObject allows you to access Java objects on your application server and by default uses a binary format called AMF for efficient data transfer. Since this "article" is about transferring large amounts of data, RemoteObject is a good choice of service because of its efficient transfer mechanism. However, the concepts discussed can easily be adapted to the other services.

Explicit Paging

Our first solution follows the same idea as a search engine. Return data one page at a time and let the user request the next page. I'm going to call this technique explicit paging. The explicit paging solution is ideal for when a user requests a number of records but may only care about a specific subset rather than the entire result set. Searching is a primary example, an address book could be another. Pages do not have be to sized according to the number of records, they can meet other search criteria like last names beginning with C.

Example 2 let's you see how this works. Assuming you ran Example 1 you got a sense of how many records could be downloaded before the wait became unbearable. On my particular setup about 1000 records was the max I wanted to put myself through, but I think in reality that number would be closer to 100 for the app to appear speedy. There is a slider on the app that allows you to change the number of records in a page. From there you can see that the page selector is created to allow access to whichever page the user desires. The page selector uses a Repeater to create a number of Links which are then used to select the pages.

This solution is pretty straightforward. You'll see that most of my code is actually related to the page selector itself, not the data. This means that someone with better UI skills can write a whizbang page selector that can be re-used and all you'll need to do is configure it. Flex is new, but I have no doubt that we'll see robust components sprouting up soon for these kinds of purposes.

So an explicit paging solution is useful when a user wants to see pieces of data. However we might need another solution if it's important for the user to see all the data at once.

Implicit Paging

Our second approach is a technique I'll call implicit paging. Here we don't want the end-user thinking about viewing finite pieces of data, instead it should appear that all of the data is available. Rather than downloading all of the data at once, we'll download a small piece and then as the user comes to data we haven't downloaded we'll go ahead and get it. If you use a mailreader and store your mail on a server this concept will be familiar to you. View one of your mailbox folders and begin to scroll down. As you scroll there may be a little pause as the reader gathers more data for you to look at.

This problem might sound hard; how do you configure the DataGrid control to only get small chunks of data at a time? It's actually pretty simple! The DataGrid takes a dataProvider that must conform to an "interface." Interface is in quotes because unfortunately we don't actually use an interface due to the need to mix in functionality to classes whose signature we can't modify. So all we need to do is implement the DataProvider interface using a class that knows how to retrieve data in pages instead of all at once.

Example 3 shows my simple implementation. The SimplePagingDataProvider (SPDP) is given a reference to a RemoteObject that will communicate with a ValueListService. Whenever the DataGrid asks for data (using the getItemAt method) the SPDP will check to see if the page for that item is loaded. If so the data is returned; if not the SPDP will ask the RemoteObject to download the data and in the meantime return a dummy value. This is pretty straightforward:

public function getItemAt(index : Number)
{
	var item = data[index];
	if (item == null)
	{
		item = miss(index);
	}
	return item;
}

private function miss(index : Number) : Object
{
	var page : Number = Math.floor(index / pageSize);
	//if the page was already loaded then the value actually is null
	if (pagesLoaded[page] == true) return null;
	//it's possible that the page is already being loaded
	if (pagesPending[page] == true)
	{
		//this miss event is useful for just monitoring what's going on
		dispatchEvent({type: "miss", index: index, alreadyPending: true});
		return "loading";
	}
	//if the page is not loaded call for it
	var call = dataService.getElements(page * pageSize, pageSize, this);
	call.page = page;
	//we want to keep track of how long it takes to load
	call.startTime = getTimer();
	pagesPending[page] = true;
	dispatchEvent({type: "miss", index: index, alreadyPending: false});
	return "loading";
}

The DataGrid will show blank rows while the data is loading in the background but it will continue to function (i.e., no hanging). When the data is downloaded the rows will be filled in. In the example I used a page size of 100 because on my setup there were only brief periods where the DataGrid appeared not filled in. Note that this solution works both when a user slowly moves down the list (perhaps using the PgDn key) and when dragging the ScrollBar.

Conclusion

This is just the first step in addressing the problem of dealing with large datasets in your Flex RIA. I've introduced the concept of paging (though I doubt it's new to you) and showed two different techniques for integrating a paging solution into your application. The explicit paging solution is useful when there is a lot of data to be shown but it is unlikely that the user wants to view all of it at once. The implicit paging solution acknowledges that a user wants to see a lot of data, but we bring it across the wire incremementally so that performance is acceptable. In future posts I'll try to talk about expanding these solutions to improve performance (both real and perceived), take into account dynamic data, allow sorting, and more. If you have any thoughts on this including topics you'd like to see discussed in the future please drop me a comment.

Some resources that I've begun looking at and will come into play more in future entries:

Posted by mchotin at 12:00 AM | Comments (31) | TrackBack