How to paginate a growing list? - pagination

What's a good way to paginate a list that is constantly growing?
For example, a user may open page 2 of some listing, keep looking at it for a while and then open page 3. Meanwhile, new records have been added to the beginning of the list (page 1) and caused page 3 to contain items that could have been on page 2 previously.
Should such a list be paginated from the other end? Then how to deal with the front page and the fact that it would, without special attention, contain TOTAL NUMBER OF ITEMS % PAGE SIZE items (ie. not constant)?

Assuming you have a fixed page size, some specified ordering, and the user is specifying the page they wish to view, I would simply fetch the data accordingly. Trying to do anything more complicated than that will just end up causing you unnecessary headache and confuse the user.
I think the element you're missing here is the ordering. If you specify an ordering then the user will intuitively understand. Also, that's how the majority of pagination is done, so you're not deviating from what you're user really expects.

I'd add a warning to advise the user that new items have come in and allow them to refresh the list. If they choose not to then maintain list in the state it was in when they clicked on the "Page 2" button. You can use a timestamp on the URL to determine which version of the list to serve to which user.

I do not think it's a good idea to page through a growing list. You'd better recalculate items that should be displayed on a page everytime user performs an action.

Related

Adding and Removing Fields Programmatically on Forms

Sorry this isn't a specific coding question, it is more of a design concept.
What is the usage case for programmatically adding and removing fields to Notes Forms e.g. NotesDocument.RemoveItem(), ie why would you add and remove fields in the background?
For many years I have designed my forms with the fields layed out on the form which are required and then hide and show as required.
By adding dynamically you can't position them and frustratingly removing them or deleting they still appear the Database Fields in Domino Designer, getting rid of them is a bit a a black art, but that's another story.
I must be missing a trick or a basic design concept. Any thoughts on best practice would be appreciated.
Many thanks.
Yes, you are missing the difference between "Fields" and "Items". A field is a design element that you can place anywhere on your form. You define how it looks, what content it contains, what datatype it is, etc.
When creating a document with the form the value of the FIELD is stored in an ITEM in the resulting NotesDocument.
This item is totally decoupled from the field that created it. If you were to change the field in the form from text to number or move it around or make a names- field of it, the item in the existing documents would never change unless you open the documents and save them in frontend or use any LotusScript or Formula Code to recalculate the document in backend.
Very often items are added programmatically to documents to fulfill different purposes: Calculate values to be displayed in views, calculate values that are import for the workflow but not for the user, etc.
Complex applications often consist of a lot more items than there are fields in the several forms.
Back to your question: Removing an item from a document simply removes the value that was created by the field in the form. When reopening the document, the item will be repopulated, either by default value or whatever....
Usually you would use this to remove items that you no longer need (and probably already removed from the form).
As soon as you removed all references to a field / item everywhere in design and documents, you can finally get rid of it completely by compacting the database.
An item is distinct from a field in Notes. The form is purely a UI concept, the item is what the data is stored in.
Manipulating data in the backend can be used for a number of reasons. One such use case is the setting of a flag when a date on the form has expired.
Say you want a view showing all documents that have expired. Your rules dictate that documents are considered as "Expired" after 7 days. You could create a view with a formula that shows all document whose date is 7 days older than today:
SELECT Date < #Adjust(#Today; 0; 0; -7; 0; 0; 0);
This view will ALWAYS be out of date and will constantly be updated by the server as it re-evaluates #Today.
Now, a better way would be to create an agent that runs daily that sets an item on the document to indicate that it has expired e.g.
#SetField("Expired"; 1);
The view formula would then be
SELECT Expired = 1
The view would only need to update daily and you have a much faster view because of it.
RemoveItem is used to get rid of data no longer needed e.g. FaxNumber.
There are many use cases for RemoveItem. Here's one that comes up frequently.
You have a database and an agent that processes documents in that database. Every time it runs, the agent replaces the value of a bunch of items. There are a variety of error conditions that can cause it to abort processing a document early, but you're a smart programmer and you've accounted for that with on error traps. When you hit one, you log an error message, save your document, and then either abort your agent or go on to processing the next document.
But at this point, some of the items that the agent normally updates have values saved from this run, and some of them have values saved from a previous run. This might be bad. This might be confusing for someone who is looking at the item values and trying to figure out what's going on. This might even cause validation errors on the form.
So how do you avoid this? At the very beginning of your agent, you call a cleanup sub that finds and removes all the items that the agent is going to update. Now you have a clean slate, and if your agent hits that error condition, it can save whatever it can save without any concern about whether it is leaving things in an inconsistent state. Of course, in cases where you are doing this to avoid validation errors, your validation formulas will have to be smart enough to be checking #IsAvailable for dependent items, but that's a good practice anyhow.

when startKey is unknown how to implement strategy for couchdb/pouchDb

Numerous readings indicate that skip should REALLY be avoided when doing pagination. As in the link most cite that using startKey and limit is the way to go. After I get the first page I know the startKey of that page, the lastKey of that page and the total entries. If I have a pagination control with page numbers as buttons and the user selects page 3, how do I get there? I have no idea what the startKey of page3. Perhaps, I do a simple view up front to go get the start keys for the each page.
This page nicely describes pagination:
http://docs.couchdb.org/en/1.6.1/couchapp/views/pagination.html
So, you can't really have a "Go to page 298", but to have links to the previous and next 5 pages, you can look up a larger number of preceeding and following documents and generate links accordingly. For example, if you have 10 posts per page, look up 50 following keys and take every 10th one.
As for making a "Go to page X", perhaps a background script that generates somesort of cache?

how can I clear or set values in a digestive-functors view

I have used postForm from Text.Digestive in digestive-functions which returns something like a (Just view, Nothing) in the case of a form error. I can then take the view and render it to HTML to display the form errors.
My issue is that I do not want every field to be rendered as given by the user. Specifically, my captcha value should be reset to a newly computed one rather than reusing the old one (which would give an infinite number of captcha attempts). But I do not know how to compute a new view with one field cleared/set, given the old view.
Alternatively, does anyone know if this would be easier with reform? I haven't looked at it much yet but it seems nice that it does away with the string-based-programming part of digestive-functors.
I repeated the question on the bug tracker where someone eventually came up with a working hack. The view value turns out the be a record. You can mutate its viewInput field before rendering it. viewInput is an association list from paths to forms.

Create domino view dynamically in XPages

I want to know if I can click a button in my XPage and dynamically create a Domino View and then show it in a panel control on the same page. The reason I want to do this is because I have a categorized view and I don't want to lose category data by using full text search. So I am thinking of creating a new view dynamically and pass my search parameters, like end date or start date, into the view selection formula.
Is it possible? Any other alternative solution is also welcome.
yes you can, but you don't want to. A Domino view takes space in the database and quite some time for its first use. So you end up with a lot of views taking space and the need to adjust database space after removal. Your response times will suck big time.
Categories as shown in Notes views are no web interaction pattern, so you might want to solve a problem that actually shouldn't exist.
The preferred method for Domino application is navigation / drill down over search. But you could do a FTSearch where you add your category to the search parameters and render your results in a repeat control instead of a view control. There you have more control over the look and feel.
Whether or not it's the best solution, the answer to the immediate question about creating a view on the fly is yes: the Database class has a couple "createView" methods to allow you to create a new view, either entirely from scratch or based on a named other view. From there, you can use the "setSelectionFormula" and "createColumn" methods in the created View to build what you want. You can't do EVERYTHING with those methods, but it may be enough.
One problem you'd likely run into is ACL access: you'll need Designer rights to the database, which a normal user most likely wouldn't have. If you use the sessionAsSigner object to fetch a signer version of the DB (say, "var signerDB = sessionAsSigner.getDatabase(database.getServer(), database.getFilePath())"), you can work from there. Off the top of my head, I don't remember if you will also have to up the "Maximum Internet access" setting on the last tab of the ACL to Designer as well, but you may.
I am assuming that you are referring to the problem that exists when you choose the documents based on the category. This is something that I find highly annoying and I wish that it was possible to turn this on and off. It makes sense for embedded views, but not for much else.
What I did to solve this was to include the category value in the next column. In this way that text could still be seen, even if it was a flat view.
Alternatively, you could also look into using a repeater control and create your own way of presenting the information. This would be used instead of a (Dynamic)ViewPanel control. You could then present the information any way you wanted as long as it is returned in the viewrow set.
Happy Programming!

WCF Data Service Paging Behavior

In my sample project, I set the entity page size to 20. Then I have an entity set with result count which is divisible to the page size. For example, the Categories set which has 100 items. When I go to:
http://localhost/Sample.svc/Categories?$skiptoken=80
I got 81st to 100th categories and the page has the "next" link
http://localhost/Sample.svc/Categories?$skiptoken=100
I tried to go to that page and it returns nothing.
What's the explanation for that?
The paging simply takes the next PageSize items. If it finds less than that, then it's clear there are no more items to return so you don't get the next link. If the query returns the requested number of items, the runtime doesn't try to figure out if this is the last page or not, it simply returns a next link. It might happen that such a link will return no results.
In fact the next link is not bound to return any results, but as long as the response constains another next link, there are potentially more results. The standard built in paging will return pages of the predefined size (except for the last one), but services are free to use any other kind of paging which might return different sizes for each page (including empty pages).
To directly answer your question "Why is the last page empty?":
The runtime doesn't "look ahead" so it can't tell if a given page is the last one except for when it gets less than the expected number of results. Looking ahead would be both costly (asking for more than necessary) and potentially wrong (what if the extra result causes an error...).

Resources