[SalesForce] Is it possible to use Remote Object jsNamespace with an apex:includeScript

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:

  // And, finally, run the page
jQuery(document).ready(function()
{
    vfStudyApp.table    = jQuery('#myTable tbody');
    vfStudyApp.template = jQuery('#template').html();

    Mustache.parse(vfStudyApp.template);

    // Bind application functions to UI events
    jQuery('#refreshButton').click(vfStudyApp.fetchContactArray);
    jQuery('#addButton').click(vfStudyApp.addContact);

    // Initial load of contacts list
    vfStudyApp.fetchContactArray();
});
Related Topic