Remote Objects provide a way to perform simple CRUD operations on objects from a Visualforce page via JavaScript. With the Winter ’15 release, Remote Objects are generally available (GA). Infinite scrolling refers to a pattern where the next set of data is loaded dynamically as the user scrolls down the page. This article shows how Remote Objects can be used to provide infinite scroll capabilities to a table of Contacts. All code is available on GitHub.
The Page
The page is a simple table of Contacts owned by the current user. As the user scrolls down the page, Contacts are dynamically added to the table after they are retrieved using Remote Objects.
Remote Object Definition
The Contact Remote Object model is defined in the <apex:remoteObjectModel>
standard component tag.
1 2 3 4 |
<apex:remoteObjects > <apex:remoteObjectModel jsShortHand="con" name="Contact" fields="Id,Name,Email,Phone,MobilePhone,Image_URL__c" retrieve="{!$RemoteAction.ContactsController.retrieveContacts}"/> </apex:remoteObjects> |
This defines the shorthand to be used in JavaScript, the fields that are available for use in Remote Object operations, and an override of the standard retrieve method. It is important to define all fields that can be used in a query and not just the ones that are being retrieved. For example, if you are sorting by or have a field in a where clause, but aren’t using it in the result, you still must list it in the fields attribute. If you do not, you will get an error when the retrieve operation executes.
Scroll Detection
There are many different infinite scroll plugins and different ways to achieve an infinite scroll without a plugin. This code uses some jQuery, but not a plugin to detect when the next set of data needs to be loaded.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var scrollListener = function() { $j(window).one("scroll", function() { if ($j(window).scrollTop() >= $j(document).height() - $j(window).height() - 100) { if (offsetVal < totalContacts) { loadRecords(); } } setTimeout(scrollListener, 200); }); }; $j(document).ready(function() { loadRecords(); scrollListener(); }); |
The JavaScript sets up an event listener that will be executed at most once (see jQuery.one) and after it executes it will set itself up to listen again, with a 200 ms delay. The delay and the usage of the one function are used for efficiency reasons, because the scroll event could be fired many times in rapid succession. The loadRecords
function is only called if the offset is still less than the total number of records. Note that the maximum offset allowed is 2,000.
Remote Object Retrieve
The loadRecords
function dynamically builds the criteria to retrieve the current chunk of records. Note that the offset
must be greater than 0 to be included in the criteria. If it is not greater than 0, an error will be generated. The records are sorted by Name ascending, and the chunk size, controlled by the limit portion of the clause, is 5.
1 2 3 4 5 6 7 8 9 |
var criteria = { limit: limitVal, orderby: [ {Name: "ASC"} ], }; // offsetVal must be at least 1 if (offsetVal >= 1) { criteria.offset = offsetVal; } |
The callback function iterates over the returned records, appending them to the table. For simplicity’s sake the callback just pops an alert if there is an error.
The retrieve method was overridden because the total number of possible Contacts was needed to know when to stop retrieving. The override in the controller simply queries for the total number of contacts owned by the current user and stores that number in the result.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@RemoteAction public static Map<String, Object> retrieveContacts(String type, List<String> fields, Map<String, Object> criteria) { // Retrieve using the standard retrieve Map<String, Object> result = RemoteObjectController.retrieve(type, fields, criteria); // Add in the total record count for the current user. Needed to know when to stop. Integer totalContacts = [ SELECT Count() FROM Contact WHERE OwnerId =: UserInfo.getUserId() ]; // Create a new map since the result map is read-only Map<String, Object> customResult = new Map<String, Object> {'totalContacts'=> totalContacts}; customResult.putAll(result); return customResult; } |
The JavaScript callback then assigns that number to the totalContacts
variable by retrieving it from the event.result
object.
1 2 3 4 5 6 7 8 9 10 11 |
con.retrieve(criteria, function(err, records, event) { if (err) { alert(err); } else { offsetVal += records.length; totalContacts = event.result.totalContacts; records.forEach(function(record) { addTableRow(record); }); } }); |
Conclusions
Remote Objects are useful in JavaScript based applications inside of Salesforce, but can also be leveraged in simple Visualforce pages to provide a simple way to access data. To learn more, consult the Visualforce Developer’s Guide which contains excellent documentation on Remote Objects.
All code is available on GitHub.
Fantastic Peter! Really like this.
Thanks Barry!
This is very cool. Thanks for sharing. Quick question: I stuck the page into an inline (embedded page) on the account layout. In this case the data is not rendered . Have to tied that?
I have not tried that. You might have to tweak what triggers the scrolling, though. The code in this post uses the window and document. You may need to detect when the scrolling needs to happen relative to a div instead. See the second part of this answer on Stack Overflow: (code snippet copied from answer below)
Go here for the greatest best anti aging products currently available anywhere plus reasonably priced.
I liked your idea peter but in the VF Page I am using in salesforce.I don’t Require any scrolling functionality all I ned is just to fetch all the rcords as they way they are and show it with help of jstree without any limitation in the number of records retrieved.
After going over a few of the articles on your
web page, I honestly appreciate your technique of blogging.
I saved it to my bookmark site list and will be checking back in the near future.
Please check out my web site as well and let me know what
you think.
I think everything published made a bunch of sense. But, what about this?
what if you added a little information? I am not saying your information isn’t solid., however suppose you added a post title
to possibly get folk’s attention? I mean Infinite Scroll
with Remote Objects – Peter Knolle is kinda
vanilla. You should peek at Yahoo’s front page and watch how they create news headlines to grab viewers interested.
You might add a related video or a pic or two to grab people excited about everything’ve got to
say. Just my opinion, it might make your posts a little livelier.