Visualforce 远程对象

JavaScript 远程处理是一种常用、功能强大且高效的 Web 应用程序构建方法 使用 Visualforce,特别是用于创建用于 Salesforce 移动应用程序或工作的页面 使用 JavaScript 库,例如 jQuery 或 AngularJS。Visualforce 远程 对象是启用 直接从 JavaScript 对 sObject 执行基本 DML 操作。远程 对象消除了一些复杂性 通过减少对 Apex 控制器或扩展中方法的需求,从 JavaScript 远程处理。

@RemoteAction

幕后,远程 对象控制器处理共享规则、字段级安全性和其他数据可访问性 关注。使用远程的页面 对象受所有标准 Visualforce 限制的约束,但与 JavaScript 远程处理、远程处理一样 对象调用不计算在内 达到 API 请求限制。使用 Visualforce Remote 对象包括在同一页面上实现两个单独的功能。

  1. 访问定义,使用 Visualforce 和 Remote 编写 对象组件。这些组件会生成一组 JavaScript 代理对象,您可以 在步骤 2 中使用。
  2. 数据访问函数,用 JavaScript 编写。这些函数使用代理对象 由访问定义提供,以执行创建、检索、更新和 对数据执行删除操作。

然后,您的页面使用数据访问函数来响应用户交互,例如 表单提交或控制更改,或执行定期操作以响应计时器,或 你可以用 JavaScript 编写的大多数内容。

远程的简单示例 对象

这个简短的示例演示了您需要实现的两个功能 使用远程对象。此 Visualforce 页面检索一个 列出 10 条仓库记录,并在页面上显示它们以响应用户 单击〖检索仓库〗按钮。

<apex:page>
    
    <!-- Remote Objects definition to set accessible sObjects and fields -->
    <apex:remoteObjects >
        <apex:remoteObjectModel name="Warehouse__c" jsShorthand="Warehouse" 
            fields="Name,Id">
            <apex:remoteObjectField name="Phone__c" jsShorthand="Phone"/>
        </apex:remoteObjectModel>
    </apex:remoteObjects>

    <!-- JavaScript to make Remote Objects calls -->
    <script>
        var fetchWarehouses = function(){
            // Create a new Remote Object
            var wh = new SObjectModel.Warehouse();
            
            // Use the Remote Object to query for 10 warehouse records
            wh.retrieve({ limit: 10 }, function(err, records, event){
                if(err) {
                    alert(err.message);
                }
                else {
                    var ul = document.getElementById("warehousesList");
                    records.forEach(function(record) {
                        // Build the text for a warehouse line item
                        var whText = record.get("Name");
                        whText += " -- ";
                        whText += record.get("Phone");
                        
                        // Add the line item to the warehouses list
                        var li = document.createElement("li");
                        li.appendChild(document.createTextNode(whText));
                        ul.appendChild(li);
                    });
                }
            });
        };
    </script>

    <h1>Retrieve Warehouses via Remote Objects</h1>

    <p>Warehouses:</p>

    <ul id="warehousesList">
    </ul>
    <button onclick="fetchWarehouses()">Retrieve Warehouses</button>

</apex:page>

通知 此页面有些不寻常 – 没有控制器或控制器扩展。 所有数据访问均由远程处理 对象组件。此示例的第一部分是远程 对象组件,用于指定要在 页。

<apex:remoteObjects >
    <apex:remoteObjectModel name="Warehouse__c" jsShorthand="Warehouse" fields="Name,Id">
        <apex:remoteObjectField name="Phone__c" jsShorthand="Phone"/>
    </apex:remoteObjectModel>
</apex:remoteObjects>

这些 组件生成 JavaScript 模型类,访问中的每个 sObject 一个 规范,用于直接从 JavaScript 进行数据访问调用 法典。请注意该属性的使用,该属性将完整的 Salesforce API 名称映射到 在 JavaScript 代码中使用的更简单、更短的名称。如果您打算打包和 分发代码,设置为 必不可少,因为它消除了在 打包的代码。使用速记可以完成所有工作。

jsShorthandjsShorthand此示例的第二部分是一个 JavaScript 函数,该函数使用以下模型 由访问定义组件生成,用于检索一组要显示的记录 在 页。

<!-- JavaScript to make Remote Objects calls -->
<script>
    var fetchWarehouses = function(){
        // Create a new Remote Object
        var wh = new SObjectModel.Warehouse();
        
        // Use the Remote Object to query for 10 warehouse records
        wh.retrieve({ limit: 10 }, function(err, records, event){
            if(err) {
                alert(err.message);
            }
            else {
                var ul = document.getElementById("warehousesList");
                records.forEach(function(record) {
                    // Build the text for a warehouse line item
                    var whText = record.get("Name");
                    whText += " -- ";
                    whText += record.get("Phone");
                    
                    // Add the line item to the warehouses list
                    var li = document.createElement("li");
                    li.appendChild(document.createTextNode(whText));
                    ul.appendChild(li);
                });
            }
        });
    };
</script>

这 函数的第一行从模型创建一个 Warehouse 对象。请注意, 创建它的调用使用 for sObject 而不是对象的完整 API 名称。遵循此最佳实践 将 JavaScript 代码与组织命名空间的细节分离, sObject 和字段名称等,并使代码更加简洁明了。

jsShorthand

第二行使用新的 Warehouse 对象 来执行对 Warehouse 记录的查询。该调用提供两个 参数:一个简单的查询说明符和一个用于处理结果的匿名函数。这 函数是标准的 JavaScript。它循环访问结果并创建列表项以 追加到页面上的仓库列表。wh页面正文是静态的 HTML。

<h1>Retrieve Warehouses via Remote Objects</h1>

<p>Warehouses:</p>

<ul id="warehousesList">
</ul>
<button onclick="fetchWarehouses()">Retrieve Warehouses</button>

你 代码将结果添加到列表中。 加载页面时,列表为空。单击该按钮将触发 JavaScript 之前定义的函数,用于执行查询并添加结果。

warehousesList

使用远程 JavaScript 中的对象

远程生成的 JavaScript 模型 对象组件提供了一个 JavaScript API,用于为应用创建读取和保存的函数 值返回给 Salesforce。使用组件创建的基本模型进行实例化 相应 sObject 的特定模型。然后使用特定模型执行操作 在他们的 sObject 上,例如检索、创建、更新和删除。

<apex:remoteObjects>远程的基本型号 对象由组件创建。基本模型为 Remote 提供了一个伪命名空间 使用它创建的对象。默认情况下,基本模型是 命名的,但您可以使用该属性来设置名称。使用不同的底座 对相关远程进行分组的模型 沿功能线或包装线的对象。为 例:

<apex:remoteObjects>SObjectModeljsNamespace

<apex:remoteObjects jsNamespace="MyCorpModels">
    <apex:remoteObjectModel name="Contact" fields="FirstName,LastName"/>
</apex:remoteObjects>
<apex:remoteObjects jsNamespace="TrackerModels">
    <apex:remoteObjectModel name="Shipment__c" fields="Id,TrackNum__c"/>
</apex:remoteObjects>

特定型号

您通常不会自己创建基础模型,而是使用生成的 基础模型作为创建特定模型的工厂。例如,使用上面的 声明,在 JavaScript 中实例化一个 Contact 模型,例如 这:

var ct = new MyCorpModels.Contact();

注意 这是一个 JavaScript 模型 联系人对象,而不是特定的联系人记录。

ct

ct表示一个特定的对象,并提供 页面的 JavaScript 和 Salesforce 服务。 可用于执行基本的“CRUD” 对 Contact 对象执行的操作(创建、读取、更新和删除)在 数据库。Contactct在以下各节中,示例基于以下远程 对象声明,它使用所有三个 Remote 对象组件,并演示如何添加自定义字段、Notes__c、 带有“简写”名称,以便在 JavaScript 中访问它 自然的。

<apex:remoteObjects jsNamespace="RemoteObjectModel">
    <apex:remoteObjectModel name="Contact" fields="Id,FirstName,LastName,Phone">
        <apex:remoteObjectField name="Notes__c" jsShorthand="Notes"/>
    </apex:remoteObjectModel>
</apex:remoteObjects>

这 声明使您能够访问联系人记录上的五个字段。

实例化模型和访问字段

实例化设置了或未设置字段值的模型,具体取决于您的意图。 通常,当您想要将更改写入数据库时,您将设置字段 并在您只是阅读时省略字段。字段值是通过传入来设置的 一个 JSON 字符串,其中包含要在新模型上设置的字段的值。若要创建未设置字段的模型,请使用空参数创建该模型 列表。

var ct = new RemoteObjectModel.Contact();

要实例化设置了字段的模型,通常要创建新记录,请传入 包含字段名称和值对的对象。为 例:

var ct = new RemoteObjectModel.Contact({ 
    FirstName: "Aldo",
    LastName: "Michaels", 
    Phone: "(415) 555-1212"
});

远程 对象模型使用基本 和 方法来检索和设置字段值。 为 例:

get()set()

var ct = new RemoteObjectModel.Contact({ FirstName: "Aldo" });
ct.get('FirstName');  // 'Aldo'
ct.get('Phone'); // <undefined>
ct.set('FirstName', 'Benedict');
ct.set('Phone', '(415) 555-1212');

有 使用属性列表中的属性列表设置字段值之间没有功能差异 构造函数并使用 设置字段值。

set()

使用 Remote 创建记录 对象

通过调用 Remote 创建记录 对象模型实例。

create()

create()接受两个 参数,两者都是可选的。

RemoteObjectModel.create({field_values}, callback_function)

块 使您能够在一个语句中定义和创建记录。设置字段 值,就像使用 JSON 字符串创建模型时一样。为 例如,以下两个调用是等效的。

field_valuescreate()

var ctDetails = { FirstName: 'Marc', LastName: 'Benioff' };

// Call create() on an existing Contact model, with no arguments
var ct = new RemoteObjectModel.Contact(ctDetails);
ct.create();

// Call create() on an empty Contact model, passing in field values
var ct = new RemoteObjectModel.Contact();
ct.create(ctDetails);

create()不 直接返回结果。回调函数使您能够处理 异步服务器响应。

注意

所有服务器操作 使用远程 对象是异步执行的。任何依赖于 正在完成的请求(包括处理返回的结果)必须 放置在回调函数中。你的回调函数 最多可以接受三个参数。

function callback(Error error, Array results, Object event) { // ... }

查看远程 对象回调函数,了解有关编写 Remote 的详细信息 对象回调函数。该字段已设置为 远程对象作为成功调用的一部分。您可以访问此 字段。

Idcreate()

var ctDetails = { FirstName: 'Marc', LastName: 'Benioff' };
var ct = new RemoteObjectModel.Contact();
ct.create(ctDetails, function(err) {
    if(err) { 
        console.log(err);
        alert(err.message);
    }
    else {
        // this is the contact
        console.log(ct.log());     // Dump contact to log
        console.log(ct.get('Id')); // Id is set when create completes
    }
});

注意该函数的使用;它相当于 Remote 对象。

log()toString()

使用远程检索记录 对象

通过调用远程来检索记录 对象模型实例。

retrieve()

retrieve()需要两个参数,一个用于查询 条件,一个用于回调 处理器。

RemoteObjectModel.retrieve({criteria}, callback_function)

criteria可以是远程对象查询对象或返回对象的函数。以下两个调用是 等效。

var ct = new RemoteObjectModel();

// Empty callback functions for simplicity
ct.retrieve({where: {FirstName: {eq: 'Marc' }}}, function() {}); // query object

ct.retrieve(function(){
	return({where: {FirstName: {eq: 'Marc' }}});
}, function() {}); // function returning query object

请参阅远程的格式和选项 对象查询条件,用于查询对象的说明。

retrieve()不返回结果 径直。回调函数使您能够处理服务器响应 异步。

注意

所有服务器操作 使用远程 对象是异步执行的。任何依赖于 正在完成的请求(包括处理返回的结果)必须 放置在回调函数中。你的回调函数 最多可以接受三个参数。

function callback(Error error, Array results, Object event) { // ... }

查看远程 对象回调函数,了解有关编写 Remote 的详细信息 对象回调函数。

若要使用日期检索记录,请将 JavaScript date 对象传入查询。

var myDate = new Date('2017-01-20');
ct.retrieve({where: {CloseDate: {eq: myDate}}}, function() {});

使用远程更新记录 对象

通过调用远程来更新记录 对象模型实例。

update()

update()接受三个 参数,都是可选的,并且可以在 同时,具体取决于您提供的参数。

RemoteObjectModel.update([record_ids], {field_values}, callback_function)

record_ids是一个字符串数组,其中字符串是 要更新的记录数。如果这 参数,则在 使用远程对象实例。更新记录的最简单方法是调用 本身。

IdIdupdate()

ctDetails = {FirstName: "Marc", LastName: "Benioff"};
ct = new RemoteObjectModel.Contact(ctDetails);
ct.create();

// Later, in response to a page event...
ct.set('Phone', '555-1212');
ct.update();

更常见的是,您可能需要更新记录以响应表单提交。更新 记录可以像读取一些表单值一样简单,包括记录的 ,并将这些值传递给 。为 例:

Idupdate()

var record = new RemoteObjectModel.Contact();
record.update($j('#contactId').val(),
{
    FirstName: $j('#fName').val(),
    LastName: $j('#lName').val(),
    Phone: $j('#phone').val(),
    Notes: $j('#notes').val() 
});

可靠的代码包括用于处理错误的回调。以下 代码的完成方式与上一个示例相同,但已更改为使用 事件处理程序和回调函数。

// Handle the Save button
function updateContact(e){
    e.preventDefault();

    var record = new RemoteObjectModel.Contact({
        Id: $jQuery('#contactId').val(),
        FirstName: $jQuery('#fName').val(),
        LastName: $jQuery('#lName').val(),
        Phone: $jQuery('#phone').val(),
        Notes: $jQuery('#notes').val() 
    });
    record.update(updateCallback);
}

// Callback to handle DML Remote Objects calls
function updateCallback(err, ids){
    if (err) { 
        displayError(err); 
    } else {
        // Reload the contacts with current list
        getAllContacts();
        $jQuery.mobile.changePage('#listpage', {changeHash: true});
    }
}

您可以同时更新多条记录,只要更新 要执行的是统一的,即每条记录都相同。为 例如,您可能需要从 列表,将状态字段更改为“已存档”或 当前时间戳。若要更新一个请求中的记录,请传递数组 的 s 到 .要更新的字段 可以设置为远程对象模型本身的一部分,但它是 将它们直接传递给 更安全,如下所示:

Idupdate()update()

var ct = new RemoteObjectModel.Contact();
ct.update(
    ['003xxxxxxxxxxxxxxx', '003xxxxxxxxxxxxxxx'], 
    { FirstName: "George", LastName: "Foreman" },
    function(err, ids) {
        if (err) { 
            displayError(err); 
        } else {
            // Reload the contacts with current list
            getAllContacts();
            $jQuery('#status').html(ids.length + ' record(s) updated.');
            $jQuery.mobile.changePage('#listpage', {changeHash: true});
        }
});

注意

当您以这种方式更新多条记录时,所有 的记录在同一服务器端事务中更新。

使用远程更新插入记录 对象

通过调用远程来保存记录 对象模型实例。

upsert()

upsert()是一种便利 函数,如果记录存在,则更新记录,如果不存在,则创建记录。 在幕后委托给 或 .用于为 不受记录影响的页面或应用程序 来自新的输入表单或编辑记录页面。upsert()create()update()upsert()

upsert()接受两个参数, 两者都是可选的。

RemoteObjectModel.upsert({field_values}, callback_function)

块 使您能够设置值并将记录保存在一个语句中。 像创建模型时一样,使用 JSON 字符串设置字段值。 例如,以下两个调用是等效的。

field_valuesupsert()

// Call upsert() on a Contact model, with no arguments
// ct is a RemoteObjectModel.Contact that already has data
ct.set('Phone', '(415) 777-1212');
ct.upsert();

// Call upsert() on a Contact model, passing in field values
// ct is a RemoteObjectModel.Contact that already has data
ct.upsert({Phone: '(415) 777-1212'});

在前面的示例中,不清楚联系人是否 存在于数据库中,或者如果它是新联系人,则 来自输入表单。 处理细节。如果在联系人上设置了字段,则 联系方式将更新。如果没有,则创建一个新联系人。upsert()IdId

upsert()不 直接返回结果。回调函数使您能够处理 异步服务器响应。

注意

所有服务器操作 使用远程 对象是异步执行的。任何依赖于 正在完成的请求(包括处理返回的结果)必须 放置在回调函数中。你的回调函数 最多可以接受三个参数。

function callback(Error error, Array results, Object event) { // ... }

查看远程 对象回调函数,了解有关编写 Remote 的详细信息 对象回调函数。

使用 Remote 删除记录 对象

通过调用远程删除记录 对象模型实例。

del()

del()接受两个参数, 两者都是可选的,并且可以删除一条或多条记录,具体取决于 您提供的参数。

注意

为什么代替 ? 是保留字 在 JavaScript 中。del()delete()delete

RemoteObjectModel.del([record_ids], callback_function)

record_ids是一个字符串数组,其中字符串是 要删除的记录的 S。如果这 参数,则在 使用远程对象实例。删除记录的最简单方法是调用 本身。

IdIddel()

ctDetails = {FirstName: "Tobe", LastName: "Ornottobe"};
ct = new RemoteObjectModel.Contact(ctDetails);
ct.create();

// After some thought, and the async operation completes...
// It's not to be; delete the contact
ct.del();

更常见的是,您可能需要删除记录以响应 按钮单击。删除记录就像从页面中获取记录然后传递一样简单 的 到 .例如:

IdIddel()

var id = $jQuery('#contactId').val();
var ct = new RemoteObjectModel.Contact();
ct.del(id);

可靠的代码包括用于处理错误的回调。以下 代码的完成方式与上一个示例相同,但已更改为使用 事件处理程序和回调函数。

// Handle the delete button click
function deleteContact(e){
    e.preventDefault();
    var ct = new RemoteObjectModel.Contact();
    ct.del($jQuery('#contactId').val(), updateCallback);
}

// Callback to handle DML Remote Objects calls
function updateCallback(err, ids){
    if (err) { 
        displayError(err); 
    } else {
        // Reload the contacts with current list
        getAllContacts();
        $jQuery.mobile.changePage('#listpage', {changeHash: true});
    }
}

要删除一个请求中的多条记录,例如,选中 列表中的项目 – 将 s 数组传递给 。

Iddel()

var ct = new RemoteObjectModel.Contact();
ct.del(['003xxxxxxxxxxxxxxx', '003xxxxxxxxxxxxxxx'], function(err, ids) {
    if (err) { 
        displayError(err); 
    } else {
        // Reload the contacts with current list
        getAllContacts();
        $jQuery('#status').html(ids.length + ' record(s) deleted.');
        $jQuery.mobile.changePage('#listpage', {changeHash: true});
    }
});

注意

当您以这种方式删除多条记录时,所有 的记录将在同一服务器端事务中删除。

远程的格式和选项 对象查询条件

远程 对象使用对象来指定操作条件。使用此对象 指定查询的位置、限制和偏移条件。

retrieve()查询对象的结构化格式使 Visualforce 能够验证 标准,减少运行时错误的可能性。格式为 简单。

<apex:remoteObjectsjsNamespace="RemoteObjectModel">    
    <apex:remoteObjectModel name="Contact" fields="FirstName,LastName"/>  
</apex:remoteObjects>

<script>
var ct = new RemoteObjectModel.Contact();
ct.retrieve( 
    { where: { 
        FirstName: {eq: 'Marc'}, 
        LastName: {eq: 'Benioff'} 
      }, 
      orderby: [ {LastName: 'ASC'}, {FirstName: 'ASC'} ],
      limit: 1 },  

    function(err, records) { 
        if (err) { 
            alert(err); 
        } else { 
            console.log(records.length); 
            console.log(records[0]); 
        } 
    } 
);
</script>

这 查询条件:找到名为 Marc Benioff 的联系人,并将查询限制为单个 结果。

哪里条件

where条件使您能够筛选 检索操作的结果,与 SOQL 查询中的条件大致相同。可用的运算符 条件是:

WHEREwhere

  • eq:等于
  • ne:不等于
  • lt:小于
  • lte:小于或等于
  • gt:大于
  • gte:大于或等于
  • like:字符串匹配。与 SOQL,使用“%”作为通配符。
  • in:in,用于查找与一组 固定值。提供 值作为数组,例如 [‘Benioff’, ‘Jobs’, ‘盖茨’]。
  • nin:not in,用于查找与 一组固定值。提供 值作为数组,例如 [‘Benioff’, ‘Jobs’, ‘盖茨’]。
  • and:逻辑 AND,用于 组合条件
  • or:逻辑 OR,用于组合 条件

在对象中,添加字段名称和 条件对以创建复杂的条件。默认情况下,多个条件是 视为 AND 条件。您可以使用 和 创建其他条件条件。 为 例:

whereandor

{ 
where: 
    { 
    or: 
        {
        FirstName: { like: "M%" }, 
        Phone: { like: '(415)%' }
        }
    }
}

使用 和 的组合根据日期范围筛选结果。例如:ltegte

<apex:remoteObjects jsNamespace="RemoteObjectModel">
    <apex:remoteObjectModel name="Account" fields="Id,Name,CreatedDate"/>
</apex:remoteObjects>

<script>
    var account_created_from_date = new Date('2017-01-01');
    var account_created_to_date = new Date('2018-01-01');
    var clauses = {       
        'where': {
            'CreatedDate': { 'lte': account_created_to_date },
            'and': {
                'CreatedDate': { 'gte': account_created_from_date },
                'Id': { 'ne': '' }
            }
        }   
    };

    var ct = new RemoteObjectModel.Account();
    ct.retrieve(
        clauses,
        function(err, records) { 
            if (err) { 
                console.log(err); 
            } else { 
                console.log(records.length); 
                console.log(records[0]); 
            } 
        } 
    );
</script>

订购者条件

orderby使您能够 为结果设置排序顺序。您最多可以对三个字段进行排序。指定条件 作为包含名称/值对的 JavaScript 对象数组。这 要排序的字段是名称,排序说明是值。 排序说明使您能够按升序或降序排序,并 对 NULL 值进行排序。例如:

orderby

orderby: [ {Phone: "DESC NULLS LAST"} , {FirstName: "ASC"} ]

限制和偏移条件

limit并使您能够检索特定的 一次记录数,并翻阅一组扩展的 结果。offset

用于 指定在一批结果中要返回的记录数。默认 值为 20。最大值为 100。limit

用于指定要跳过的记录数 在将记录添加到返回结果之前的总体结果集。最小值为 1。 最大偏移量为 2,000 行。请求 大于 2,000 的偏移量会导致错误。offsetNUMBER_OUTSIDE_VALID_RANGE

远程 对象回调函数

远程 对象以异步方式将所有请求发送到 Salesforce 服务。代码处理响应 到远程 您提供的回调函数中的对象操作。回调函数处理更新 包含操作结果和返回的错误的页面。

回调函数是标准 JavaScript 中用于处理事件和异步操作的技术。远程 对象使用此模式来处理其异步操作的响应。当你 调用远程 对象操作,您可以提供操作的参数和回调(可选) 功能。调用操作后,JavaScript 代码将继续不间断地运行。 当远程操作完成并返回结果时,回调函数 被调用并接收操作的结果。远程 可以编写对象回调函数以接收最多三个参数。

function callback(Error error, Array results, Object event) { // ... }
名字类型描述
错误JavaScript Error 对象标准 JavaScript Error 对象。如果操作成功,则为 。用于检索 失败的原因。errornullerror.message
结果JavaScript 数组一个数组,包含操作的结果。如果 操作是一个, 结果是相应 Remote 的实例 对象。否则,数组包含表示受影响 记录。retrieve()Id
事件JavaScript 对象一个 JavaScript 对象,该对象提供传输 Remote 的 JavaScript 远程处理事件的详细信息 对象操作。

大多数回调函数会检查错误,然后使用 结果。通常使用该对象 仅在调试和复杂的错误管理方面。

event

下面是一个简单的回调函数,用于处理操作的结果。

retrieve()

function getAllContacts() {
    $j.mobile.showPageLoadingMsg();
    
    var c = new RemoteObjectModel.Contact();
    c.retrieve({ limit: 100 }, function (err, records) { 
        // Handle errors
        if (err) { 
            displayError(err); 
        } else {
            // Add the results to the page
            var list = $j(Config.Selectors.list).empty();
            $j.each(records, function() {
                var newLink = $j('<a>'+this.get('FirstName')+' '+this.get('LastName')+'</a>');
                newLink.appendTo(list).wrap('<li></li>');
            });
            
            $j.mobile.hidePageLoadingMsg();
            list.listview('refresh');
        }
    });
}

在 此示例调用匿名函数并将其传递为 回调。回调函数检查错误,然后使用 jQuery 循环访问结果记录数组,并将它们添加到页面中。一些细节 省略以专注于回调结构。请参阅使用远程的示例 带有 jQuery Mobile 的对象,用于获取完整的页面源代码。

getAllContacts()retrieve()

覆盖默认远程对象操作

覆盖默认的远程 使用您自己的 Apex 代码进行对象操作,以扩展或自定义 Remote 的行为 对象。

Remote 幕后花絮 对象、基本操作—, , ,和 —使用 Remote 对象控制器,相当于普通 Visualforce 页面的标准控制器。您可以 覆盖远程 用于扩展或替换此控制器的内置行为的对象操作。重写 远程的 对象操作是用 Apex 编写的,并通过将它们添加到页面的 Remote 中来生效 对象定义。create()retrieve()update()del()

注意

无法重写该操作。 它只是一个便利功能,在幕后它委托给 either 或 .重写其中任一方法时,重写的方法 被自动用作 适当。upsert()create()update()upsert()

远程 方法重写的对象访问定义

覆盖远程使用远程方法对对象进行操作,将操作的属性设置为方法 替换默认方法。例如,下面介绍如何使用远程覆盖联系人的操作 方法。

create()

<apex:remoteObjectModel name="Contact" fields="FirstName,LastName,Phone" 
    create="{!$RemoteAction.RemoteObjectContactOverride.create}"/>

该属性采用 视觉力 引用要用作内置操作重写的方法的表达式。@RemoteActioncreate()这 表达式采用 的形式,为 全局处理您的位置 组织命名空间,就像 JavaScript 远程处理一样。请注意, 包含该方法的类 需要设置为页面的控制器或控制器扩展 页。$RemoteAction.OverrideClassName.overrideMethodName$RemoteAction@RemoteAction

使用此声明,每当页面的 JavaScript 代码调用联系人远程对象的函数时, 而不是使用远程对象控制器,将调用您的远程方法。create()

远程 对象重写方法

远程 对象重写方法作为 Apex 类中的方法编写,您可以将其作为 控制器或控制器扩展。@RemoteAction重写的方法签名 方法 是:

@RemoteAction
public static Map<String,Object> methodName(String type, Map<String,Object> fields)

type 参数是正在执行操作的 sObject 类型, 字段映射是包含值的集合 在重写方法之前在远程对象上设置的 叫。

返回值是表示 Remote 结果的映射 对象操作。此映射通常包括调用结果、状态和 要作为自定义方法的一部分提供的任何自定义数据。这 构造有效返回映射的最简单方法是使用 .这是标准 为远程提供内置功能的控制器 对象,您可以通过以下方式将数据操作语言 (DML) 操作委托给它 传递方法的参数。例如,这里有一个方法,它只执行 的内置版本:

RemoteObjectControllercreate()create()

@RemoteAction
public static Map<String, Object> create(String type, Map<String, Object> fields) {
    Map<String, Object> result = RemoteObjectController.create(type, fields);
    return result;
}

这 方法实际上是一个空操作;也就是说,此方法执行完全相同的操作 内置版本本来可以做到的,不多也不少。您的覆盖方法 可以执行您需要的任何其他 Apex,包括日志记录、额外的 DML、 其他方法调用,依此类推。有关远程的更完整示例 对象重写方法以及使用它的页面,请参阅在远程中使用远程方法重写的示例 对象。

重要

标准控制器自动处理 共享 Remote 的规则、所有权和其他安全问题 对象。相比之下,自定义控制器或控制器扩展中的方法在 系统模式,允许对组织中的所有数据进行完全访问。 此行为与使用 自定义控制器或控制器扩展。当您编写控制器代码时,您 需要自己处理访问权限和其他问题。RemoteObjectController

最佳做法是使用 你的关键词 控制器或控制器扩展类,并尽可能多地委托给 .with sharingRemoteObjectController

在 Remote 中使用 Remote 方法重写的示例 对象

此示例代码演示如何为 Remote 创建远程方法重写 对象操作。该示例提供了一个排序的联系人列表和一个用于输入 新联系人。新的联系人操作将覆盖内置的远程 对象操作。该示例还演示了 混合远程具有多个 Web 开发库的对象,用于呈现适合移动设备的用户 接口。

create()

此示例使用 jQuery、Bootstrap 和 Mustache 工具包,从 外部内容分发网络 (CDN)。这是 Visualforce 页面,其中包含 大胆。

<apex:page showHeader="false" standardStylesheets="false" docType="html-5.0" 
    title="Contacts—RemoteObjects Style" controller="RemoteObjectContactOverride">

    <!-- Include in some mobile web libraries -->
    <apex:stylesheet value="//netdna.bootstrapcdn.com/bootswatch/3.1.1/superhero/bootstrap.min.css"/>
    <apex:includeScript value="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"/>
    <apex:includeScript value="//cdnjs.cloudflare.com/ajax/libs/mustache.js/0.7.2/mustache.min.js"/>

    <!-- Set up Remote Objects, with an override for create() method -->
    <apex:remoteObjects jsNamespace="$M">
        <apex:remoteObjectModel name="Contact" fields="FirstName,LastName,Phone" 
            create="{!$RemoteAction.RemoteObjectContactOverride.create}"/>
    </apex:remoteObjects>

    <!-- 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: 'DESC'} ]) 
                        <button id="bRefresh" class="btn btn-success btn-sm" 
                            type="button">Refresh</button>
                    </caption>
                    <caption id="msgBox" class="alert alert-danger hidden"></caption>
                    <thead>
                        <tr><td>FirstName</td><td>LastName</td><td>Phone</td></tr>
                    </thead>
                    <tbody></tbody>
                    <tfoot>
                        <tr>
                        <td><input type="text" name="FirstName" id="iFirstName" 
                            placeholder="John" class="form-control" /></td>
                        <td><input type="text" name="LastName" id="iLastName" 
                            placeholder="Doe" class="form-control" /></td>
                        <td>
                            <div class="input-group">
                              <input type="text" name="Phone" id="iPhone" 
                                placeholder="(123) 456-7890" class="form-control" />
                              <span class="input-group-btn">
                                <button id="bAdd" 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) -->
    <script id="tmpl" type="x-tmpl-mustache">
        <tr><td>{{FirstName}}</td><td>{{LastName}}</td><td>{{Phone}}</td></tr>
    </script>

    <!-- Page functionality -->
    <script>
        var table = $('#myTable tbody');
        var template = $('#tmpl').html();
        Mustache.parse(template);

        // Retrieve all contacts and add to results table on page
        var fetchContacts = function() {
            (new $M.Contact()).retrieve({
                orderby: [ {LastName: 'ASC'}, {FirstName: 'DESC'} ],
            }, function(err, records) {
                if (!err) {
                    // Add some status messages to the log panel
                    $('#log')
                    .append('<p>Fetched contact records.</p>')
                    .append('<p>Records Size: '+ records.length + '!</p>');

                    // Update the table of contacts with fresh results
                    table.empty();
                    records.forEach(function(rec) {
                        table.append(Mustache.render(template, rec._props));                    
                    });                
                } else {
                    $('#msgBox').text(err.message).removeClass('hidden');            
                }
            });
        };
        
        var addContact = function() {
            // Create a new Remote Object from form values
            (new $M.Contact({
                FirstName: $('#iFirstName').val(),
                LastName: $('#iLastName').val(),
                Phone: $('#iPhone').val()
            })).create(function(err, record, event) {
                // New record created...
                if (!err) {
                    // Reset the New Record form fields, for the next create
                    $('input').each(function() {
                        $(this).val('');
                    });
                    
                    // Add some status messages to the log panel
                    $('#log')
                    .append('<p>Contact created!</p>')
                    // Custom data added to event.result by override function
                    .append('<p>Got custom data: ' + event.result.custom + '</p>'); 

                    // Redraw the results list with current contacts
                    fetchContacts();
                } else {
                    $('#msgBox').text(err.message).removeClass('hidden');            
                }
            });
        };

        // Bind application functions to UI events
        $('#bRefresh').click(fetchContacts);
        $('#bAdd').click(addContact);
        
        // Initial load of the contacts list
        fetchContacts();
    </script>
</apex:page>

前面示例中的关键代码行位于 Remote 对象访问定义。将单个属性添加到联系人远程对象定义 设置 覆盖:

create="{!$RemoteAction.RemoteObjectContactOverride.create}"

该属性采用 视觉力 引用要用作内置操作重写的方法的表达式。

@RemoteActioncreate()在本例中,引用的方法位于 Apex 类中,该类是页面的 控制器。重写方法的代码很简单。

public with sharing class RemoteObjectContactOverride {
     
    @RemoteAction
    public static Map<String, Object> create(String type, Map<String, Object> fields) {
        System.debug(LoggingLevel.INFO, 'Before calling create on: ' + type);
        
        // Invoke the standard create action
        // For when you want mostly-normal behavior, with a little something different
        Map<String, Object> result = RemoteObjectController.create(type, fields);

        System.debug(LoggingLevel.INFO, 'After calling create on: ' + type);
        System.debug(LoggingLevel.INFO, 'Result: ' + result);
        
        // Here's the little something different, adding extra data to the result
        Map<String, Object> customResult = 
            new Map<String, Object> {'custom' => 'my custom data' };
        customResult.putAll(result);
        
        return customResult;
    }
}

此方法记录调用,然后 使用标准调用来执行创建。它 执行相同的数据操作语言 (DML) 命令以创建记录 内置版本会,因为它使用的是内置版本。后 执行 create,该方法会执行更多的日志记录。最后,它增加了一些额外的内容 数据复制到 JavaScript 回调函数将接收的返回有效负载 Visualforce 页面。@RemoteActionRemoteObjectController.create()

它添加了有趣的额外数据,并使覆盖 内置方法很有用。前面的控制器添加的额外数据 是微不足道的,仅用于说明目的。实际覆盖可以包括 更复杂的逻辑 – 计算的结果、其他方法调用等。 需要了解的重要一点是,新的自定义替代方法可以执行 幕后的附加内容,并可以返回内置版本的额外数据 不能。

使用远程的示例 使用 jQuery Mobile 的对象

Visualforce 远程 Objects 旨在与 JavaScript 框架很好地“融合”。这个扩展但简单的例子 演示如何使用远程 使用 jQuery Mobile 查看联系人列表以及添加、编辑和删除的对象 他们。

此示例使用 Salesforce Mobile Pack 中的 jQuery Mobile,并且基于 在移动随附的示例代码上 打包用于 jQuery。远程 对象和 jQuery Mobile 可以很容易地为 电话。

具有远程对象和 jQuery Mobile 的简单联系人编辑器

<apex:page docType="html-5.0" showHeader="false" sidebar="false">          

	<!-- Include jQuery and jQuery Mobile from the Mobile Pack -->
    <apex:stylesheet value="{!URLFOR($Resource.MobilePack_jQuery, 
         'jquery.mobile-1.3.0.min.css')}"/>
    <apex:includeScript value="{!URLFOR($Resource.MobilePack_jQuery, 
         'jquery-1.9.1.min.js')}"/>
    <apex:includeScript value="{!URLFOR($Resource.MobilePack_jQuery, 
         'jquery.mobile-1.3.0.min.js')}"/>

    <!-- Remote Objects declaration -->
    <apex:remoteObjects jsNamespace="RemoteObjectModel">
        <apex:remoteObjectModel name="Contact" fields="Id,FirstName,LastName,Phone">
            <!-- Notes is a custom field added to the Contact object -->
            <apex:remoteObjectField name="Notes__c" jsShorthand="Notes"/>
        </apex:remoteObjectModel>
    </apex:remoteObjects>

    <head>
        <title>Contacts</title>
        <meta name="viewport" 
           content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
        
        <script type="text/javascript">
            var $j = jQuery.noConflict(); 

            // Config object with commonly used data
            // This keeps some hard-coded HTML IDs out of the code
            var Config = {
                Selectors: {
                    list: '#cList',
                    detailFields: "#fName #lName #phone #notes #error #contactId".split(" ")
                },
                Data: {
                    contact: 'contact'
                }
            };
            
            // Get all contacts, and display them in a list 
            function getAllContacts() {
                $j.mobile.showPageLoadingMsg();
                
                var c = new RemoteObjectModel.Contact();
                // Use the 'limit' operator to increase the default limit of 20
                c.retrieve({ limit: 100 }, function (err, records) { 
                    // Handle any errors
                    if (err) { 
                        displayError(err); 
                    } else {
                        // Empty the current list
                        var list = $j(Config.Selectors.list).empty();
                        // Now add results records to list
                        $j.each(records, function() {
                            var newLink = $j('<a></a>', {
                                text: this.get('FirstName') + ' ' + this.get('LastName')
                            });
                            newLink.data(Config.Data.contact, this.get('Id'));
                            newLink.appendTo(list).wrap('<li></li>');
                        });
                        
                        $j.mobile.hidePageLoadingMsg();
                        list.listview('refresh');
                    }
                });
            }

            // Handle the Save button that appears on both
            // the Edit Contact and New Contact pages
            function addUpdateContact(e){
                e.preventDefault();

                var record = new RemoteObjectModel.Contact({
                    FirstName: $j('#fName').val(),
                    LastName: $j('#lName').val(),
                    Phone: $j('#phone').val(),
                    Notes: $j('#notes').val() 
                    // Note use of shortcut 'Notes' in place of Notes__c
                });

                var cId = $j('#contactId').val();
                if( !cId ) { // new record
                    record.create(updateCallback);
                } else { // update existing
                    record.set('Id', cId);
                    record.update(updateCallback);
                }
            }
            
            // Handle the delete button
            function deleteContact(e){
                e.preventDefault();
                var ct = new RemoteObjectModel.Contact();
                ct.del($j('#contactId').val(), updateCallback);
            }
            
            // Callback to handle DML Remote Objects calls
            function updateCallback(err, ids){
                if (err) { 
                    displayError(err); 
                } else {
                    // Reload the contacts with current list
                    getAllContacts();
                    $j.mobile.changePage('#listpage', {changeHash: true});
                }
            }

            // Utility function to log and display any errors
            function displayError(e){
                console && console.log(e);
                $j('#error').html(e.message);
            }
        
            // Attach functions to the buttons that trigger them
            function regBtnClickHandlers() {
                $j('#add').click(function(e) {
                    e.preventDefault();
                    $j.mobile.showPageLoadingMsg();
                    
                    // empty all the clic handlers                    
                    $j.each(Config.Selectors.detailFields, function(i, field) {
                        $j(field).val('');
                    });
                    
                    $j.mobile.changePage('#detailpage', {changeHash: true});
                    $j.mobile.hidePageLoadingMsg();
                });
        
                $j('#save').click(function(e) {
                   addUpdateContact(e);
                });
        
                $j('#delete').click(function(e) {
                   deleteContact(e);
                });
            }

            // Shows the contact detail view, 
            // including filling in form fields with current data
            function showDetailView(contact) {
                $j('#contactId').val(contact.get('Id'));
                $j('#fName').val(contact.get('FirstName'));
                $j('#lName').val(contact.get('LastName'));
                $j('#phone').val(contact.get('Phone'));
                $j('#notes').val(contact.get('Notes'));
                $j('#error').html('');
                $j.mobile.changePage('#detailpage', {changeHash: true});
            }

            // Register click handler for list view clicks
            // Note: One click handler handles the whole list	
            function regListViewClickHandler() {
                $j(Config.Selectors.list).on('click', 'li', function(e) {

                    // show loading message
                    $j.mobile.showPageLoadingMsg();

                    // get the contact data for item clicked
                    var id = $j(e.target).data(Config.Data.contact);

                    // retrieve latest details for this contact
                    var c = new RemoteObjectModel.Contact();
                    c.retrieve({ 
                        where: { Id: { eq: id } } 
                    }, function(err, records) { 
                        if(err) { 
                            displayError(err); 
                        } else {
                            showDetailView(records[0]);
                        }

                        // hide the loading message in either case
                        $j.mobile.hidePageLoadingMsg();
                    });
                });
            }

            // And, finally, run the page
            $j(document).ready(function() {
                regBtnClickHandlers();
                regListViewClickHandler();
                getAllContacts();
            });

        </script>    
    </head>

    <!-- HTML and jQuery Mobile markup for the list and detail screens -->
    <body>    
    
        <!-- This div is the list "page" -->
        <div data-role="page" data-theme="b" id="listpage">                
            <div data-role="header" data-position="fixed">
                <h2>Contacts</h2>
                <a href='#' id="add" class='ui-btn-right' data-icon='add' 
                   data-theme="b">Add</a>
            </div>
            <div data-role="content" id="contactList">            
                <ul id="cList" data-filter="true" data-inset="true" 
                    data-role="listview" data-theme="c" data-dividertheme="b">
                </ul>
            </div>
        </div>
                
        <!-- This div is the detail "page" -->
        <div data-role="page" data-theme="b" id="detailpage">
            <div data-role="header" data-position="fixed">
                <a href='#listpage' id="back2ContactList" class='ui-btn-left' 
                   data-icon='arrow-l' data-direction="reverse" 
                   data-transition="flip">Back</a>
                <h1>Contact Details</h1>
            </div>
            <div data-role="content">
                <div data-role="fieldcontain">
                    <label for="fName">First Name:</label>
                    <input name="fName" id="fName" />
                </div>
                <div data-role="fieldcontain">
                    <label for="lName">Last Name:</label>
                    <input name="lName" id="lName" />
                </div>
                <div data-role="fieldcontain">
                    <label for="phone">Phone:</label>
                    <input name="phone" id="phone"/>
                </div>
                <div data-role="fieldcontain">
                    <label for="notes">Notes:</label>
                    <textarea name="notes" id="notes"/>
                </div>

                <h2 style="color:red" id="error"></h2>

                <input type="hidden" id="contactId" />
                <button id="save" data-role="button" data-icon="check" 
                    data-inline="true" data-theme="b" class="save">Save</button>
                <button id="delete" data-role="button" data-icon="delete" 
                    data-inline="true" class="destroy">Delete</button>
            </div>
        </div>
    </body>
</apex:page>

请注意,虽然所有四个远程 演示了对象操作,只有三个回调处理程序。

  • getAllContacts()加载联系人列表的调用 并为回调提供匿名函数。回调检查 错误,然后循环访问结果,将它们添加到页面中。retrieve()
  • 同样,调用加载单个 联系详情页面,结果也由 匿名函数。showDetailView()retrieve()
  • addUpdateContact()并处理添加, 更新和删除联系人。这两种方法都作为回调传递 功能。 不使用远程的结果 对象操作。它只检查错误,将它们记录到控制台,然后 调用刷新 页面。deleteContact()updateCallback()updateCallback()getAllContacts()

使用远程的最佳实践 对象

Visualforce 远程Objects 是一个有效的工具,用于快速向 Visualforce 页面添加简单的数据操作。远程 Objects 易于使用,具有不需要 Apex 代码即可实现读取的轻量级组件 以及将数据写入 Salesforce 服务。远程 但是,对象并不总是适合这项工作的工具,因此了解远程操作非常重要 对象的工作原理以及何时使用其他工具,例如 JavaScript 远程处理。

字段级安全性

远程 对象遵循组织的字段级别安全设置。创建时请记住这一点 使用远程的页面 对象。查看页面的用户无法访问的字段显示为空白。操作 如果修改字段数据 (, , 和 ) 在请求中包含不可访问的字段,则它们将失败并显示错误。create()update()upsert()

交易边界

)

相比之下,JavaScript 远程处理事务 边界位于 Apex 方法上。它 在一种方法中轻松创建发票和相关行项目记录,其中自动 Apex 交易可确保所有 记录是一起创建的,或者根本不创建。@RemoteAction

业务逻辑的适当放置和测试

仔细考虑将应用程序的业务逻辑放在何处,尤其是在 这很复杂。当您创建支持创建、编辑和 删除单个对象,如使用 Remote 的示例 使用 jQuery Mobile 的对象,业务逻辑是最小的。将此业务逻辑放在客户端,在 远程 对象和 JavaScript,可以完全合适。当您有更复杂的业务规则和 但是,从客户端层中删除该逻辑可能更有效,并且 在服务器端构建它。在决定将组织的业务逻辑放在何处时,请考虑以下几点。

  • 安全性和一致性:请记住,用户可能会在 在事务中,或者改变页面的 JavaScript 使用 Firebug 和其他 工具。远程 对象强制执行验证规则、触发器、共享规则、字段级安全性等 数据访问限制,但如果您将业务规则放在 JavaScript 而不是 Salesforce 中,这些规则可能会被中断, 更改或绕过。
  • 可测试性:服务器端的业务逻辑可以使用 Salesforce 提供的许多工具进行测试。为 因此,我们鼓励您将复杂的行为放在 Apex 中,并使用 Apex 测试框架 以验证它是否按预期工作。
  • 性能:如果您的处理需要将许多记录作为事务的一部分进行查看,但 不会在浏览器中显示它们,我们建议您避免将该数据发送到客户端,并且 而是在服务器上“本地”处理数据。想想你的页面需要做什么数据 它的工作,并确保您不会不必要地通过网络复制它。

处理复杂性

应用程序需要仔细管理复杂性。简单的联系人管理器或商店定位器 页面管理起来并不复杂,但许多业务流程都有。 远程 对象与 jQuery 和 AngularJS 等 JavaScript 框架搭配得很好,这些框架可以帮助 应用程序用户界面的复杂性。始终考虑将关注点分开 您的应用程序分为多个层,并尽可能使它们离散。这称为 “关注点分离”,这是一种经典的软件模式和最佳实践。

请考虑将数据完整性规则放在触发器和验证规则中。还要考虑 将业务流程规则封装在 Apex 代码中,您可以通过可与 JavaScript 远程处理或 SOAP 或 REST 服务一起使用的方法访问这些规则 您可以在任何地方使用。@RemoteAction

远程的替代品 对象

远程 Objects 是一个有用的工具,用于快速创建具有基本数据操作的页面。当工作那 您的页面需要做的比这更大,考虑到 Salesforce 提供了许多替代方案 闪电平台开发人员。

  • 标准 Visualforce 可用于实现广泛的应用程序功能。 Visualforce 在使用标准控制器时提供了许多自动功能,并且 使用您自己的 Apex 代码支持完全自定义的功能。
  • JavaScript 远程处理也适用于 第三方 JavaScript 框架,使您能够访问 顶点。
  • Salesforce Mobile 允许您使用 声明性工具,而不是代码。

仔细考虑您的页面或应用程序需要做什么,然后选择正确的 工作工具。有时该工具是远程的 对象,有时是别的东西。

远程 对象限制

Visualforce 虽然远程 对象不受某些资源限制的约束,它有自己的限制。远程 对象受以下限制的约束。

  • 远程 对象不是规避 Salesforce 服务限制的方法。远程 对象调用不受 API 限制的约束,但使用远程的 Visualforce 页面不受限制 对象受所有标准 Visualforce 限制的约束。
  • 在单个请求中最多可以检索 100 行。要显示更多行,请提交 使用查询的其他请求 参数。OFFSET
  • 远程 对象不支持字段。您无法检索 或设置 类型为 的对象字段的值。BlobBlob
  • 将属性设置为“远程” 对象组件禁用为那些 Remote 生成 JavaScript 对象。依赖于未呈现的 Remote 的任何页面功能 还应禁用对象。renderedfalse