Newer Version Available
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.