65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 JQuery Mobile & WebSQL 创建 CRUD Web 应用

starIconstarIconstarIconstarIconstarIcon

5.00/5 (5投票s)

2015年3月29日

CPOL

11分钟阅读

viewsIcon

36749

downloadIcon

1243

演示如何使用 SQLite 数据库进行移动开发

引言

我将演示如何使用 JQuery Mobile 和 WebSQL 创建一个 CRUD 移动 Web 应用程序。尽管 WebSQL 的未来似乎正在消退,因为它不再被 W3C 支持,而 IndexedDB 正在被更多地采用。WebSQL 是一个关系数据库,存在于浏览器中,不幸的是 IE 和 FireFox 不支持它,但任何与 Webkit 相关的浏览器都具备其功能。

为了实现这一点,我们将创建一个 JavaScript 文件来处理所有数据库操作,例如创建数据库、创建表、创建索引、插入记录、更新记录、删除记录以及检索表中的所有记录。

一些不太重要的假设:我将假设您了解一些 WebSQL 知识以及如何执行 SQL 查询。我还将假设您了解一些 JQuery Mobile 开发知识以及如何通过代码将 JavaScript 文件链接到您的 HTML 文件。

下载 AddressBookSQL.zip

背景

CRUD 应用程序基本上是为三件事而开发的:CR - 创建记录,U - 更新记录,D - 删除记录。过去,我写过几篇文章,主要讨论了如何使用不同的框架执行这些功能。它们是:

  1. 使用 LocalStorage 进行 CRUD
  2. 使用单个 JSON 和 PHP 进行 Web 服务器 CRUD

以及现在的这个框架,使用 WebSQL。有关 WebSQL 的更多详细信息,您可以阅读此 wiki 文档 此处

使用代码

WEBSQL.JS - 数据库维护

通过关注分离,我将读取和写入数据库的代码分离到一个 JavaScript 文件中。这确保了只有对象可以传递给 WebSQL 文件来管理记录操作。我将首先讨论源代码中附带的 websql.js 文件以及如何在您自己的应用程序中使用它。

// variables for database table maintenance
var DB_REAL = "REAL";
var DB_INTEGER = "INTEGER";
var DB_BLOB = "BLOB";
var DB_TEXT = "TEXT";
var DB_FLOAT = "FLOAT";
var DB_NUMERIC = "NUMERIC";

这些是为 WebSQL 数据库创建的各种字段类型,然而在此示例应用程序中最常用的是 DB_TEXT。我将它们声明为我的应用程序代码可以访问的常量。

function Left(str, n) {
    // return a left part of a string
    var s = str + '';
    var iLen = s.length;
    if (n <= 0) {
        return "";
        } else if (n >= iLen) {
        return str;
        } else {
        return s.substr(0, n);
    }
}

这是一个辅助函数,用于返回字符串的左侧部分,例如 Left("My Name", 2) 将返回 "My"。

function Len(str) {
    // return the length of a string
    if (typeof (str) === 'object') {
        return str.length;
    }
    str += '';
    return str.length;
}

这也是另一个用于返回字符串长度的辅助函数,例如 Len("Anele Mbanga") 将返回 12。它由 WebSQL 脚本文件用于维护数据库。

创建和打开数据库

function SqlOpenDb(shortName, version, displayName, maxSize) {
    // code to open the database and returns a variable, one can open different
    // databases, database size is 1MB, increase dbsize to <= 5
    var db, dbsize = 1;
    try {
        if (!window.openDatabase) {
            return 0;
            } else {
            if (typeof (shortName) === 'undefined') {
                return 0;
            }
            if (typeof (version) === 'undefined') version = "";
            if (typeof (displayName) === 'undefined') displayName = shortName;
            if (typeof (maxSize) === 'undefined') maxSize = dbsize * (1024 * 1024);
            db = openDatabase(shortName, version, displayName, maxSize);
        }
        } catch (e) {
        return 0;
    }
    return db;
}

SqlOpenDb 脚本是创建和打开数据库(如果不存在)的主要脚本。截至撰写本文时,websql 的最大数据库大小为 5MB。此函数的使用方式是,该函数返回一个对象,该对象将包含传递给数据库的所有函数。如果浏览器不支持 WebSQL,则返回零。

要打开数据库,可以像这样调用此函数...

var db = SqlOpenDb("AddressBook");

这将会在浏览器中创建一个 1MB 的数据库来存储您所有的记录。打开数据库后,您需要创建将存储信息的表,然后在此插入记录。我忘了提,这里调用数据库维护函数的方式是通过使用 JQuery 的 deferred 方法。我发现这有助于避免创建额外的函数来调用 execute 方法的 successfailure。这意味着会调用一个数据库事务,在事务完成后,根据成功或失败可以执行其他函数。您很快就会看到。

创建表

function SqlCreateTable(db, TableName, FieldsAndTypes, PrimaryKey, AutoIncrement) {
    // code to create a table in the websql database
    // fieldsandtypes is a json object
    // autoincrement is the field name to autoincrement
    var sb = "(";
    for (item in FieldsAndTypes) {
        sb += "[" + item + "] " + FieldsAndTypes[item];
        if (item == PrimaryKey) {
            sb += " NOT NULL PRIMARY KEY";
        }
        if (item == AutoIncrement) {
            sb += " AUTOINCREMENT";
        }
        sb += ", ";
    }
    sb = Left(sb, (Len(sb) - 2));
    sb += ")";
    sb = "CREATE TABLE IF NOT EXISTS [" + TableName + "] " + sb + ";";
    return Execute(db, sb);
}

上面的方法是用于在数据库中创建表的方法。此方法会检查数据库表是否存在,如果不存在则创建。如您所见,之前定义的 LeftLen 方法在此函数中使用。

要创建表,您需要将 SqlOpenDb 函数中使用的数据库对象、要创建的表名、FieldsAndTypes(将作为主键的字段名)以及将自增的字段名传递进去。请记住,JavaScript 是区分大小写的。此函数循环遍历传递的对象,获取字段名、字段类型,并构建 CREATE TABLE 语句。如果给定的 PrimaryKey 与给定的任何字段名匹配,则会将其标记为是,自增字段也同样如此。

指定字段名和类型

创建表时,您需要先指定字段名和类型,然后再调用方法。这实际上比您想象的要容易,但是如果您查看上面的源代码,您会知道您只需传递一个可以循环的对象。让我们在下面的示例中定义要传递的对象。我们将创建一个带有几个字段的联系人表。

var FT = {};

FT.FullName = DB_TEXT;

FT.MobileNumber = DB_NUMERIC;

SqlCreateTable(db, "Contacts", FT, "FullName", "");

将此放入一个函数中,将轻松创建您的表,并且如我所说,如果它不存在。

插入记录

我们已经打开了数据库,创建了表,现在是时候向其中添加记录了。这里遵循与创建表相同的流程。

function SqlInsertRecord(db, tblName, tblRecord) {
    // code to insert a record into the database
    // fields are passed as parameters
    var qry, flds = "", vals = "", avals = [];
    for (var key in tblRecord) {
        flds += "[" + key + "],";
        vals += "?,";
        avals.push(tblRecord[key]);
    }
    flds = Left(flds, Len(flds) - 1);
    vals = Left(vals, Len(vals) - 1);
    qry = "INSERT INTO [" + tblName + "] (" + flds + ") VALUES (" + vals + ");";
    return Execute(db, qry, avals);
}

SqlInsertRecord 会接收数据库对象、要更新的表名以及要添加的记录对象。您可以这样定义 tblRecord:

var tblRec = {};

tblRec.FullName = "Anele Mbanga";

tblRec.MobileNumber = 123456789

SqlInsertRecord(db, "Contacts", tblRec);

如您所见,数据库方法相当直接,但不同之处在于传递给它们以维护数据库中记录的对象。

从表中获取记录

现在您已经打开了数据库,创建了表,并添加了一条记录,如何读取它?从表中读取的记录将作为 json 对象返回。您可以读取单条记录或返回表中的所有记录。

function SqlGetRecordWhere(db, tblName, tblWhere) {
    // code to get a record from database using a where clause
    // tblWhere should be objects
    var qry = "", vals = "", avals = [];
    for (item in tblWhere) {
        vals += "[" + item + "] = ? AND ";
        avals.push(tblWhere[item]);
    }
    vals = Left(vals, Len(vals) - 5);
    qry = "SELECT * FROM [" + tblName + "] WHERE " + vals + ";";
    return Execute(db, qry, avals);
}

SqlGetRecordWhere 将根据您的 WHERE 子句从表中返回单条/多条记录。tblWhere 子句也是一个对象,我们将其解析以返回我们的记录。这使用了 参数化 查询从数据库中提取记录。您还需要传递数据库对象、表名和 where 对象子句。例如,要返回 FullName = "Anele Mbanga" 的记录,您可以这样定义 where 子句。

var rWhere = {};

rWhere.FullName = "Anele Mbanga";

SqlGetRecordWhere(db, "Contacts", rWhere);

// return all records from a table sorted by primary key

function SqlGetRecords(db, TableName, PrimaryKey) {
    // return all records from a table ordered by primary key
    var qry = "SELECT * FROM [" + TableName + "] ORDER BY [" + PrimaryKey +"]";
    return Execute(db, qry);
};

要从数据库中返回所有记录,我们的代码将是:

SqlGetRecords(db, "Contacts", "FullName");

更新现有记录

要更新现有记录,我们向 SqlUpdateRecordWhere 传递几个对象:1. 我们要更新的字段,以及上面的 WHERE 子句,数据库对象和表名。

function SqlUpdateRecordWhere(db, tblName, tblRecord, tblWhere) {
    // code to update a record on a database
    // tblRecord and tblWhere should be objects
    var qry = "", vals = "", wvals = "", avals = [];
    for (item in tblRecord) {
        vals += "[" + item + "] = ?,";
        avals.push(tblRecord[item]);
    }
    for (item in tblWhere) {
        wvals += "[" + item + "] = ? AND ";
        avals.push(tblWhere[item]);
    }
    vals = Left(vals, Len(vals) - 1);
    wvals = Left(wvals, Len(wvals) - 5);
    qry = "UPDATE [" + tblName + "] SET " + vals + " WHERE " + wvals + ";";
    return Execute(db, qry, avals);
}

这将创建 UPDATE 语句来更新表,然后运行 Execute 来更新表。例如,要更新我们联系人的 MobileNumber,我们将定义我们的流程如下。

var rM = {}, rW = {};

rM.MobileNumber = 98765432

rW.FullName = "Anele Mbanga";

SqlUpdateRecordWhere(db, "Contacts", rM, rW);

删除现有记录。

要删除现有记录,您也像上面的示例一样传递要删除的对象。

function SqlDeleteRecordWhere(db, tblName, tblWhere) {
    // delete a record from a table using a where clause
    // pass the where fields as parameters
    var qry, wvals = "", avals = [];
    for (item in tblWhere) {
        wvals += "[" + item + "] = ? AND ";
        avals.push(tblWhere[item]);
    }
    // remove last ' AND '
    wvals = Left(wvals, Len(wvals) - 5);
    qry = "DELETE FROM [" + tblName + "] WHERE " + wvals + ";";
    return Execute(db, qry, avals);
};

该函数循环遍历指定的每个字段并构建 DELETE 语句。删除记录的示例如下:

var dR = {};

dR.FullName = "Anele Mbanga";

SqlDeleteRecordWhere(db, "Contacts", dR);

返回不重复字段

有时您可能想从数据库中返回一个不重复的字段,

function SqlGetDistinctField(db, TableName, FldName) {
    // return distinct records from a table
    var qry = "SELECT DISTINCT [" + FldName + "] FROM [" + TableName + "] ORDER BY [" + FldName +"]";
    return Execute(db, qry);
};

SqlGetDistinctField 将会有所帮助。如您所见,您可以定义自己的 SQL 语句并将其传递给 Execute 函数。那么,我们来解释一下 Execute 函数。

Execute 方法是我们在这里以延迟方式执行数据库 SQL 语句的主要函数。我来解释一下。

function Execute(db, qry, args){
    // execute a query against the database using defer
    if (typeof (args) === 'undefined') args = [];
    return $.Deferred(function (d) {
        db.transaction(function (tx) {
            tx.executeSql(qry, args, successWrapper(d), failureWrapper(d));
        });
    });
};

execute 方法会接收数据库对象、我们要执行的查询字符串以及一个包含我们要处理的参数的数组。这会返回一个 JQuery Deferred 对象,当我们的操作成功或失败时,该对象会分别通过 successWrapperfailureWrapper 进行处理。

function successWrapper(d) {
    // when sql query succeeds
    return (function (tx, data) {
        d.resolve(data)
    })
};

function failureWrapper(d) {
    // when sql query fails
    return (function (tx, error) {
        d.reject(error)
    })
};

当我们的数据库操作成功时,将返回一个 ResultSet 作为数据对象,并可以传递和处理。在大多数情况下,您会希望将结果集的所有数据作为简单对象返回。由于我希望我的代码易于兼容,无论我是开发 LocalStorage 还是像前几篇文章中演示的 Php ajax 调用,我都遵循了相同的模式,并将其转换为 JSON 数组。

function ResultSetToJSON(results, PrimaryKey) {
    // process data returned by successWrapper;
    // return it as a json object using primary key as key
    var Records = {};
    var len = results.rows.length - 1, priKey, i, row;
    // loop through each row
    for (i = 0; i <= len; i++) {
        // get the row
        row = results.rows.item(i);
        // get the primary key
        priKey = row[PrimaryKey];
        // cleanse the primary key
        priKey = priKey.split(' ').join('-');
        // set row to object using primary key
        Records[priKey] = row;
    }
    return Records;
}

通过此函数,successWrapper 返回的数据将传递给 ResultSetToJSON,指定主键以返回一个 JSON 对象,该对象可以通过您表中所有记录的每个主键来访问。

我们已详细解释了数据库操作,现在让我们创建用户界面来使用这些操作。让我们创建一个简单的地址簿 CRUD 应用。

用户界面

图 1 - 创建/更新新联系人详细信息

图 2 - 联系人地址详细信息

图 3 - 地址簿列表

上图显示了我们将要创建的用户界面,以演示我们的 CRUD 应用。我们希望捕获地址详细信息并保存它们,同时也能在需要时删除地址。

HTML 定义 - 添加联系人屏幕

<div id="pgAddContact" data-role="page">
                <div data-position="left" data-display="reveal" data-position-fixed="true" id="pgAddContactPnl" data-role="panel">
                    <ul data-role="listview" id="pgAddContactPnlLV">
                        <li ><a data-transition="slide" href="#pgAddContact">New</a></li>
                        <li ><a data-transition="slide" href="#pgContactMindCity">Relationships > City</a></li>
                        <li ><a data-transition="slide" href="#pgRptContact">Report</a></li>
                        <li ><a data-transition="slide" href="#pgContact">Back</a></li>
                    </ul>
                </div>
                
                <header id="pgAddContactHdr" data-role="header" data-position="fixed">
                    <h1>Address Book</h1>
                    <a data-role="button" id="pgAddContactMenu" href="#pgAddContactPnl" data-icon="bars" class="ui-btn-left">Menu</a>
                    <div id="pgAddContactHdrNavBar" data-role="navbar">
                        <ul>
                            <li><a href="#" data-href="pgAddContactDetails" id="pgAddContactDetailsBtn" class="ui-btn-active">Details</a>
                            </li>
                            <li><a href="#" data-href="pgAddContactAddress" id="pgAddContactAddressBtn">Address</a>
                            </li>
                        </ul>
                    </div>
                </header>
                <div id="pgAddContactCnt" data-role="content">
                    <h3>Add Contact</h3><div id="pgAddContactDetails" class="tab-content">
                        <div data-role="fieldcontain">
                            <label for="pgAddContactFullName">Full Name<span style='color:red;'>*</span></label>
                            <input title="Enter full name here." type="text" name="pgAddContactFullName" id="pgAddContactFullName" placeholder="Enter full name here." autocomplete="off" data-clear-btn="true" class="required"></input>
                        </div>
                        <div data-role="fieldcontain">
                            <label for="pgAddContactCompany">Company<span style='color:red;'>*</span></label>
                            <input title="Enter company here." type="text" name="pgAddContactCompany" id="pgAddContactCompany" placeholder="Enter company here." autocomplete="off" data-clear-btn="true" class="required"></input>
                        </div>
                        <div data-role="fieldcontain">
                            <label for="pgAddContactJobTitle">Job Title<span style='color:red;'>*</span></label>
                            <input title="Enter job title here." type="text" name="pgAddContactJobTitle" id="pgAddContactJobTitle" placeholder="Enter job title here." autocomplete="off" data-clear-btn="true" class="required"></input>
                        </div>
                        <div data-role="fieldcontain">
                            <label for="pgAddContactEmailAddress">Email Address<span style='color:red;'>*</span></label>
                            <input title="Enter email address here." type="email" name="pgAddContactEmailAddress" id="pgAddContactEmailAddress" placeholder="Enter email address here." autocomplete="off" data-clear-btn="true" class="required"></input>
                        </div>
                        <div data-role="fieldcontain">
                            <label for="pgAddContactBusinessPhone">Business Phone</label>
                            <input type="tel" pattern="\d\d\d \d\d\d \d\d\d\d" name="pgAddContactBusinessPhone" id="pgAddContactBusinessPhone" placeholder="Enter business phone here." autocomplete="off" data-clear-btn="true"></input>
                        </div>
                        <div data-role="fieldcontain">
                            <label for="pgAddContactBusinessFax">Business Fax</label>
                            <input type="tel" pattern="\d\d\d \d\d\d \d\d\d\d" name="pgAddContactBusinessFax" id="pgAddContactBusinessFax" placeholder="Enter business fax here." autocomplete="off" data-clear-btn="true"></input>
                        </div>
                        <div data-role="fieldcontain">
                            <label for="pgAddContactMobilePhone">Mobile Phone</label>
                            <input type="tel" pattern="\d\d\d \d\d\d \d\d\d\d" name="pgAddContactMobilePhone" id="pgAddContactMobilePhone" placeholder="Enter mobile phone here." autocomplete="off" data-clear-btn="true"></input>
                        </div>
                        <div data-role="fieldcontain">
                            <label for="pgAddContactHomePhone">Home Phone</label>
                            <input type="tel" pattern="\d\d\d \d\d\d \d\d\d\d" name="pgAddContactHomePhone" id="pgAddContactHomePhone" placeholder="Enter home phone here." autocomplete="off" data-clear-btn="true"></input>
                        </div>
                    </div>
                    <div id="pgAddContactAddress" class="tab-content">
                        <div data-role="fieldcontain">
                            <label for="pgAddContactStreetAddress1">Street Address 1</label>
                            <input type="text" name="pgAddContactStreetAddress1" id="pgAddContactStreetAddress1" placeholder="Enter street address 1 here." autocomplete="off" data-clear-btn="true"></input>
                        </div>
                        <div data-role="fieldcontain">
                            <label for="pgAddContactStreetAddress2">Street Address 2</label>
                            <input type="text" name="pgAddContactStreetAddress2" id="pgAddContactStreetAddress2" placeholder="Enter street address 2 here." autocomplete="off" data-clear-btn="true"></input>
                        </div>
                        <div data-role="fieldcontain">
                            <label for="pgAddContactCity">City</label>
                            <input type="text" name="pgAddContactCity" id="pgAddContactCity" placeholder="Enter city here." autocomplete="off" data-clear-btn="true"></input>
                        </div>
                        <div data-role="fieldcontain">
                            <label for="pgAddContactState">State</label>
                            <input type="text" name="pgAddContactState" id="pgAddContactState" placeholder="Enter state here." autocomplete="off" data-clear-btn="true"></input>
                        </div>
                        <div data-role="fieldcontain">
                            <label for="pgAddContactProvince">Province</label>
                            <input type="text" name="pgAddContactProvince" id="pgAddContactProvince" placeholder="Enter province here." autocomplete="off" data-clear-btn="true"></input>
                        </div>
                        <div data-role="fieldcontain">
                            <label for="pgAddContactPostalCode">Postal Code</label>
                            <input type="number" name="pgAddContactPostalCode" id="pgAddContactPostalCode" placeholder="Enter postal code here." autocomplete="off" data-clear-btn="true"></input>
                        </div>
                    </div>
                </div>
                
                <footer id="pgAddContactFtr" data-role="footer" data-position="fixed">
                    <div id="pgAddContactFtrNavBar" data-role="navbar">
                        <ul>
                            <li><a id="pgAddContactBack" data-icon="carat-l">Cancel</a>
                            </li>
                            <li><a type="submit" id="pgAddContactSave" data-icon="action">Save</a>
                            </li>
                        </ul>
                    </div>
                </footer></div>

前两篇文章详细讨论了如何创建视图,此处遵循了相同的方法。此视图有一个左侧面板,在选择菜单时显示。页脚有一个带有“取消”和“保存”按钮的导航栏。“取消”会带您到联系人列表,“保存”会将新联系人插入数据库。

在“添加联系人”页面上单击保存按钮时执行的代码如下:

JavaScript - 添加联系人

// Save click event on Add page
            $('#pgAddContactSave').on('click', function (e) {
                e.preventDefault();
                e.stopImmediatePropagation();
                // save the Contact
                var ContactRec;
                //get form contents into an object
                ContactRec = pgAddContactGetRec();
                //save object to SQL
                app.addContact(ContactRec);
            });

用户输入的数据被读取到一个对象中并存储到 pgAddContactGetRec 中,然后将其传递给 app.addContact,后者将记录插入数据库。

// add a new record to SQL storage.
        app.addContact = function (ContactRec) {
            // define a record object to store the current details
            var FullName = ContactRec.FullName;
            // cleanse the record key of spaces.
            FullName = FullName.split(' ').join('-');
            ContactRec.FullName = FullName;
            // store the json object in the database
            $.when(SqlInsertRecord(dbAddressBook, "Contact", ContactRec)).done(function () {
                //show a toast message that the record has been saved
                toastr.success('Contact record successfully saved.', 'Address Book');
                //find which page are we coming from, if from sign in go back to it
                var pgFrom = $('#pgAddContact').data('from');
                switch (pgFrom) {
                    case "pgSignIn":
                    $.mobile.changePage('#pgSignIn', {transition: pgtransition});
                    break;
                    default:
                    // clear the edit page form fields
                    pgAddContactClear();
                    //stay in the same page to add more records
                }
                }).fail(function (err) {
                //show a toast message that the record has not been saved
                toastr.success('Contact record NOT successfully saved.', 'Address Book');
            });
        };
// store the json object in the database
            $.when(SqlInsertRecord(dbAddressBook, "Contact", ContactRec)).done(function () {

插入记录到数据库的关键一行就是上面这一行。因为我们使用的是 websql 维护的延迟方法,所以在 $.when .done.fail 语句之间调用数据库方法来执行。这基本上意味着,当 SqlInsertRecord 方法执行完毕时,运行 done 后面的函数中的代码;如果失败,则执行 failure 中的操作。在这种情况下,当 Web 方法完成时,会显示一个提示消息“已保存”,如果失败,则显示一个提示消息“无法保存记录”。

HTML 定义 - 更新联系人屏幕

<div data-url="FullName" id="pgEditContact" data-role="page">
                    <div data-position="left" data-display="reveal" data-position-fixed="true" id="pgEditContactPnl" data-role="panel">
                        <ul data-role="listview" id="pgEditContactPnlLV">
                            <li ><a data-transition="slide" href="#pgAddContact">New</a></li>
                            <li ><a data-transition="slide" href="#pgContactMindCity">Relationships > City</a></li>
                            <li ><a data-transition="slide" href="#pgRptContact">Report</a></li>
                            <li ><a data-transition="slide" href="#pgContact">Back</a></li>
                        </ul>
                    </div>
                    
                    <header id="pgEditContactHdr" data-role="header" data-position="fixed">
                        <h1>Address Book</h1>
                        <a data-role="button" id="pgEditContactMenu" href="#pgEditContactPnl" data-icon="bars" class="ui-btn-left">Menu</a>
                        <div id="pgEditContactHdrNavBar" data-role="navbar">
                            <ul>
                                <li><a href="#" data-href="pgEditContactDetails" id="pgEditContactDetailsBtn" class="ui-btn-active">Details</a>
                                </li>
                                <li><a href="#" data-href="pgEditContactAddress" id="pgEditContactAddressBtn">Address</a>
                                </li>
                            </ul>
                        </div>
                    </header>
                    <div id="pgEditContactCnt" data-role="content">
                        <h3>Edit Contact</h3><div id="pgEditContactDetails" class="tab-content">
                            <div data-role="fieldcontain">
                                <label for="pgEditContactFullName">Full Name<span style='color:red;'>*</span></label>
                                <input readonly="readonly" data-clear-btn="true" autofocus="true" title="Enter full name here." type="text" name="pgEditContactFullName" id="pgEditContactFullName" placeholder="Enter full name here." autocomplete="off" class="required"></input>
                            </div>
                            <div data-role="fieldcontain">
                                <label for="pgEditContactCompany">Company<span style='color:red;'>*</span></label>
                                <input title="Enter company here." type="text" name="pgEditContactCompany" id="pgEditContactCompany" placeholder="Enter company here." autocomplete="off" data-clear-btn="true" class="required"></input>
                            </div>
                            <div data-role="fieldcontain">
                                <label for="pgEditContactJobTitle">Job Title<span style='color:red;'>*</span></label>
                                <input title="Enter job title here." type="text" name="pgEditContactJobTitle" id="pgEditContactJobTitle" placeholder="Enter job title here." autocomplete="off" data-clear-btn="true" class="required"></input>
                            </div>
                            <div data-role="fieldcontain">
                                <label for="pgEditContactEmailAddress">Email Address<span style='color:red;'>*</span></label>
                                <input title="Enter email address here." type="email" name="pgEditContactEmailAddress" id="pgEditContactEmailAddress" placeholder="Enter email address here." autocomplete="off" data-clear-btn="true" class="required"></input>
                            </div>
                            <div data-role="fieldcontain">
                                <label for="pgEditContactBusinessPhone">Business Phone</label>
                                <input type="tel" pattern="\d\d\d \d\d\d \d\d\d\d" name="pgEditContactBusinessPhone" id="pgEditContactBusinessPhone" placeholder="Enter business phone here." autocomplete="off" data-clear-btn="true"></input>
                            </div>
                            <div data-role="fieldcontain">
                                <label for="pgEditContactBusinessFax">Business Fax</label>
                                <input type="tel" pattern="\d\d\d \d\d\d \d\d\d\d" name="pgEditContactBusinessFax" id="pgEditContactBusinessFax" placeholder="Enter business fax here." autocomplete="off" data-clear-btn="true"></input>
                            </div>
                            <div data-role="fieldcontain">
                                <label for="pgEditContactMobilePhone">Mobile Phone</label>
                                <input type="tel" pattern="\d\d\d \d\d\d \d\d\d\d" name="pgEditContactMobilePhone" id="pgEditContactMobilePhone" placeholder="Enter mobile phone here." autocomplete="off" data-clear-btn="true"></input>
                            </div>
                            <div data-role="fieldcontain">
                                <label for="pgEditContactHomePhone">Home Phone</label>
                                <input type="tel" pattern="\d\d\d \d\d\d \d\d\d\d" name="pgEditContactHomePhone" id="pgEditContactHomePhone" placeholder="Enter home phone here." autocomplete="off" data-clear-btn="true"></input>
                            </div>
                        </div>
                        <div id="pgEditContactAddress" class="tab-content">
                            <div data-role="fieldcontain">
                                <label for="pgEditContactStreetAddress1">Street Address 1</label>
                                <input type="text" name="pgEditContactStreetAddress1" id="pgEditContactStreetAddress1" placeholder="Enter street address 1 here." autocomplete="off" data-clear-btn="true"></input>
                            </div>
                            <div data-role="fieldcontain">
                                <label for="pgEditContactStreetAddress2">Street Address 2</label>
                                <input type="text" name="pgEditContactStreetAddress2" id="pgEditContactStreetAddress2" placeholder="Enter street address 2 here." autocomplete="off" data-clear-btn="true"></input>
                            </div>
                            <div data-role="fieldcontain">
                                <label for="pgEditContactCity">City</label>
                                <input type="text" name="pgEditContactCity" id="pgEditContactCity" placeholder="Enter city here." autocomplete="off" data-clear-btn="true"></input>
                            </div>
                            <div data-role="fieldcontain">
                                <label for="pgEditContactState">State</label>
                                <input type="text" name="pgEditContactState" id="pgEditContactState" placeholder="Enter state here." autocomplete="off" data-clear-btn="true"></input>
                            </div>
                            <div data-role="fieldcontain">
                                <label for="pgEditContactProvince">Province</label>
                                <input type="text" name="pgEditContactProvince" id="pgEditContactProvince" placeholder="Enter province here." autocomplete="off" data-clear-btn="true"></input>
                            </div>
                            <div data-role="fieldcontain">
                                <label for="pgEditContactPostalCode">Postal Code</label>
                                <input type="number" name="pgEditContactPostalCode" id="pgEditContactPostalCode" placeholder="Enter postal code here." autocomplete="off" data-clear-btn="true"></input>
                            </div>
                        </div>
                    </div>
                    
                    <footer id="pgEditContactFtr" data-role="footer" data-position="fixed">
                        <div id="pgEditContactFtrNavBar" data-role="navbar">
                            <ul>
                                <li><a id="pgEditContactBack" data-icon="carat-l">Cancel</a>
                                </li>
                                <li><a type="submit" id="pgEditContactUpdate" data-icon="action">Update</a>
                                </li>
                                <li><a id="pgEditContactDelete" data-icon="delete">Delete</a>
                                </li>
                            </ul>
                        </div>
                    </footer></div>

要访问“联系人更新”屏幕,需要从 ListView 列表中选择一个联系人,然后会出现此屏幕。用户随后可以更新联系人详细信息或选择删除以删除联系人。

JavaScript - 更新联系人

当用户更新联系人时,屏幕上的详细信息会被读取并保存为一个对象,该对象被传递给我们 app.js 文件中的 update 方法。

// Update click event on Edit Page
            $('#pgEditContactUpdate').on('click', function (e) {
                e.preventDefault();
                e.stopImmediatePropagation();
                // save the Contact
                var ContactRecNew;
                //get contents of Edit page controls
                ContactRecNew = pgEditContactGetRec();
                //save updated records to SQL
                app.updateContact(ContactRecNew);
            });

一旦单击“更新”按钮,联系人详细信息就会被保存到 app.updateContact 中,让我们看看 app.updateContact 做什么。

//update an existing record and save to SQL
        app.updateContact = function (ContactRec) {
            // lookup specific Contact
            var FullName = ContactRec.FullName;
            //cleanse the key of spaces
            FullName = FullName.split(' ').join('-');
            //define the record to update
            var ContactUpdate = {};
            ContactUpdate.FullName = FullName;
            $.when(SqlUpdateRecordWhere(dbAddressBook, "Contact", ContactRec, ContactUpdate)).done(function () {
                //record has been saved
                toastr.success('Contact record updated.', 'Address Book');
                // clear the edit page form fields
                pgEditContactClear();
                // show the records listing page.
                $.mobile.changePage('#pgContact', {transition: pgtransition});
                }).fail(function (err) {
                toastr.error('Contact record not updated, please try again.', 'Address Book');
                return;
            });
        };

这里,我们也延迟执行直到完成。$.when SqlUpdateRecordWhere,使用该人员的 FullName 更新联系人详细信息后,会显示一条提示消息“联系人详细信息已更新”,否则会提示“用户详细信息未更新”。

JavaScript - 删除联系人

从更新联系人屏幕,如果用户选择删除联系人,单击“删除”按钮将触发一系列事件。系统会提示用户是否要删除联系人,如果用户单击“是”,联系人详细信息将从 websql 数据库中删除。消息框已在前几篇文章中讨论过,此处不再重复,请参考前文。

联系人是通过主键删除的,在本例中为主键是 FullName。

//delete a record from SQL using record key
        app.deleteContact = function (FullName) {
            FullName = FullName.split(' ').join('-');
            //define the record to delete
            var ContactDelete = {};
            ContactDelete.FullName = FullName;
            $.when(SqlDeleteRecordWhere(dbAddressBook, "Contact", ContactDelete)).done(function () {
                //record has been deleted
                toastr.success('Contact record deleted.', 'Address Book');
                // show the page to display after a record is deleted, this case listing page
                $.mobile.changePage('#pgContact', {transition: pgtransition});
                }).fail(function (err) {
                toastr.error('Contact record not deleted, please try again.', 'Address Book');
                return;
            });
        };

我们首先使用 FullName 构建 where 子句,然后执行 SqlDeleteRecordWhere。这样,在删除联系人时,会显示一条提示消息,然后用户会被带到联系人列表屏幕。

您可以参考之前的文章了解如何列出记录,但为了本文的需要,我们将只讨论如何使用 CheckStorageMethod 获取数据库中的所有记录。

在所有联系人列出在 ListView 之前,会执行 checkForContactStorage 函数。此函数会读取数据库中的所有记录并将它们加载到 listview 中。

JavaScript - 获取所有联系人并显示它们

//display records if they exist or tell user no records exist.
        app.checkForContactStorage = function () {
            //get records from SQL.
            //when returned, parse then as json object
            var ContactObj = {};
            $.when(SqlGetRecords(dbAddressBook, "Contact", "FullName")).done(function (dta) {
                // return json object of all records
                ContactObj = ResultSetToJSON(dta, "FullName");
                // are there existing Contact records?
                if (!$.isEmptyObject(ContactObj)) {
                    // yes there are. pass them off to be displayed
                    app.displayContact(ContactObj);
                    } else {
                    // nope, just show the placeholder
                    $('#pgContactList').html(ContactHdr + noContact).listview('refresh');
                }
                }).fail(function (err) {
                //just show the placeholder
                $('#pgContactList').html(ContactHdr + noContact).listview('refresh');
            });
        };

$.when SqlGetRecords .done 后,dta 返回 ResultSet,该 ResultSet 被传递给 ResultSetToJSON,其中包含 contacts 数据库中的所有记录。找到这些记录后,它们会通过 app.displayContact(ContactObj); 显示,并且 listview 会被刷新。

JavaScript - 创建数据库和表

在执行所有这些 CRUD 操作之前,应该为该应用程序创建数据库和相应的表。这应该在 init 时进行。

1. 我们打开了数据库

//open the websql database
            dbAddressBook = SqlOpenDb("AddressBook");

2. 我们创建了表,先定义它,然后调用函数来创建它。

//create a websql table for SQL-Contact
            app.ContactSqlCreateTable = function () {
                var tblStructure = {};
                tblStructure.FullName = DB_TEXT;
                tblStructure.Company = DB_TEXT;
                tblStructure.JobTitle = DB_TEXT;
                tblStructure.EmailAddress = DB_TEXT;
                tblStructure.BusinessPhone = DB_TEXT;
                tblStructure.BusinessFax = DB_TEXT;
                tblStructure.MobilePhone = DB_TEXT;
                tblStructure.HomePhone = DB_TEXT;
                tblStructure.StreetAddress1 = DB_TEXT;
                tblStructure.StreetAddress2 = DB_TEXT;
                tblStructure.City = DB_TEXT;
                tblStructure.State = DB_TEXT;
                tblStructure.Province = DB_TEXT;
                tblStructure.PostalCode = DB_TEXT;
                SqlCreateTable(dbAddressBook, "Contact", tblStructure, "FullName", "");
            };
            app.ContactSqlCreateTable();

关注点

找到 JQuery 的延迟函数来操作函数调用,这有助于我确保最小化数据库维护代码。websql.js 代码可用于任何 WebSQL 数据库,因为您只需将对象传递给函数即可。虽然此应用程序中添加了更多功能来显示与联系人相关的城市关系,但我们并未在此进行解释,因为这里的重点主要是 CRUD 功能。您还可以使用三个步骤的 JQM.Show 移动应用程序创建此类 CRUD 应用,这是一个 RAD 工具,可以为您编写所有代码来创建原型。

历史

这是我们关于使用不同框架创建 CRUD Web 应用的文章系列的第三部分。我们讨论了 LocalStorage,然后是 Web 相关的 JSON,现在是 WebSQL。接下来的步骤将是 MySQL,并根据兴趣和使用情况考虑其他数据库。您可以在此处留下评论,告诉我您还想在这些文章中看到什么,以便我探索和提供更多信息。如果您发现任何有用的内容,请投票并推荐我可能需要添加的任何改进。感谢您花时间阅读本文。#祝您愉快

© . All rights reserved.