5

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>
4

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();
});
2

Yes it's possible.

The issue is probably that you put the JS code into the Resource without wrapping it into a function. If you now include the Resource typically topmost in your page, the JS will be executed immediately, without the Saleforce JS created by <apex:remoteObjects> has a chance to execute. And since this creates your model variable, the earlier call has to fail.

So in my example, it actually worked fine to put the JS into a Static Resource.

Assume you have a TestPage.page with the following content:

<apex:page >
    <h1>TestPage</h1>
    <apex:remoteObjects jsNamespace="rm">
        <apex:remoteObjectModel name="Contact" fields="FirstName,LastName,Phone" />
    </apex:remoteObjects>   
    <script>
        var ct = new rm.Contact({ 
            FirstName: "Aldo",
            LastName: "Michaels", 
            Phone: "(415) 555-1212"
        });     
        var cts = new rm.Contact();
        cts.retrieve({ limit: 10 }, function(error, records, event){ 
            console.log(records);
        });
    </script>
</apex:page>

This works as we all agree fine!

Now let's create a Static Resource called TestPageJs.resource containing this:

function go() {
    var ct = new rm.Contact({ 
        FirstName: "Aldo",
        LastName: "Michaels", 
        Phone: "(415) 555-1212"
    });     
    var cts = new rm.Contact();
    cts.retrieve({ limit: 10 }, function(error, records, event){ 
        console.log(records);
    });
}

And change the page to that:

<apex:page >
    <apex:includeScript value="{!$Resource.TestPageJs}"/>
    <h1>TestPage</h1>
    <apex:remoteObjects jsNamespace="rm">
        <apex:remoteObjectModel name="Contact" fields="FirstName,LastName,Phone" />
    </apex:remoteObjects>   
    <script>
        // var ct = new rm.Contact({ 
        //     FirstName: "Aldo",
        //     LastName: "Michaels", 
        //     Phone: "(415) 555-1212"
        // });      
        // var cts = new rm.Contact();
        // cts.retrieve({ limit: 10 }, function(error, records, event){ 
        //  console.log(records);
        // });
    </script>
</apex:page>

If you now type go() in your JS console, it works again. You can put your go() invocation also back into the page. Everywhere below your <apex:remoteObjects> should work fine.

  • Cheers for your speculation, but both references (there are two) are within functions... I'll add the entire working page above so you can see the script in question. – Brian Kessler Nov 27 '14 at 14:51
  • Please cross check my example. It'll work for sure. Let me see if I can figure out your issue later today. Finally it should work too. – Uwe Heim Nov 27 '14 at 14:55
  • I've managed SOME improvement by enclosing the last several lines within jQuery(document).ready(function(){ /* code here */ }); ... The error no longer appears. But the table no longer displays. – Brian Kessler Nov 27 '14 at 15:16
  • I'm just on the run having only my phone with me. Later I'll try to look at your code. In the meantime try to simplify your code. Remote Objects are most likely not the reason for your problems at all. Suspect other stuff too. – Uwe Heim Nov 27 '14 at 15:21
  • I figured out the rest of the problem... Posting answer presently. Cheers for the help. – Brian Kessler Nov 27 '14 at 15:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.