Newer Version Available

This content describes an older version of this product. View Latest

An Example of Using Remote Objects with jQuery Mobile

Visualforce Remote Objects is designed to “blend” well with JavaScript frameworks. This extended but simple example shows how to use Remote Objects with jQuery Mobile to view a list of contacts and to add, edit, and delete them.

This example uses jQuery Mobile from the Salesforce Mobile Packs and is based on sample code that is included with the Mobile Pack for jQuery. Remote Objects and jQuery Mobile make it easy to create a simple contact manager page for a phone.

A Simple Contact Editor with Remote Objects and jQuery Mobile

1<apex:page docType="html-5.0" showHeader="false" sidebar="false">          
2
3	<!-- Include jQuery and jQuery Mobile from the Mobile Pack -->
4    <apex:stylesheet value="{!URLFOR($Resource.MobilePack_jQuery, 
5         'jquery.mobile-1.3.0.min.css')}"/>
6    <apex:includeScript value="{!URLFOR($Resource.MobilePack_jQuery, 
7         'jquery-1.9.1.min.js')}"/>
8    <apex:includeScript value="{!URLFOR($Resource.MobilePack_jQuery, 
9         'jquery.mobile-1.3.0.min.js')}"/>
10
11    <!-- Remote Objects declaration -->
12    <apex:remoteObjects jsNamespace="RemoteObjectModel">
13        <apex:remoteObjectModel name="Contact" fields="Id,FirstName,LastName,Phone">
14            <!-- Notes is a custom field added to the Contact object -->
15            <apex:remoteObjectField name="Notes__c" jsShorthand="Notes"/>
16        </apex:remoteObjectModel>
17    </apex:remoteObjects>
18
19    <head>
20        <title>Contacts</title>
21        <meta name="viewport" 
22           content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
23        
24        <script type="text/javascript">
25            var $j = jQuery.noConflict(); 
26
27            // Config object with commonly used data
28            // This keeps some hard-coded HTML IDs out of the code
29            var Config = {
30                Selectors: {
31                    list: '#cList',
32                    detailFields: "#fName #lName #phone #notes #error #contactId".split(" ")
33                },
34                Data: {
35                    contact: 'contact'
36                }
37            };
38            
39            // Get all contacts, and display them in a list 
40            function getAllContacts() {
41                $j.mobile.showPageLoadingMsg();
42                
43                var c = new RemoteObjectModel.Contact();
44                // Use the 'limit' operator to increase the default limit of 20
45                c.retrieve({ limit: 100 }, function (err, records) { 
46                    // Handle any errors
47                    if (err) { 
48                        displayError(err); 
49                    } else {
50                        // Empty the current list
51                        var list = $j(Config.Selectors.list).empty();
52                        // Now add results records to list
53                        $j.each(records, function() {
54                            var newLink = $j('<a></a>', {
55                                text: this.get('FirstName') + ' ' + this.get('LastName')
56                            });
57                            newLink.data(Config.Data.contact, this.get('Id'));
58                            newLink.appendTo(list).wrap('<li></li>');
59                        });
60                        
61                        $j.mobile.hidePageLoadingMsg();
62                        list.listview('refresh');
63                    }
64                });
65            }
66
67            // Handle the Save button that appears on both
68            // the Edit Contact and New Contact pages
69            function addUpdateContact(e){
70                e.preventDefault();
71
72                var record = new RemoteObjectModel.Contact({
73                    FirstName: $j('#fName').val(),
74                    LastName: $j('#lName').val(),
75                    Phone: $j('#phone').val(),
76                    Notes: $j('#notes').val() 
77                    // Note use of shortcut 'Notes' in place of Notes__c
78                });
79
80                var cId = $j('#contactId').val();
81                if( !cId ) { // new record
82                    record.create(updateCallback);
83                } else { // update existing
84                    record.set('Id', cId);
85                    record.update(updateCallback);
86                }
87            }
88            
89            // Handle the delete button
90            function deleteContact(e){
91                e.preventDefault();
92                var ct = new RemoteObjectModel.Contact();
93                ct.del($j('#contactId').val(), updateCallback);
94            }
95            
96            // Callback to handle DML Remote Objects calls
97            function updateCallback(err, ids){
98                if (err) { 
99                    displayError(err); 
100                } else {
101                    // Reload the contacts with current list
102                    getAllContacts();
103                    $j.mobile.changePage('#listpage', {changeHash: true});
104                }
105            }
106
107            // Utility function to log and display any errors
108            function displayError(e){
109                console && console.log(e);
110                $j('#error').html(e.message);
111            }
112        
113            // Attach functions to the buttons that trigger them
114            function regBtnClickHandlers() {
115                $j('#add').click(function(e) {
116                    e.preventDefault();
117                    $j.mobile.showPageLoadingMsg();
118                    
119                    // empty all the clic handlers                    
120                    $j.each(Config.Selectors.detailFields, function(i, field) {
121                        $j(field).val('');
122                    });
123                    
124                    $j.mobile.changePage('#detailpage', {changeHash: true});
125                    $j.mobile.hidePageLoadingMsg();
126                });
127        
128                $j('#save').click(function(e) {
129                   addUpdateContact(e);
130                });
131        
132                $j('#delete').click(function(e) {
133                   deleteContact(e);
134                });
135            }
136
137            // Shows the contact detail view, 
138            // including filling in form fields with current data
139            function showDetailView(contact) {
140                $j('#contactId').val(contact.get('Id'));
141                $j('#fName').val(contact.get('FirstName'));
142                $j('#lName').val(contact.get('LastName'));
143                $j('#phone').val(contact.get('Phone'));
144                $j('#notes').val(contact.get('Notes'));
145                $j('#error').html('');
146                $j.mobile.changePage('#detailpage', {changeHash: true});
147            }
148
149            // Register click handler for list view clicks
150            // Note: One click handler handles the whole list	
151            function regListViewClickHandler() {
152                $j(Config.Selectors.list).on('click', 'li', function(e) {
153
154                    // show loading message
155                    $j.mobile.showPageLoadingMsg();
156
157                    // get the contact data for item clicked
158                    var id = $j(e.target).data(Config.Data.contact);
159
160                    // retrieve latest details for this contact
161                    var c = new RemoteObjectModel.Contact();
162                    c.retrieve({ 
163                        where: { Id: { eq: id } } 
164                    }, function(err, records) { 
165                        if(err) { 
166                            displayError(err); 
167                        } else {
168                            showDetailView(records[0]);
169                        }
170
171                        // hide the loading message in either case
172                        $j.mobile.hidePageLoadingMsg();
173                    });
174                });
175            }
176
177            // And, finally, run the page
178            $j(document).ready(function() {
179                regBtnClickHandlers();
180                regListViewClickHandler();
181                getAllContacts();
182            });
183
184        </script>    
185    </head>
186
187    <!-- HTML and jQuery Mobile markup for the list and detail screens -->
188    <body>    
189    
190        <!-- This div is the list "page" -->
191        <div data-role="page" data-theme="b" id="listpage">                
192            <div data-role="header" data-position="fixed">
193                <h2>Contacts</h2>
194                <a href='#' id="add" class='ui-btn-right' data-icon='add' 
195                   data-theme="b">Add</a>
196            </div>
197            <div data-role="content" id="contactList">            
198                <ul id="cList" data-filter="true" data-inset="true" 
199                    data-role="listview" data-theme="c" data-dividertheme="b">
200                </ul>
201            </div>
202        </div>
203                
204        <!-- This div is the detail "page" -->
205        <div data-role="page" data-theme="b" id="detailpage">
206            <div data-role="header" data-position="fixed">
207                <a href='#listpage' id="back2ContactList" class='ui-btn-left' 
208                   data-icon='arrow-l' data-direction="reverse" 
209                   data-transition="flip">Back</a>
210                <h1>Contact Details</h1>
211            </div>
212            <div data-role="content">
213                <div data-role="fieldcontain">
214                    <label for="fName">First Name:</label>
215                    <input name="fName" id="fName" />
216                </div>
217                <div data-role="fieldcontain">
218                    <label for="lName">Last Name:</label>
219                    <input name="lName" id="lName" />
220                </div>
221                <div data-role="fieldcontain">
222                    <label for="phone">Phone:</label>
223                    <input name="phone" id="phone"/>
224                </div>
225                <div data-role="fieldcontain">
226                    <label for="notes">Notes:</label>
227                    <textarea name="notes" id="notes"/>
228                </div>
229
230                <h2 style="color:red" id="error"></h2>
231
232                <input type="hidden" id="contactId" />
233                <button id="save" data-role="button" data-icon="check" 
234                    data-inline="true" data-theme="b" class="save">Save</button>
235                <button id="delete" data-role="button" data-icon="delete" 
236                    data-inline="true" class="destroy">Delete</button>
237            </div>
238        </div>
239    </body>
240</apex:page>
Note that although all four Remote Objects operations are demonstrated, there are only three callback handlers.
  • getAllContacts() calls retrieve() to load a list of contacts and provides an anonymous function for the callback. The callback checks for errors and then iterates through the results, adding them to the page.
  • Similarly, showDetailView() calls retrieve() to load a single contact for the detail page, and the results are also handled by an anonymous function.
  • addUpdateContact() and deleteContact() handle adding, updating, and deleting contacts. Both methods pass updateCallback() as the callback function. updateCallback() doesn’t use the results of the Remote Objects operation. It only checks for errors, logs them to the console, and then calls getAllContacts() to refresh the page.