Currently playing with An Example of Using Remote Method Overrides in Remote Objects
My VisualForce Page includes the following
<apex:remoteObjects jsNamespace="$RemoteContactModel">
<apex:remoteObjectModel name="Contact" fields="FirstName,LastName,Phone" create="{!$RemoteAction.OverrideRemoteContactObjectVFCtrl.create}" />
</apex:remoteObjects>I
If I embed my JavaScript between <script>
and </script>
tags, my script works perfectly.
BUT if instead, I put the EXACT same JavaScript into a static resource and call it with
<apex:includeScript value="{!$Resource.VF_OverrideRemoteContactObjectJS}"/>
The scripts do NOT execute, and instead my Console Log reveals:
ReferenceError: $RemoteContactModel is not defined
Is there some reason that an off-page, included static-resource/script should behave differently?
Is there some way I can fix this?
As it seems the specific contents of the script may be relevant, the entire working VisualForce page, including the script, now follows:
<apex:page controller="OverrideRemoteContactObjectVFCtrl" showHeader="false" standardStylesheets="false" docType="html-5.0" title="Contacts--Remote Objects Style">
<!-- Include in some mobile web libraries -->
<apex:stylesheet value="{!$Resource.bootstrap}" />
<apex:includeScript value="{!$Resource.jquery}" />
<apex:includeScript value="//cdnjs.cloudflare.com/ajax/libs/mustache.js/0.7.2/mustache.min.js" />
<!-- Setup Remote Objects, with an override for create() method -->
<apex:remoteObjects jsNamespace="$RemoteContactModel">
<apex:remoteObjectModel name="Contact" fields="FirstName,LastName,Phone" create="{!$RemoteAction.OverrideRemoteContactObjectVFCtrl.create}" />
</apex:remoteObjects>I
<!-- Page markup-->
<div class="container">
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-8">
<table id="myTable" class="table table-bordered table-striped table-condensed">
<colgroup>
<col class="col-md-3" />
<col class="col-md-3" />
<col class="col-md-3" />
</colgroup>
<caption>
Contact Data Order ([ {LastName: 'ASC'}, {FirstName: 'ASC'} ])
<button id="refreshButton" class="btn btn-success btn-sm" type="button">Refresh</button>
</caption>
<caption id="messageBox" class="alert alert-danger hidden"></caption>
<thead>
<tr><th>LastName</th><th>FirstName</th><th>Phone</th></tr>
</thead>
<tbody></tbody>
<tfoot>
<tr>
<td><input type="text" name="FirstName" id="inputFirstName" placeholder="John" class="form-control" /></td>
<td><input type="text" name="LastName" id="inputLastName" placeholder="Doe" class="form-control" /></td>
<td>
<div class="input-group">
<input type="text" name="Phone" id="inputPhone" placeholder="(123) 456-7890" class="form-control" />
<span class="input-group-btn">
<button id="addButton" class="btn btn-primary" type="button">Save</button>
</span>
</div>
</td>
</tr>
</tfoot>
</table>
<div class="panel panel-default">
<div class="panel-heading">Log</div>
<div class="panel-body" id="log"></div>
</div>
</div>
<div class="col-md-2"></div>
</div>
</div>
<!-- Results template (table rows of Contacts) -->
<!-- apex:includeScript value="{!$Resource.VF_OverrideRemoteContactObjectMustache}" id="template" html-type="x-tmpl-mustache" /> -->
<script id="template" type="x-tmpl-mustache">
<tr><td>{{LastName}}</td><td>{{FirstName}}</td><td>{{Phone}}</td></tr>
</script>
<!-- Page Functionality -->
<!-- See https://developer.salesforce.com/docs/atlas.en-us.pages.meta/pages/pages_remote_objects_example_overrides.htm -->
<apex:includeScript value="{!$Resource.VF_OverrideRemoteContactObjectJS}"/>
<script>
jQuery.noConflict();
var vfStudyApp = vfStudyApp || {};
vfStudyApp.table = jQuery('#myTable tbody');
vfStudyApp.template = jQuery('#template').html();
Mustache.parse(vfStudyApp.template);
// -----------------------------------------------------------------------------------------------------------------
// Retrieve all contacts and add to results table on page
vfStudyApp.fetchContactArray = function()
{
retrieveOptions = { orderby: [ {LastName: 'ASC'} , {FirstName: 'DESC'} ] };
(new $RemoteContactModel.Contact()).retrieve(retrieveOptions, vfStudyApp.retrieveContactCallback);
}
vfStudyApp.retrieveContactCallback = function(error, contactRecordArray)
{
if (error) { vfStudyApp.displayError(error) }
else
{
vfStudyApp.addStatusMessageToLogPanel('Fetched contact records.'); // Add some status messages to the log panel
vfStudyApp.addStatusMessageToLogPanel('Records Size: ' + contactRecordArray.length + '!');
vfStudyApp.updateContactTable(contactRecordArray); // Update the table of contacts with fresh results
}
}
vfStudyApp.updateContactTable = function(contactRecordArray)
{
vfStudyApp.table.empty();
contactRecordArray.forEach(vfStudyApp.appendContact);
}
vfStudyApp.appendContact = function(contactRecord)
{
vfStudyApp.table.append(Mustache.render(vfStudyApp.template, contactRecord._props));
}
// -----------------------------------------------------------------------------------------------------------------
vfStudyApp.createNewContactFromForm = function()
{
return new $RemoteContactModel.Contact
({
FirstName : jQuery('#inputFirstName').val(),
LastName : jQuery('#inputLastName').val(),
Phone : jQuery('#inputPhone').val()
});
}
vfStudyApp.addContact = function()
{
newContact = vfStudyApp.createNewContactFromForm();
newContact.create(vfStudyApp.createContactCallback);
}
vfStudyApp.createContactCallback = function(error, contactRecord, event)
{
if (error) { vfStudyApp.displayError(error) }
else
{
vfStudyApp.resetNewContactForm(); // Reset the New Record form fields, for next create
vfStudyApp.addStatusMessageToLogPanel('Contact created.'); // Add some status messages to the log panel
vfStudyApp.addStatusMessageToLogPanel('Got custom data: ' + event.result.custom); // Custom data added to event.result by override function
vfStudyApp.fetchContactArray(); // Redraw the results list with current contacts
}
}
vfStudyApp.resetNewContactForm = function()
{
jQuery('input').each
(function(){
jQuery(this).val('');
});
}
// -----------------------------------------------------------------------------------------------------------------
vfStudyApp.displayError = function(error)
{
jQuery('#messageBox').text(error.message).removeClass('hidden');
}
vfStudyApp.addStatusMessageToLogPanel = function(statusMessage)
{
jQuery('#log').append('<p>'+statusMessage+'</p>');
}
// -----------------------------------------------------------------------------------------------------------------
// And, finally, run the page
jQuery(document).ready(function()
{
// Bind application functions to UI events
jQuery('#refreshButton').click(vfStudyApp.fetchContactArray);
jQuery('#addButton').click(vfStudyApp.addContact);
// Initial load of contacts list
vfStudyApp.fetchContactArray();
});
</script>
</apex:page>
Best Answer
Thanks to @Uwe Heim, I came to the realization that the external JavaScript was prematurely trying to access first the Remote Object and then the Mustache.js Template
I was able to solve the problem by telling JavaScript to wait until both these elements were ready with: