How do I force the SmartTable to load all items? - odata

My SAP UI5 view contains a SmartTable that is bound to an entity set in an ODataModel in read-only mode.
The table limits the number of items it displays to 100, verifiable by the ?$top=0&$limit=100 parameters it appends ot the data query to the server.
However, my users would like to have the table load and display all items, without paging or having to press some "More" button.
Is there a way to override the SmartTable's default behavior? For example by setting some growingSize property to "Infinity"? Or by modifying the aggregation binding? Or by adding annotations to the OData service?

Since you did not specify the number of expected items or the table you're using here are some general considerations and a few possible solutions.
The type of table
There are a few things to consider between the varying types of tables you can use, there is some advice of SAP itself from the design guidelines:
Do not use the responsive table if: You expect the table to contain more than around 1,000 rows. Try using the analytical table or grid table instead; they are easier to handle, perform better, and are optimised for handling large numbers of items.
Ways around it
First option I can think of, if you're using a responsive table and you expect less than 1000 rows then the scroll to load feature might be of interest which should load more entries when the user reaches the bottom of the current list.
There are ways to increase the default size of 100, both through the table using the growingThreshold property, or through the declaration of the oData model using the sizeLimit
If you're using a grid table then the scroll-to-load works slightly differently, since it doesn't display all rows at the same time. Instead, it destroys the current lines to display new lines, which is why it's recommended for (very) large datasets.
Second, if none of those solutions work for your users you could alternatively first fetch the count of the current list including filters, so you can set an accurate threshold on the table before displaying results. If done correctly, your oData service should return a count using /myserivce/MyEntity/$count?$filters... when queried. CDS does this automatically, older services will need to implement that separately.
Last, if you know the list never exceeds a certain number of lines, you could set the growingThreshold parameter on the table to that number and then you don't have to worry about fetching an accurate count first.
How all of this is implemented depends a bit on how you create the smart table (elements, manually etc) so I'm not sure how to provide usable example code

You can achieve this by using a formatter function which will return how many entries are within your model.
<SmartTable growingThreshold = "{path:'yourModel>/', formatter:'.formatter.sizeCalculator'}">
In the formatter File, which is usually to find in the model folder:
sizeCalcualtor: function(oModel){
let count = 0;
for(let i in oModel){
//add item to count;
}
return count;
}

Related

Any tips for displaying user dependent values in a list of items?

It is pretty common for a web application to display a list of items and for each item in the list to indicate to the current user whether they have already viewed the associated item.
An approach that I have taken in the past is to store HasViewed objects that contain the Id of a viewed item/object and the Id of the User who has viewed that item/object.
When it comes time to display a list of items this requires querying the database for the items, and separately querying the database for the HasViewed objects, and then combining the results of these queries into a set of objects constructed solely for the purpose of displaying them in the view.
Each e.g li then uses the e.g. has_viewed property of the objects constructed above.
I think it is time to find a better approach and would like to know what approaches you would recommend.
i wanna to know whether there is better idea also.
Right now my solution is putting the state in the redis and cached the view with fragment cache.

Good algorithm for reloading a batch of UICollectionView or UITableView changes?

Setup: I have a UICollectionView or UITableView that’s backed by a simple array data source. I keep a copy of that data source in the controller.
Now, I get a notification from the system that there’s new data available. I get a new array where items may have been added, removed and changed positions.
So now I have two data objects:
previous array that's in sync with what the UI is currently showing
new array where items have been added, removed, moved
To get the UI in sync with the new array, I need to generate a bunch of UI calls. In case of UICollectionView, those are
- (void)insertItemsAtIndexPaths:(NSArray *)indexPaths
- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath
- (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths
And there’s a similar set of methods for UITableView.
I specifically don’t want to reload the whole table because that’s more expensive than just working with a few items.
So, the question is: given the previous and new data source array, how do I generate the correct set of UI calls, and when do I "swap out" my old data source for the new?
I'd see this as largely equivalent to the diff / patch problem, where the aim is to find the minimal number of changes between one text file and another and then to apply those changes. In that case, the implementation defines the operations:
add or insert
delete
change
... but not move. The reason for omitting move isn't immediately obvious to me but I'd strongly suspect that including move would require a very costly computation to find the optimal movement.
So, if we restrict the operations to those listed above, the Hunt-McIlroy algorithm described in An Algorithm for Differential File Comparison, or one of its descendents, would find a close-to-optimal set of changes.
The difference between your problem and the classic diff / patch is that you have a two-dimensional table, while diff / patch deals with a one-dimensional set of items (lines of text). The best way to convert the 2-D problem into the 1-D problem would depend on the particular characteristics of the changes that tend to be made in your data table.
For example, if the table is n rows by m columns and changes tend to be grouped in rows or rows are inserted or deleted as a whole, then you would likely be best to consider the table as if it was a text file and do the diff line by line. Alternatively, if changes tend to be grouped in columns or columns are inserted or deleted, you could do the diff column by column. If the alterations include inserting or deleting individual cells (which result in the subsequent cells shifting right or left as a result), you could treat the table as if each cell in the table was on a separate line of the text file, linearising the table in either row-first or column-first order.
Without knowing the details of your problem in this respect, however, my inclination would be to avoid premature optimisation. Thus, I'd tend to start by implementing the Hunt-McIlroy algorithm row-by-row if m < n, or column-by-column if n < m, then profile the application in use for a while before deciding that either a more sophisticated version of the algorithm or an alternative mapping of your problem to the Hunt-McIlroy solution was warranted.
A good discussion of a variety of diff algorithms can be found here on stackoverflow.
I have worked on an app that deals with a very similar problem that i was able to solve. I was expecting a complicated solution but it was really simple. Here is how you can solve it:
1) Create a new array in which you will receive new items and call it let us say 'moreItems', also synthesise it of course.
#property (nonatomic, readonly) NSMutableArray *moreItems;
2) In viewDidLoad of the ViewController that is linked with your TableView or CollectionView, alloc/init this array:
moreItems = [[NSMutableArray alloc] init];
3) Now you need to add your existing array let us say it was "items" to the new array that you have just created named 'moreItems'. You can do this using 'addObjectsFromArray'
[moreItems addObjectsFromArray:[channel items]];
'channel items' contains the objects it has received earlier and it will add these items to the newly created array called 'moreItems'. I am assuming you are collecting data from some web service, so you can implement this step in connectionDidFinsihLoading.
4) Now change your data source of TableView/CollectionView and replace it with the new array that is 'moreItems'
5) Next issue is that you don't want to reload the whole tableview and want to deal with new items. To have this functionality, you need to persist items. You can use archiving or core data whatever you are comfortable with.
6) The items that are already fetched, you will have to persist them let us say with archiving. And show them in the tableview when the user opens the app while it grabs more items that are updated on the web service.So first show the persisted items immediately and then handle the new items.
7) You need to look for some unique object, in my case it was the 'link' as every item has a different link and i can sort and handle them on this basis. You also need to use
- (BOOL) isEqual:(id)object
to compare the links on the web service and the links that are already in the tableview. This step is necessary because then your app won't add the items with links that are already in the tableview.
8) If there is some date associated with each item, you can use that date to sort them and show the new ones on top by using 'sortUsingComparator'
9) Also you need to use "[[self tableView] insertRowsAtIndexPath:rows withRowAnimation:UITableViewRowAnimationTop" to have that effect that shows user the new items have been added on top of the earlier ones.
Hope this helps.
I specifically don’t want to reload the whole table because that’s
more expensive than just working with a few items.
Are you sure this is true? Unless you have pathological case where every single cell is visible on your screen and there are hundreds of them, I find it hard to believe how will the "efficient" way be any faster than the "brute force" way.
Calling reloadData will only ask your delegate to update those cells that will end up visible on screen, it will not cause the entire table view to recreate every single of its cells.
If you're implementing some methods that change the complexity of calculating dimensions (like tableView:heightForRowAtIndexPath) you won't get any benefit using "efficient" way since the table view will have to recalculate all its heights anyway.
I'm not sure if solutions you are looking for will fix the problem, but let's just hope that I'm terribly wrong.
If your data source already has diff information, UITableView reloadRowsAtIndexPaths:withRowAnimation: will make it less-performance intensive:

How to display large list in the view without using database slicing?

I have a service that generates a large map through multiple iterations and calculations from multiple tables. My problem is I cannot use pagination offset to slice the data because the data is coming from multiple tables and different modifications happen on the data. To display this on the screen; I have to send the map with 10-20,000 records to the view and that is problematic with this large dataset.
At this time I have on-page pagination but this is very slow and inefficient.
One thing I thought is to dump it on a table and query it each time but then I have to deal with concurrent users.
My question is what is the best approach to display this list when I cannot use database slicing (offset, max)?
I am using
grails 1.0.3
datatables and jquery
Maybe SlickGrid! is an option for you. One of there examples works with 50000 rows and it seems to be fast.
Christian
I end up writing the result of the map in a table and use the data slicing on that table for pagination. It takes some time to save the data but at least I don't have to worry about the performance with the large data. I use time-stamp to differentiate between requests. each requests will be saved and retrieved with its time stamp.

How to remove duplicate records in grid?

Good morning !
What is the best way to remove duplicate records from grid control? I use Delphi 2009 and devEx quantumGrid component.
I tried looping through all the records and when a duplicate record is found then add it to list and apply filter on grid. I found this as time consuming logic. There are also two downsides of this approach.
[1] When the duplicate records are considerably more say 10K records then applying filter takes lot of time, because of lot of entries to filter out.
[2] Looping through all the records is itself time consuming for big result set like 1M rows.
SQL query returns me distinct rows, but when the user hides any column in grid, then it resembles as if there are duplicate records(internally they are distinct).
Is there any other way of doing this?
Any ideas on this are greatly helpful !
Thanks & Regards,
Pavan.
Can you alter your dataset to not return duplicate records in the first place? I would normally only return the records I want displayed instead of returning unwanted records from the database and then using a database grid to try to suppress unwanted records.
With thousands of rows I would add an additional field to the DB called say Sum or Hash or if you can't change the DB add a calculated field if it is a ClientDataSet but this carries overhead at display time
Calculate the contents of the hash field with something fast and simple like a sum of all the chars in your text field. All dupes are now easily identified. Add this field to your Unique or Distinct Query parameters or filter out on that.
Just an Idea.
Checking for duplicates is always a bit tricky, for the reasons you just mentioned. The best way to do it in this particular case is probably to filter before the data reaches the grid.
If this grid is getting its records from a database, try tweaking your SQL query to not return any duplicate records. (The "distinct" keyword can be useful here.) The database server can usually do a much better job of it than you can.
If not, then you're probably loading your result set from some sort of object list. Try filtering the list and culling duplicate objects before you load it into the grid. Then it's over with and you don't have to filter the grid itself. This is a lot less time-consuming.
I have worked with DevExpress's Quantum Grid for some time and their support form http://www.devexpress.com/Support/Center/ is excellent. When you post questions the DevExpress staff will answer you directly. With that said, I did a quick search for you and found some relevant articles.
how to hide duplicate row values: http://www.devexpress.com/Support/Center/p/Q142379.aspx?searchtext=Duplicate+Rows&p=T1|P0|83
highlight duplicate records in a grid: http://www.devexpress.com/Support/Center/p/Q98776.aspx
Unfortunately, it looks like you will have to iterate through the table in order to hide duplicate values. I would suggest that you try to clean the data up before it makes it to the grid. Ideally you would update the code/sql that produces the data. If that is not possible, you could write a TcxCustomDataSource that will scrub the data when it is first loaded. This should have better performance because you will not be using the grid's api to access the data.
Edit
ExpressQuantumGrid will not automatically hide rows that look like duplicates because the user hid a column. See: http://www.devexpress.com/Support/Center/p/Q205956.aspx?searchtext=Duplicate+Rows&p=T1|P0|83.
Poster
For example, I have a dataset which
contains two fields ID and TXT. ID is
a unique field and TXT field may
contain duplicate values. So when the
dataset is connected to the grid with
all columns visible, the records are
unique. See the image1.bmp. But if I
hide the ID column then the grid shows
duplicate rows. See the image2.bmp.
DevExpress Team
I'm sorry, but our ExpressQuantumGrid
Suite doesn't support such a
functionality, because this task is
very specific. However, you can
implement it manually.

Custom Listbox: Limit Maximum Item Count

I have silverlight 3.0 project that has a listbox that is databound to a list of items. What I want to do is limit the number of items displayed in the listbox to be <= 10. I originally accomplished this by limiting the data bound to the list to 10 items by doing a .Take(10) on my orignal data and databinding the result.
The problem w/ the .Take(10) approach is that the original datasource may change and since .Take() returns a reference (or copy not sure) of the original data I sometimes do not see changes in the data reflected in my UI.
I'm trying to figure out a better way of handling this rather than the .Take() approach. It seems you shouldn't 'filter' your data using LINQ functions if you have more than one UI element bound to the same data. My only thought on how to do this better is to make a custom container that will limit the count, but that seems like it might be a mountain of work to make a custom stackpanel or equivalent.
Take(10) does not make a copy, it just appends another step to the LINQ query. But all execution is still deferred till someone pulls the items of the query.
If you were setting the items statically, a copy would indeed be created, by running the query once. But since you set the constructed query as the ItemsSource property of the list box, it can run and update it any time, so it is the right approach.
The real reason why you sometimes do not see changes in the data reflected in the UI is that the list box has no way to determine why the data returned by the query have changed and it surely doesn't want to keep constantly trying to refetch the data and maybe update itself. You need to let it know.
How can you let it know? The documentation for ItemsSource says that "you should set the ItemsSource to an object that implements the INotifyCollectionChanged interface so that changes in the collection will be reflected (...).". Apparently the default way of doing things by .Net itself does not work in your case.
So there are some examples how to implement that yourself e.g. in this SO answer. If even the top-level source data collection (over which you are doing the LINQ query) does not support these notifications (which you would just forward), you might need to update the list box manually from your other code which changes the underlying data.

Resources