• 构建通过 Database.com 提供技术支持的 PhoneGap 应用程序


     要求
    其他必要产品
    Database.com account
    用户级别
    全部
    
    必需产品
    PhoneGap Build
    范例文件
    Database.Com-PhoneGap-Sample
    在这篇文章中,我们将探究使用 PhoneGap 构建的移动应用程序的创建过程,所有数据 均通过 Database.com 提供并保存。在深入探讨技术细节之前,我们先来回顾一下相关术语。
    
    PhoneGap
    
    PhoneGap 是一种免费的开放源码技术,旨在使开发人员使用基于 Web 的传统技术,创建能够在多个平台上进行本机安装的移动应用程序。我们最好将 PhoneGap 视为占据设备整个宽度和高度的 Web 视图。在该 Web 视图内,您需要使用 HTML、CSS 和 JavaScript 语言构建应用程序接口。
    
    
    图 1. 应用程序 Web 视图
    该应用程序的 Web 视图基于操作系统的本机 Web 视图,其本质上与标准 Web 浏览器相同(窗口镶边除外)。您将完全使用 HTML、CSS 和 JavaScript 语言构建所有导航和内容元素,并使用 JavaScript 编写应用程序逻辑。
    
    PhoneGap 提供了一个 JavaScript 与本机之间的桥梁。这样,您的应用程序将无需编写任何本机代码即可与本机操作系统进行交互。您完全用 JavaScript 构建应用程序逻辑,并利用 PhoneGap API 与设备操作系统进行交互。
    
    
    图 2. PhoneGap 可助您访问各种设备功能
    “开箱即用”的 PhoneGap 可访问设备加速计、罗盘及地理位置功能,访问设备联系人、本机文件系统、设备相机、媒体播放及捕获功能。您可以在多个平台上访问上述所有功能,而无需编写任何本机代码行,所有功能完全可以通过 JavaScript 进行访问。如果这还不够,您还可以通过在可扩展架构之上构建“本机插件”来轻松扩展 PhoneGap,稍后我将在本文中进行进一步的细节说明。
    
    PhoneGap 还提供了一种针对常用移动生态系统包装应用程序的方式。PhoneGap 应用程序的输出内容是二进制应用程序存档,其中包含应用程序赖以运行的所有必要的 HTML、CSS 和 JavaScript 资产。
    
    
    图 3. PhoneGap 针对常见移动生态系统包装应用程序
    对于 iOS 设备,此输出为 IPA 文件,对于 Android 而言是 APK 文件,而对于 Windows Phone 则是 XAP 文件等… 所有这些二进制文件格式均为正常存档,您可以将它们上传至 iTunes Store、Google Play、BlackBerry App World 及 Windows Phone Marketplace,也可以直接安装至某台设备。单个 PhoneGap 应用程序代码库可针对 Apple iOS、Google Android、Windows Phone、BlackBerry、HP WebOS、Symbain 及 Samsung Bada 操作系统,且可针对手机和平板电脑规格。
    
    正如我前面所说,PhoneGap 是一种免费的开放源码技术。整个 PhoneGap 代码库均可作为 Apache Cordova项目的一部分进行免费访问。您可以立即下载源代码、进行更改及回馈项目!
    
    Database.com
    
    Database.com是 salesforce.com 提供的一个云数据库。Database.com 提供了多种托管关系数据库功能,您可以借此轻松地进入应用程序内部,并且无需自行对基本结构进行管理。您可以运用基于 Web 的管理界面来创建对象和关系,并利用 REST 或 SOAP API 访问自己的数据。您可以通过 开发人员中心了解有关 Database.com 功能的更多信息。
    
    应用程序
    
    在本文中,我们将会浏览使用 Database.com 上的数据的基本 PhoneGap 应用程序的创建过程。
    
    下面是相关基本概念:它是一种可供“现场”工作人员使用的应用程序,用以前往不同的位置,收集联系人或“潜在客户”。它可以用来搜集联系人信息及注释,也可以记录用户的 GPS 坐标,以识别信息捕获位置。它还可让您返回并编辑此前捕获的数据。当然,还需要确保该应用程序能够在各种平台上进行访问。下面让我们来看一下基本流程,然后我们将详细介绍如何创建该应用程序。
    
    
    图 4. 示例应用程序的三个主界面
    您可以看到,该应用程序包含三个主界面,“Home” 界面(让您自行选择添加新记录还是查看现有记录)、“Leads” 列表(用于显示现有记录)及添加/编辑记录的表单。还有一个是登录/身份验证界面,我们将在稍后进行介绍,但现在您可以看到这是一个简单的应用程序用例。
    
    您可以在以下网址下载此应用程序的完整源代码:https://github.com/triceam/Database.Com-PhoneGap-Sample.
    
    数据库
    此应用程序将对数据存储使用 Database.com 上的一个自定义表。虽然这是一个基本的单表示例,但 Database.com 还可以支持大型的多重关系数据结构。
    
    注册免费帐户后,登录并单击 System Overview 界面上的 “Create A New Object”。
    
    
    图 5. 在 System Overview 界面上创建新对象
    这将会显示 “New Custom Object” 对话框。您需要在此处输入对象标签/名称。我在这里创建了一个 “Lead” 对象。纳入即将通过 PhoneGap 应用程序捕获的潜在客户。
    
    
    图 6. 输入对象标签/名称。
    接下来,向 "Lead" 对象添加自定义字段。在 “Custom Fields & Relationships” 部分,单击 “New” 按钮添加数据字段。
    
    我添加了 “First Name”、“Last Name”、“Latitude”、“Longitude”、“Notes”、“Email” 及 “Telephone” 字段。只需按照在线向导中所列的步骤 - 它将会指导您完成此流程。
    
    
    图 7. 在 “Custom Fields & Relationships” 下添加数据字段
    此时,我们已经创建了这些数据对象,它们将用于保存在应用程序内捕获的数据。然而,您还需要再执行一个步骤,才能在 PhoneGap 应用程序内使用这些对象。为远程访问数据,您必须创建一个远程访问“应用程序”。此应用程序的配置将包括一个独特的密钥,用来以正确的方式对用户进行身份验证及识别您的数据对象。
    
    要创建远程访问“应用程序”,只需展开 “Develop” 类别,然后选择 “New”。
    
    
    图 8. 在 “Develop” 类别中创建新的远程访问“应用程序”
    这将会显示 “Remote Access Edit” 表单,您需要在这里指定应用程序名称、联系人电子邮件及应用程序的回调 URL 配置。应用程序名称将用作登录应用程序时的说明性文字,回调 URL 将用来在用户成功完成身份验证后重定向用户的浏览器。
    
    
    图 9. 在 “Remote Access Edit” 表单中指定应用程序详细信息
    在保存此信息后,系统将会向您提供一个“用户密钥”,供您在此后登录及访问 Database.com 中的数据服务时使用。记录您的用户密钥。稍后您需要在 JavaScript 配置中使用此密钥。
    
    
    图 10. 从 Database.com 登录及访问数据服务需要使用的“用户密钥”
    在获取用户密钥后,您便可以开始通过 Database.com 来回推拉数据。
    
    Forcetk.js
    该应用程序将使用 forcetk.js JavaScript 包装器与 Database.com 的 REST API 开展通信。Forcetk.js (Force.com JavaScript REST Tookit) 是 REST API的一个开放源码包装器,用以简化基于 JavaScript 的应用程序内部的 Force.com/Database.com 数据使用过程。Forcetk.js 可提供 OAuth2身份验证挂钩及各种辅助方法,从而使数据检索和更新变得异常方便。
    
    在 PhoneGap 中构建应用程序时,无需代理即可直接访问 force.com REST API(与您在构建基于标准浏览器的应用程序时的操作相同)。
    
    PhoneGap 应用程序
    
    在利用 PhoneGap 时,您应当完全用 HTML、CSS 和 JavaScript 语言创建应用程序。您可以在任何文本编辑器中开发这些应用程序。自行决定使用哪种方法。您可以使用简单文本编辑器(如 TextMate)、HTML 编辑器(如 Dreamweaver),也可以使用复杂 IDE(如 Xcode、Eclipse 或 Visual Studio)。Xcode、Eclipse 或 Visual Studio 等 IDE 可让您使用 USB 连接直接在设备上部署 PhoneGap 应用程序。不过,您也可以使用PhoneGap Build(一种基于云的 PhoneGap 编译器),您只需加载 HTML、CSS 和 JavaScript 代码,它即可为您生成平台特定二进制文件。
    
    由于要使用 HTML、CSS 和 JavaScript 构建应用程序界面,您可以利用现有的工具和框架提高开发人员的工作效率。此示例将利用以下工具加快开发流程:
    
    Zepto.js – 一种开发加速器库,用以提供创建交互式动态 JavaScript 体验所需的实用工具和快捷功能。Zepto.js 包含一种 jQuery 兼容语法,并经过移动核心优化。
    Twitter Bootstrap – 一种用户界面样式库,用以提供各种 CSS 样式及 HTML/JavaScript 组件,从而使您的应用程序更像“应用程序”,而非“只是网页”。
    Mustache.js – 一种易于使用的模板库,可让您创建 HTML“模板”,以便在动态 JavaScript 应用程序内部使用。模板技术有助于您轻松分离 HTML 用户界面层与 JavaScript 语言编写的应用程序逻辑。
    首先,在应用程序内创建根 HTML 文件,它将成为该应用程序的切入点。所有 PhoneGap 应用程序均通过 "index.html" 文件启动,它也是应用程序的“根”。我在 index.html 文件内纳入了所有适当的库,并加入了空白 HTML <body>。 HTML <body> 空白是因为我们将通过 JavaScript 动态创建整个用户界面。
    
    <html>
        <head>
        <link rel="stylesheet" href="assets/css/bootstrap.css" type="text/css" />
        <link rel="stylesheet" href="assets/css/styles.css" type="text/css" />
            <script type="text/javascript" src="js/libs/zepto.js"></script>
            <script type="text/javascript" src="js/libs/forcetk.js"></script>
            <script type="text/javascript" src="cordova-1.7.0.js"></script>
            <script type="text/javascript" src="js/libs/ChildBrowser.js"></script>   
            <script type="text/javascript" src="js/libs/mustache.js"></script>       
            <script type="text/javascript" src="js/salesforceWrapper.js"></script>         
            <script type="text/javascript" src="js/application.js"></script>            
        </head>
        
        <body></body>
        
    </html>
    在完成 PhoneGap 应用程序初始化后,将会发送 "deviceready" 事件。此事件表明,应用程序内容均已充分加载,所有 PhoneGap API 均已完成初始化。
    
    document.addEventListener( "deviceready", onDeviceReady );
    
    var sfw;
    
    function onDeviceReady( event ) {
        console.log("deviceready");
        
        //initialize salesforce wrapper
        sfw = new SalesforceWrapper();
    }
    首先,应用程序会初始化我创建的名为 "SalesforceWrapper" 的 JavaScript 类。SalesforceWrapper 类包装 forcetk.js 库,以便简化身份验证并纳入访问 force.com REST API 所需的全部配置信息。在这个类中,您将需要使用用户密钥(通过“远程访问”配置获取,我们已在前文进行过讨论)设置 clientID 值。
    
    function SalesforceWrapper() {
        /* AUTHENTICATION PARAMETERS */
        this.loginUrl = 'https://login.salesforce.com/';
        this.clientId = 'YOUR_KEY_GOES_HERE;
        this.redirectUri = 'https://login.salesforce.com/services/oauth2/success';
        
        /* CLASS VARIABLES */
        this.cb = undefined;     //ChildBrowser in PhoneGap
        this.client = undefined; //forceTk client instance
        
        this.init();
    }
    
    SalesforceWrapper.prototype.init = function() {
        this.client = new forcetk.Client(this.clientId, this.loginUrl);
        this.cb = window.plugins.childBrowser;
    }
    
    SalesforceWrapper.prototype.login = function (successCallback) {
        this.loginSuccess = successCallback;
        var self = this;
        self.cb.onLocationChange = function (loc) {
            if (loc.search(self.redirectUri) >= 0) {
                self.cb.close();
                self.sessionCallback(unescape(loc));
            }
        };
        self.cb.showWebPage(self.getAuthorizeUrl(self.loginUrl, self.clientId, self.redirectUri));
    }
    
    SalesforceWrapper.prototype.getAuthorizeUrl = function (loginUrl, clientId, redirectUri) {
        return loginUrl + 'services/oauth2/authorize?display=touch' + '&response_type=token&client_id=' + escape(clientId) + '&redirect_uri=' + escape(redirectUri);
    }
    
    SalesforceWrapper.prototype.sessionCallback = function(loc) {    var oauthResponse = {};
        
        var fragment = loc.split("#")[1];
        
        if (fragment) {
            var nvps = fragment.split('&');
            for (var nvp in nvps) {
                var parts = nvps[nvp].split('=');
                oauthResponse[parts[0]] = unescape(parts[1]);
            }
        }
        
        if (typeof oauthResponse === 'undefined' || typeof oauthResponse['access_token'] === 'undefined') {
            console.log("error");
        } else {
            this.client.setSessionToken(oauthResponse.access_token, null, oauthResponse.instance_url);
            if ( this.loginSuccess ) {
                this.loginSuccess();
            }
        }
        this.loginSuccess = undefined;
    }
    SalesforceWrapper 类简化了 forcetk.js的示例,因而您只需使用应用程序中的一行代码即可完成身份验证:
    
    sfw.login( setupHomeView );
    SalesforceWrapper的登录函数只需要一个参数 – 一个将在用户成功登录后进行调用的函数引用。
    
    您可能还注意到,SalesforceWrapper 是指 ChildBrowser JavaScript 类。ChildBrowser 类是 ChildBrowser PhoneGap 原生扩展的一部分,同时适用于 iOS、Android、BlackBerry 和 Windows Phone。因此,您的 PhoneGap 应用程序将具有“子”Web 视图,在这种情况下,该“子”Web 视图将用于对 Force.com API 进行身份验证。
    
    一旦 SalesforceWrapper 类完成初始化,Moustache.js 模板也将进行初始化。每个模板都是一个独立的 HTML 文件,将用于呈现界面,因而必须加载至内存才能使用。
    
    var templates = {
        structure:"views/structure.html",
        home:"views/home.html",
        form:"views/formView.html",
        list:"views/dataView.html",
        listItem:"views/listItem.html",
        loaded: 0,
        requested: 0,
    }; 
    
    function onDeviceReady( event ) {
        console.log("deviceready");
        
        //initialize salesforce wrapper
        sfw = new SalesforceWrapper();
        
        //load Mousetache HTML templates
        for (var key in templates) {
            (function() {
                var _key = key.toString();
                if ( _key != "loaded" && _key != "requested" ){
                    templates.requested ++;
             
                     var templateLoaded = function( template ){
                        onTemplateLoaded( template, _key );
                     }
                    
                    $.get( templates[ _key ], templateLoaded );
                 }
             })();
        }
    }
    
    function onTemplateLoaded(template, key) {
        
        console.log( key + ": " + template);
        templates[ key ] = template;
        templates.loaded ++;
        
        if ( templates.loaded == templates.requested ) {
            setupDefaultView();
        }
    }
    一旦模板加载完成,setupDefaultView() 函数也将随之调用。这将会根据 templates.structure 模板创建初始用户界面。
    
    var header, container;
    
    function setupDefaultView() {
        console.log("setupDefaultView");
        $("body").html( templates.structure );
        header = $("body").find("#header");
        container = $("body").find("#content");
        
        $('#login').tap(function (e) {
            e.preventDefault();
            sfw.login( setupHomeView );
        });
    }
    随后,它会为该模板的“标头”和“容器”元素设置引用以供日后使用,然后将事件处理程序添加至 “login” 按钮,这样当用户点按该按钮时,将会调用 SalesforceWrapper 类的登录功能。
    
    您可以从下面的 templates.structure 模板中查看 HTML:
    
    <div id="header">Welcome</div>
    <div id="content">
        <h3 style="padding-top:1.5em; padding-bottom:1.5em;" class="alert alert-info">Press the "Login" button to authenticate via Database.com.</h3>
        <br/><br/>
        <a id="login" class="btn btn-success">Login</a>
    </div>
    通过 CSS 样式应用格式,呈现的输出内容如下所示。一旦用户点按 “Login” 按钮,将会显示 Database.com 的 OAuth 登录界面。
    
    
    图 11. 用户点按 Login,而后显示 Database.com 的 OAuth 登录界面
    所有身份验证操作均在 ChildBrowser 内处理,且完全由 Database.com 进行维护。作为一名开发人员,您不必再担心用户帐户管理或登录功能,因为 force.com 将会代您进行处理。用户只需具备 Database.com 数据对象(数据库)的访问权限。一旦用户成功通过身份验证, setupHomeView()函数立即得到调用,并会显示该应用程序的“Home”界面。
    
    setupHomeView()函数重置/清除容器元素的内容,然后填入 templates.home 模板内容,并添加适当的事件处理程序。
    
    function resetContainer() {
        //this removes child elements and cleans up event handlers
        container.children().remove();
        container.removeClass("nopadding");
    }
    
    function setupHomeView() {
        resetContainer();
        container.html( templates.home );
        header.html( "Welcome" );
        
        $('#addNew').tap(function (e) {
            setupFormView();
            e.preventDefault();
            e.stopPropagation();
            return false;
        });
        
        
        $('#queryMyRecords').tap(function (e) {
            setupListView();
            e.preventDefault();
            e.stopPropagation();
            return false;
        });
    }
    您可以查看下面的 templates.home 模板:
    
    <h3>Please select an option:</h3>
    
    <a id="addNew" class="btn btn-info">Add New Record</a>
    <br/>
    <a id="queryMyRecords" class="btn btn-info">Query My Records</a>
    用户将会在手机上看到呈现的输出内容,如下所示。您可以看到,其中包含两个按钮,一个用于添加新记录,另一个用于查询 Database.com 中的现有记录。
    
    
    图 12. 呈现的输出
    接下来,我们来看一下当用户单击 “Add New Record” 按钮时会发生什么状况。当用户单击此按钮时,将会调用setupFormView() 按钮,从而创建新表单以便搜集用户数据。
    
    function setupFormView(data) {
        resetContainer();    
        var html =  Mustache.to_html( templates.form, data ); 
        container.html( html );
        currentLead = data;
        
        //request current location
        if ( !(data && data.Id) ) {
            header.html( "New Lead" );
            navigator.geolocation.getCurrentPosition(onGeoSuccess, onGeoError );
        }
        else {
            header.html( "Edit Lead" );
        }
            
        $('#save').tap( saveFormData );
        $('#cancel').tap( navigateBackFromFormView );
    }
    setupFormView()函数将清除容器元素,然后填入templtes.form模板中的 HTML。此时,您将会发现模板技术变得十分有用。接下来,让我们来看一下表单模板:
    
    <div id="form">
        <label for="first">First Name</label>
        <input id="first" type="text" value="{{First__c}}" />
    
        <br/>
        <label for="last">Last Name</label>
        <input id="last" type="text" value="{{Last__c}}" />
    
        <br/>
        <label for="phone">Telephone</label>
        <input id="phone" type="text" value="{{Telephone__c}}" />
    
        <br/>
        <label for="email">Email</label>
        <input id="email" type="text" value="{{Email__c}}" />
    
        <br/>
        <label for="notes">Notes</label>
        <textarea id="notes" type="text">{{Notes__c}}</textarea>
    
        <br/>
        <span id="location" class="alert alert-info">Location: {{Latitude__c}},{{Longitude__c}}</span>
    
        <br/>
        <br/>
        <a id="save" class="btn btn-success">Save</a>
        <a id="cancel" class="btn btn-danger">Cancel</a>
    </div>
    该表单包含用于生成用户界面的 HTML。双括号 "{{" 和 "}}" 中括住的值将填充为传递至 Mustache 模板引擎的数据。括号内包含的各值与传递至 Mustache.js 的数据对象属性相对应。
    
    用户界面的 HTML 字符串通过 Mustache.to_html() 函数生成。您可以在上方的 setupFormView 函数中看到,to_html()函数使用 data 参数生成模板 HTML。当创建新的潜在客户时,会向此函数传递一个空对象,因此该表单的 HTML 将包含空值。当编辑现有的潜在客户时,将会调用此函数,但会传入填充的数据对象。这将会重用同一 HTML 模板,但会填充传入的数据。
    
    当呈现在 PhoneGap 应用程序内时,您将会看到表单外观如下所示。当捕获新潜在客户时,通过 PhoneGap API 获取 GPS 位置。
    
    
    图 13. 新潜在客户表单显示 PhoneGap API 中的 GPS 位置
    用户可以输入适当的数据,然后单击 “Save” 或 “Cancel”。如果用户取消操作,则应用程序会将用户返回。然而,如果用户保存数据,则表示应用程序将数据推送至 Database.com。
    
    在saveFormData()JavaScript 函数内,从输入表单中检索数据,然后将数据分配至某个“数据”对象,数据也将发送至 Database.com。saveFormData() 函数既可用于创建新潜在客户,也可用于更新现有的潜在客户。如果存在 currentLead 变量,则用户编辑现有的潜在客户,否则用户创建新的潜在客户。如果用户正在创建新潜在客户,则调用 forcetkclient.create 函数,否则调用client.update 函数。
    
    function saveFormData( event ) {
        
        var data = {};
        data.First__c = $("#first").val();
        data.Last__c = $("#last").val();
        data.Telephone__c = $("#phone").val();
        data.Email__c = $("#email").val();
        data.Notes__c = $("#notes").val();
        
        if ( currentLead ) {
            //copy it back to the object in memory
            currentLead.First__c = data.First__c;
            currentLead.Last__c = data.Last__c;
            currentLead.Telephone__c = data.Telephone__c;
            currentLead.Email__c = data.Email__c;
            currentLead.Notes__c = data.Notes__c;
            
            //use the original lat/lon location
            data.Latitude__c = currentLead.Latitude__c;
            data.Longitude__c = currentLead.Longitude__c;
        }
        else if ( lastCoords ) {
            data.Latitude__c = lastCoords.latitude;
            data.Longitude__c = lastCoords.longitude;
        }
        try {
            if ( currentLead == undefined ) {
                sfw.client.create("Lead__C", data, saveDataSuccess, saveDataError );
            } else {
                sfw.client.update("Lead__C", currentLead.Id, data, saveDataSuccess, saveDataError );
            }
        } 
        catch(e){
            console.log(e);
        }
    }
    
    function saveDataSuccess( result ) {
        alert("Data Saved");
        navigateBackFromFormView();
    }
    
    function saveDataError( request, status, error){ 
        console.log( request.responseText ); 
        alert( request.responseText );
    }
    当调用 client.create(),时,您只需传递该类型的对象(数据对象)及成功和错误回调函数。当引用该类型的对象时,您可能已经注意到它是 "Lead__c",而不是 "Lead",您或许早已预料到。这是因为 Database.com 中的自定义对象和自定义数据字段必须使用 "__c" 后缀。
    
    当调用 client.update()时,您需要传递该类型的对象,并更新对象 ID,同时该数据对象包含新值及成功和错误回调函数。
    
    如果保存数据时发生错误,系统将会向用户显示消息。如果没有错误,系统会将用户返回至上一个视图。
    
    接下来,我们来看一下从 Database.com 检索数据的工作流程。从应用程序主屏幕中单击 "Query My Records" 按钮。这将会调用setupListView() JavaScript 函数。
    
    function setupListView() {
        resetContainer();
        
        var html = templates.list; 
        container.html( html );
        header.html( "Leads" );
        
        if(lastData) {
            renderListData();
        }
        else {
            queryRecords();
        }
        
        $('#cancel').tap( setupHomeView );
    }
    setupListView() 函数将清除容器,并填入 templates.list 模板中的 HTML。此模板并不实际显示数据,而是创建 dataContainer元素并在那里显示列表数据。
    
    <div id="dataContainer">loading...</div>
    <br/><br/>
    <a id="cancel" class="btn btn-danger" style="70%">Cancel</a>
    如果用户从编辑表单返回,将会呈现已有的内存数据。然而,对于新请求,将会调用 queryRecords()函数。
    
    从 Database.com 查询数据非常方便。当使用 forcetk.js 工具包时,您只需调用 client.query()方法,传入带有成功和错误回调函数的 SOQL 查询。SOQL是一种 Salesforce 对象查询语言,与其他数据库产品使用的 SQL(结构化查询语言)极其相似。SOQL 可让您从相关对象创建不计其数的自定义数据查询,正如您使用 SQL 可能出现的情况一样。Database.com 还包含一种在线 Workbench工具,可让您对 SOQL 查询进行测试,然后再将它们输入实际的应用程序。
    
    queryRecords()函数如下所示,在这里您可以看到,它会传入 SOQL 查询来检索数据:
    
    function queryRecords() {
        
        var query = "SELECT Email__c,First__c,Id,Last__c,Latitude__c,Longitude__c,Notes__c,Telephone__c "+
        "FROM Lead__c " + 
        "ORDER BY Last__c, First__c"
        
        sfw.client.query( query, onQuerySuccess, onQueryError );
    }
    
    function onQuerySuccess( response ) {
        
        lastData = { "records": response.records };
        renderListData();
    }
    
    function onQueryError( request, status, error ) {
        $("#dataContainer").html( "Error loading data: <br/>" + request.responseText );
    }
    从 Database.com 返回数据后,将会调用 onQuerySuccess 函数,而后随之调用renderListData() 函数。
    
    function renderListData() {
        if ( lastData ) {
            container.addClass("nopadding");
            var html = Mustache.to_html( templates.listItem, lastData ); 
            $("#dataContainer").html( html );
            $("#dataContainer").find("li").tap( onListItemTap );
            $("#cancel").tap( navigateBackFromListView );
        }
    }
    renderListData()函数基于从服务器返回的数据使用templates.listItem模板生成 HTML 列表,然后向所有 <li> 元素添加 "tap" 事件处理程序。
    
    接下来,让我们来看一下 templates.listItem模板的内容:
    
    <ul>
    {{#records}}
        <li id="{{Id}}">
            <strong>{{Last__c}}, {{First__c}}</strong>
            <div class="subtext">{{Email__c}} </div>
        </li>
    {{/records}}
    </ul>
    此模板指示 Mustache.js 遍历所有“记录”,并输出包含该潜在客户姓名和电子邮件地址的 <li> 元素。
    
    呈现数据后,您可能会看到类似于下方屏幕截图的界面。所有列表格式设置均以 CSS 语言处理,因此视觉效果更像是移动应用程序列表,而非项目符号 HTML 列表。
    
    
    图 14. CSS 格式设置将列表显示为移动应用程序列表
    当用户点按列表项时,将会调用 onListItemTap 函数。此函数将会获取与点按的列表项对应的 JavaScript 对象引用,而后调用前文所讨论的 setupFormView() 函数,传入适当的对象。
    
    function onListItemTap( event ) {
        
        var target = $( event.target )
        while (target.get(0).nodeName.toUpperCase() != "LI") {
            target=target.parent();
        }
        var id = target.attr("id");
        
        var data = getRecordById(id);    
        setupFormView( data );
        
        event.preventDefault();
        event.stopPropagation();
        
    
    function getRecordById( id ) {
        
        if ( !lastData  ) return;
        var records = lastData.records;
        
        for (var x=0; x<records.length; x++ ) {
            if (records[x].Id == id ) {
                return records[x];
            }
        }
    }
    由于重用了 setupFormView() 函数,它也会重用 templates.form模板,但填入从 Database.com 检索到的数据。请参阅下面的屏幕截图,看看显示填充数据的示例。
    
    
    图15. 重用 templates.form,填入从 Database.com 检索到的数据
    我们现在已经介绍完检索及保存 Database.com 数据的端到端解决方案,接下来谈谈部署问题。
    
    部署
    
    一项部署方案是导出 IDE 的应用程序存档。对于 iOS 应用程序,必须使用 Xcode on OS X;对于 Android 应用程序,必须使用 Eclipse(可以使用 Windows、Linux 或 OS X);对于 BlackBerry 应用程序,必须使用 BlackBerry 工具;而对于 Windows Phone,则必须使用 Visual Studio(仅限 Windows)。部署至多个平台意味着具有多个开发环境。
    
    PhoneGap Build将在此大显身手!PhoneGap Build 有助于开发人员上传代码,或者将 PhoneGap Build 指向 Git 或 SVN 库,PhoneGap Build 将执行基于云的编译,从而为用户提供 URL 以便下载特定于设备的应用程序二进制文件。
    
    在 Web 浏览器中,只需导航至 http://build.phonegap.com,然后登录即可。当您登录后,单击右上角的 "new app" 按钮。将会显示一个对话框,示意您上传代码或者指定 Git 或 SVN 库。
    
    
    图 16. 在 New app 中,上传代码或者指定 Git 或 SVN 库
    一旦 PhoneGap Build 能够访问您的代码,它将会自动执行基于云的编译,从而为您提供链接和 QR 代码供您下载应用程序二进制文件。
    
    
    图 17. 基于云的编译,其中提供了链接和 QR 代码供您下载应用程序二进制文件
    如果您在移动设备上使用 QR 代码阅读器,则可以通过捕捉图片直接将应用程序二进制文件下载至该设备。QR 代码阅读器将启动应用程序二进制文件下载,以便在您的设备上进行安装。
    
    现在您会有一个应用程序使用 Database.com 中的数据、在多台设备上运行,并构建于单一代码库之上。下图为此应用程序图像,它可同时在 Motorola Atrix (Android) 和 iPhone 4 (iOS) 上运行。
    
    
    图 18. 该应用程序可同时在 Motorola Atrix (Android) 和 iPhone 4 (iOS) 上运行
    T您可以在以下网址下载此应用程序的完整源代码:https://github.com/triceam/Database.Com-PhoneGap-Sample。请随意以此作为 PhoneGap 和 Database.com 支持的应用程序的学习起点。
    
    为什么选择 PhoneGap?
    
    如果仍然不确定 PhoneGap 是否适合您,请继续阅读本文,了解您可能需要在下一个项目中使用 PhoneGap 的几大原因。
    
    针对多个平台
    PhoneGap 可让您针对多个移动平台利用一个代码库。Web 已经通过 HTML、CSS 和 JavaScript 解决了跨平台应用程序问题。PhoneGap 充分利用 HTML、CSS 及 JavaScript 的普及性,协助您通过上述这些技术构建移动应用程序。
    
    利用现有技能
    由于 PhoneGap 应用程序均采用 HTML、CSS 和 JavaScript 构建而成,开发人员无需学习新型语言或开发范例。开发人员可重用自身先前熟悉的技能和工具快速高效地开发移动应用程序。这样既省时又省钱。
    
    重用现有工具
    由于 PhoneGap 应用程序利用 Web 技术,因此有数不清的现有二进制文件或框架可供您利用以加速应用程序开发。无论是 jQuery 或 Zepto 等解决方案加速器框架,Twitter Bootstrap、jQuery Mobile 或 Sencha 等用户界面工具包,还是 Raphael.js、Highcharts 或 RGraph.js 等数据可视化库,您的应用程序内部都有大量开源和商业资源可供利用。
    
    可扩展
    PhoneGap 提供了一些“开箱即用”的原生操作系统集成。但是,如果您要执行更多工作,它也可以完成。PhoneGap 的原生 API 构建于可扩展的基础之上,使开发人员能够创建可通过 JavaScript 应用程序调用的自定义原生代码。如果您愿意,可以让 PhoneGap“完成更多操作”。Github 上甚至还提供了大量开放源码框架: https://github.com/phonegap/phonegap-plugins。
    
    如果您需要一个插件来为连接至 Apple TV/Airplay 的 iOS 应用程序营造多屏幕体验,且这由 JavaScript 全面控制,那么 有一个专门的插件:
    
    
    图 19. 专为连接至 Apple TV/Airplay 的 iOS 应用程序提供多屏幕体验的插件
    如果您要集成条形码扫描仪、分析、推送通知、消息和通知或广告网络,我们也提供了相应的插件 (及许多其他插件)。
    
    如果您要使用 PhoneGap Web 视图作为本机应用程序组件,同样也可以实现。
    
    PhoneGap 是一种工具,供开发人员创建能够以各种方式使用的应用程序,并使用各种熟悉的可扩展技术实施开发。
    
    开放源码
    PhoneGap 是一种完全免费的开放源码技术。PhoneGap 的整个代码库均可作为 Apache Cordova项目的一部分进行免费访问。如果您要添加或调整功能,可以采用这项技术。如果您要在 PhoneGap 之上构建工具,也可以采用这项技术。如果您发现了一个错误并希望为采用 PhoneGap 技术的任何其他人修复该错误,那么完全有能力实现(我们鼓励这样做)!
    
    下一步阅读方向
    
    关于PhoneGap的更多内容,请访问PhoneGap Build页面。学习Database.com的更多特性,请访问其开发者主页。
  • 相关阅读:
    Laravel框架之Session操作
    Laravel框架之Response操作
    Laravel之简单的学生信息管理平台
    Laravel中的模板引擎Blade
    Laravel中的查询构造器
    Laravel中使用模型对数据进行操作
    Laravel中的模型的创建
    springboot
    不丢失log的情况下迁移git空间
    Vue2.0中v-for迭代语法变化(key、index)
  • 原文地址:https://www.cnblogs.com/huidaoli/p/3824612.html
Copyright © 2020-2023  润新知