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






4.33/5 (2投票s)
使用 IndexedDB 作为 JQuery Mobile 应用的后端
下载 MoviesDatabaseIndexedDB.zip
引言
今天,我们将使用 IndexedDB 作为后端来创建一个 CRUD JQuery Mobile 网页应用。我将演示以下功能:
1. 创建一个 IndexedDB 数据库和表
2. **创**建一条记录
3. **更**新现有记录
4. **删**除现有记录
5. **查**看列表中的记录
6. 从记录生成 HTML 表,并且
7. 从 HTML 表将记录导出到 Excel 文件。
IndexedDB 数据库驻留在浏览器存储中,其工作方式与 WebSQL 类似。我将在我的关于 JQuery Mobile CRUD WebSQL 的文章中讨论的相同 CRUD 功能将在此处应用。
但是,什么是 IndexedDB?
IndexedDB 是一个客户端存储大量结构化数据的 API,它还能够使用索引对这些数据进行高性能搜索。IndexedDB 是一个事务性数据库系统,类似于基于 SQL 的 RDBMS;但是,后者使用具有固定列的表,而 IndexedDB 是一个基于 JavaScript 的面向对象数据库。IndexedDB 允许您存储和检索用 键索引的对象。 structured clone algorithm支持的任何对象都可以存储。您需要指定数据库架构,打开到数据库的连接,然后在 事务中检索和更新数据。
使用 IndexedDB 执行的操作是异步的,以避免阻塞应用程序的其余部分运行。IndexedDB 最初包含异步 API 和同步 API;同步 API 仅用于 Web Workers。同步版本已从规范中删除,因为其必要性尚存疑问,但如果 Web 开发人员有足够的需求,它可能会在将来重新引入。
单个数据库项目的大小没有限制,但在某些情况下,对每个 IndexedDB 数据库的总大小有限制。要访问数据库,请在 open()
方法上调用 indexedDB
属性,该属性位于 window对象上。此方法返回一个 IDBRequest
对象;异步操作通过在 IDBRequest
对象上触发事件来与调用应用程序通信。 有关 IndexedDB 的更多信息,请参见此处。
一些简单的假设:您之前已经创建过 JQuery Mobile 应用,并且熟悉 JavaScript。
背景
本文是我关于 JQuery Mobile 和开发者在开发移动应用时可以使用到的各种数据库后端文章的延续。我讨论过的各种存储数据库信息的方法包括:
JQuery Mobile CRUD - LocalStorage
如果您阅读过这些文章,那么在开发应用程序时,我的命名方法一直很相似,只是处理数据库操作的代码有所不同。我在这里也遵循这个趋势。在本练习中,我开发了一个用于存储我电影收藏数据库的应用。对于每部电影,我们只保留简单的详细信息:电影名称、电影年份、电影类型。在电影列表中,我们将电影名称显示为标题,电影类型显示为副标题,并在计数气泡中显示电影年份。
使用代码
此应用程序的代码在主 index.html 文件中指示的 js/app.js 文件中引用。开发此应用程序时,您可以使用标准的模板来开发 HTML5 应用程序,并使用 JQuery 的 CSS 和 JS 文件进行自定义。
1. 创建一个 IndexedDB 数据库和表
为此,我们定义了将用于我们数据库的变量。
//variables to hold the indexedDB database.
var dbMoviesDatabase;
var dbName = "MoviesDatabase";
var dbVersion = 1;
在这里,我们定义了一个名为 dbMoviesDatabase 的数据库对象,我们将使用它来引用我们的数据库,数据库的名称将是“MoviesDatabase”,版本为 1。完成后,您可以通过浏览器(Chrome)的“资源”选项卡选择 MoviesDatabase,并访问数据库,如下图所示:
展开数据库树将显示您的表的详细信息。实现这一点的 Jscript 是以下代码,所有代码都在我们的 app.init 函数中执行:
// open the indexedDB database
var request = indexedDB.open(dbName, dbVersion);
//check if an upgrade is needed, this due to a version change
request.onupgradeneeded = function(e) {
var thisDB = e.target.result;
var store = null;
//create the necessary tables for the application
// create an indexedDB for IndexedDB-Movie
if (!thisDB.objectStoreNames.contains("Movie")) {
// create objectStore for PrimaryKey as keyPath="MovieName"
store = thisDB.createObjectStore("Movie", {keyPath: "MovieName" });
// thisDB.createObjectStore("Movie", { autoIncrement: true });
// create index to 'name' for conditional search
// store.createIndex('name', 'name', {unique: false });
}
};
//the database was opened successfully
request.onsuccess = function(e) {
dbMoviesDatabase = e.target.result;
}
从上面的代码可以看出,对象存储被检查是否存在一个名为“Movie”的表。如果不存在,则会创建一个带有 MovieName 主键的表。我们不使用自动增量键作为主键,因此只需一行代码即可创建带有主键的表。上图显示了在处理后信息如何在您的数据库后端中存储。
2. CR - 创建一条记录
我们创建了一个简单的用户界面,为最终用户提供捕获电影信息的功能。这由下面的屏幕显示。
用户输入电影信息并按“保存”以创建一条新记录到数据库存储。这里会发生三件事:2.1 从其 HTML 定义中读取屏幕并显示;2.2 当用户按“保存”时,从输入控件读取电影详细信息;2.3 生成一个事务以将记录对象存储到数据库。让我们详细看看这些子步骤。
2.1 创建电影屏幕 HTML 定义
<div id="pgAddMovie" data-role="page">
<div data-position="left" data-display="overlay" data-position-fixed="true" id="pgAddMovieLeftPnl" data-role="panel">
<ul data-role="listview" id="pgAddMovieLeftPnlLV">
<li><a data-transition="slide" href="#pgAddMovie" class="ui-btn ui-icon-plus ui-btn-icon-left">New</a></li>
<li><a data-transition="slide" href="#pgAddMultMovie" class="ui-btn ui-icon-plus ui-btn-icon-left">New Multiple</a></li>
<li><a data-transition="slide" href="#pgRptMovie" class="ui-btn ui-icon-eye ui-btn-icon-left">Report</a></li>
<li><a data-transition="slide" href="#pgMovie" class="ui-btn ui-icon-carat-l ui-btn-icon-left">Back</a></li>
</ul>
</div>
<div data-position="right" data-display="overlay" data-position-fixed="true" id="pgAddMovieRightPnl" data-role="panel">
<ul data-role="listview" id="pgAddMovieRightPnlLV">
</ul>
</div>
<header id="pgAddMovieHdr" data-role="header" data-position="fixed" data-tap-toggle="false">
<h1>Movies Database 1.00</h1>
<a data-role="button" id="pgAddMovieMenu" data-icon="bars" href="#pgAddMovieLeftPnl" class="ui-btn-left">Menu</a>
<a data-role="button" id="pgAddMovieRightMenu" data-icon="records" href="#pgAddMovieRightPnl" class="ui-btn-right">Records</a>
</header>
<div id="pgAddMovieCnt" data-role="content">
<h3>Add Movie</h3><div id="pgAddMovieForm">
<div data-role="fieldcontain">
<label for="pgAddMovieMovieName">Movie Name<span class='red'>*</span></label>
<input required name="pgAddMovieMovieName" title="Enter movie name here." type="text" id="pgAddMovieMovieName" placeholder="Enter movie name here." autocomplete="off" data-clear-btn="true"></input>
</div>
<div data-role="fieldcontain">
<label for="pgAddMovieMovieYear">Movie Year<span class='red'>*</span></label>
<input required name="pgAddMovieMovieYear" title="Enter movie year here." type="text" id="pgAddMovieMovieYear" placeholder="Enter movie year here." autocomplete="off" data-clear-btn="true"></input>
</div>
<div data-role="fieldcontain">
<label for="pgAddMovieMovieGenre">Movie Genre<span class='red'>*</span></label>
<input required name="pgAddMovieMovieGenre" title="Enter movie genre here." type="text" id="pgAddMovieMovieGenre" placeholder="Enter movie genre here." autocomplete="off" data-clear-btn="true"></input>
</div>
</div>
</div>
<footer id="pgAddMovieFtr" data-role="footer" data-position="fixed" data-tap-toggle="false">
<div id="pgAddMovieFtrNavBar" data-role="navbar">
<ul>
<li><a id="pgAddMovieBack" data-icon="carat-l">Cancel</a>
</li>
<li><a type="submit" id="pgAddMovieSave" data-icon="action">Save</a>
</li>
</ul>
</div>
</footer>
</div>
此屏幕上有一个左侧滑动菜单,其中包含一个列表视图,具有添加新电影、添加多部电影、查看电影报告和返回电影列表的功能。点击标题栏中的“菜单”按钮即可激活此功能。还有一个右侧滑动菜单,点击“记录”即可激活,以列出数据库中可用的电影。
电影详细信息的输入控件分别是 pgAddMovieMovieName、pgAddMovieMovieYear 和 pgAddMovieMovieGenre,它们对应于我们希望为电影模型存储的字段。“保存”按钮的名称是 pgAddMovieSave。
2.2 获取用户电影输入
// Save click event on Add page
$('#pgAddMovieSave').on('click', function (e) {
e.preventDefault();
e.stopImmediatePropagation();
//get form contents into an object
var MovieRec = pgAddMovieGetRec();
//save object to IndexedDB
app.addMovie(MovieRec);
});
一旦用户按下“保存”按钮,就会开始处理以获取用户输入并保存。数据获取是通过 pgAddMovieGetRec 完成的。
//read contents of each form input
function pgAddMovieGetRec() {
//define the new record
var MovieRec = {};
MovieRec.MovieName = $('#pgAddMovieMovieName').val().trim();
MovieRec.MovieYear = $('#pgAddMovieMovieYear').val().trim();
MovieRec.MovieGenre = $('#pgAddMovieMovieGenre').val().trim();
return MovieRec;
}
然后,使用 app.addMovie(MovieRec) 保存电影记录,该函数接收要创建的新电影的详细信息。
2.3 将电影存储到数据库
// add a new record to IndexedDB storage.
app.addMovie = function (MovieRec) {
$.mobile.loading("show", {
text: "Creating record...",
textVisible: true,
textonly: false,
html: ""
});
// clean the primary key to store
var MovieName = MovieRec.MovieName;
MovieName = MovieName.split(' ').join('-');
MovieRec.MovieName = MovieName;
// store the json object in the database
//define a transaction to execute
var tx = dbMoviesDatabase.transaction(["Movie"], "readwrite");
//get the record store to create a record on
var store = tx.objectStore("Movie");
// add to store
var request = store.add(MovieRec);
request.onsuccess = function(e) {
//show a toast message that the record has been added
toastr.success('Movie record successfully added.', 'Movies Database');
//find which page are we coming from, if from sign in go back to it
var pgFrom = $('#pgAddMovie').data('from');
switch (pgFrom) {
case "pgSignIn":
$.mobile.changePage('#pgSignIn', {transition: pgtransition});
break;
default:
// clear the edit page form fields
pgAddMovieClear();
//stay in the same page to add more records
}
}
request.onerror = function(e) {
//show a toast message that the record has not been added
toastr.error('Movie record NOT successfully added.', 'Movies Database');
}
$.mobile.loading("hide");
};
从获得的电影记录中,我们清理主键值,即电影名称,并用“-”替换所有空格。因为我们要将记录写入 IndexedDB 数据库,所以我们创建一个读写事务。然后,我们访问“movie”表并将记录按原样存储。正如您可能注意到的,记录是以 JSON 格式存储的。添加电影后,会显示一个通知告诉用户,否则会显示一个错误通知。
3. U - 更新现有记录
要更新现有记录,您需要先从现有记录中选择它,然后再更新它。与创建记录方法一样,我们基于要更新的电影定义一个记录对象。在此应用程序中,您从电影列表页面访问现有电影,这将显示电影更新页面,您也可以在此页面删除现有电影。让我们看看这个屏幕。
我们在这里选择了一部名为《速度与激情 7》的现有电影,其详细信息已弹出。由于电影名称是主键,因此无法更改,但电影年份和类型可以更改。更改电影详细信息并单击“更新”后,将读取电影详细信息并将其保存为对象,然后将其作为变量传递以将电影存储到数据库。正如您所注意到的,更新屏幕与添加屏幕类似。区别在于控件的名称。
在这种情况下,电影详细信息的输入控件分别是 pgEditMovieMovieName、pgEditMovieMovieYear 和 pgEditMovieMovieGenre,它们对应于我们希望为电影模型存储的字段。“保存”按钮的名称是 pgEditMovieUpdate。
3.1 获取更新后的电影输入
// Update click event on Edit Page
$('#pgEditMovieUpdate').on('click', function (e) {
e.preventDefault();
e.stopImmediatePropagation();
//get contents of Edit page controls
var MovieRec = pgEditMovieGetRec();
//save updated records to IndexedDB
app.updateMovie(MovieRec);
});
一旦用户按下“保存”按钮,就会开始处理以获取用户输入并保存。数据获取是通过 pgEditMovieGetRec 完成的。
//read contents of each form input
function pgEditMovieGetRec() {
//define the new record
var MovieRec = {};
MovieRec.MovieName = $('#pgEditMovieMovieName').val().trim();
MovieRec.MovieYear = $('#pgEditMovieMovieYear').val().trim();
MovieRec.MovieGenre = $('#pgEditMovieMovieGenre').val().trim();
return MovieRec;
}
然后,使用 app.updateMovie(MovieRec) 保存电影记录,该函数接收更新后的电影记录的详细信息。
3.2 将更新后的电影存储到数据库
//update an existing record and save to IndexedDB
app.updateMovie = function (MovieRec) {
$.mobile.loading("show", {
text: "Update record...",
textVisible: true,
textonly: false,
html: ""
});
// lookup specific Movie
var MovieName = MovieRec.MovieName;
//cleanse the key of spaces
MovieName = MovieName.split(' ').join('-');
MovieRec.MovieName = MovieName;
//define a transaction to execute
var tx = dbMoviesDatabase.transaction(["Movie"], "readwrite");
//get the record store to create a record on
var store = tx.objectStore("Movie");
//get the record from the store
store.get(MovieName).onsuccess = function(e) {
var request = store.put(MovieRec);
request.onsuccess = function(e) {
//record has been saved
toastr.success('Movie record updated.', 'Movies Database');
// clear the edit page form fields
pgEditMovieClear();
// show the records listing page.
$.mobile.changePage('#pgMovie', {transition: pgtransition});
}
request.onerror = function(e) {
toastr.error('Movie record not updated, please try again.', 'Movies Database');
return;
}
};
$.mobile.loading("hide");
};
从获得的电影记录中,我们清理主键值,即电影名称,并用“-”替换所有空格。因为我们要将记录写入 IndexedDB 数据库,所以我们创建一个读写事务。然后,我们访问“movie”表,使用主键查找现有记录,成功后,我们存储记录的详细信息。
4. D - 删除现有记录
要能够删除现有记录,首先需要选择它,然后删除它。要在此应用程序中删除电影,您需要从电影更新屏幕访问该功能。单击删除按钮的运行方式如下:
// delete button on Edit Page
$('#pgEditMovieDelete').on('click', function (e) {
e.preventDefault();
e.stopImmediatePropagation();
//read the record key from form control
var MovieName = $('#pgEditMovieMovieName').val().trim();
//show a confirm message box
$('#msgboxheader h1').text('Confirm Delete');
$('#msgboxtitle').text(MovieName.split('-').join(' '));
$('#msgboxprompt').text('Are you sure that you want to delete this movie? This action cannot be undone.');
$('#msgboxyes').data('method', 'deleteMovie');
$('#msgboxno').data('method', 'editMovie');
$('#msgboxyes').data('id', MovieName.split(' ').join('-'));
$('#msgboxno').data('id', MovieName.split(' ').join('-'));
$('#msgboxyes').data('topage', 'pgEditMovie');
$('#msgboxno').data('topage', 'pgEditMovie');
$.mobile.changePage('#msgbox', {transition: 'pop'});
});
电影名称被读取,并存储在我们的消息框屏幕定义中。系统会提示用户确认删除操作。
当用户选择删除电影,通过单击“是”,将调用 app.deleteMovie,传递存储在“是”按钮的 data-id 属性中的电影名称。
//delete a record from IndexedDB using record key
app.deleteMovie = function (MovieName) {
$.mobile.loading("show", {
text: "Deleting record...",
textVisible: true,
textonly: false,
html: ""
});
MovieName = MovieName.split(' ').join('-');
//define a transaction to execute
var tx = dbMoviesDatabase.transaction(["Movie"], "readwrite");
//get the record store to delete a record from
var store = tx.objectStore("Movie");
//delete record by primary key
var request = store.delete(MovieName);
request.onsuccess = function(e) {
//record has been deleted
toastr.success('Movie record deleted.', 'Movies Database');
// show the page to display after a record is deleted, this case listing page
$.mobile.changePage('#pgMovie', {transition: pgtransition});
}
request.onerror = function(e) {
toastr.error('Movie record not deleted, please try again.', 'Movies Database');
return;
}
$.mobile.loading("hide");
};
此过程成功后,用户会收到记录已删除的通知,否则会收到相应的通知。再次,在 movie 表上定义了一个读写事务。然后打开 store,然后使用 moviename 执行 store.delete 方法来删除数据库中的记录。
5. V - 查看列表中的记录
从存储中查看记录涉及读取对象存储中的所有现有记录,在本例中将它们显示在列表视图中。这发生在电影列表页面。让我们看看电影列表页面的定义。这只是一个简单的页面,带有一个空白的列表视图,该列表视图在页面显示时在运行时更新。
<div id="pgMovie" data-role="page">
<div data-position="left" data-display="overlay" data-position-fixed="true" id="pgMovieLeftPnl" data-role="panel">
<ul data-role="listview" id="pgMovieLeftPnlLV">
<li><a data-transition="slide" href="#pgAddMultMovie" class="ui-btn ui-icon-plus ui-btn-icon-left">New Multiple</a></li>
<li><a data-transition="slide" href="#pgRptMovie" class="ui-btn ui-icon-eye ui-btn-icon-left">Report</a></li>
<li><a data-transition="slide" href="#pgMenu" class="ui-btn ui-icon-carat-l ui-btn-icon-left">Back</a></li>
</ul>
</div>
<header id="pgMovieHdr" data-role="header" data-position="fixed" data-tap-toggle="false">
<h1>Movies Database 1.00</h1>
<a data-role="button" id="pgMovieMenu" data-icon="bars" data-transition="slide" href="#pgMovieLeftPnl" class="ui-btn-left">Menu</a>
<a data-role="button" id="pgMovieNew" data-icon="plus" data-theme="b" class="ui-btn-right">New</a>
</header>
<div id="pgMovieCnt" data-role="content">
<h3>Movies</h3><ul data-role="listview" data-inset="true" id="pgMovieList" data-autodividers="true" data-filter="true" data-filter-placeholder="Search Movies">
<li data-role="list-divider">Your Movies</li>
<li id="noMovie">You have no movies</li>
</ul>
</div>
</div>
为了使此工作正常,在我们的 js/app.js 中,我们定义了一些变量来管理列表视图。
// variable definitions go here
var MovieLi = '<li><a data-id="Z2"><h2>Z1</h2><p>DESCRIPTION</p><p><span class="ui-li-count">COUNTBUBBLE</span></p></a></li>';
var MovieHdr = '<li data-role="list-divider">Your Movies</li>';
var noMovie = '<li id="noMovie">You have no movies</li>';
对于每个列表视图项,我们希望显示标题、描述和计数气泡,如下所示:
此电影列表在页面被访问时发生,通过调用 app.checkForMovieStorage,该调用由 pagebeforechange 方法触发。让我们看看。
//display records if they exist or tell user no records exist.
app.checkForMovieStorage = function () {
$.mobile.loading("show", {
text: "Checking storage...",
textVisible: true,
textonly: false,
html: ""
});
//get records from IndexedDB.
//when returned, parse then as json object
var MovieObj = {};
//define a transaction to read the records from the table
var tx = dbMoviesDatabase.transaction(["Movie"], "readonly");
//get the object store for the table
var store = tx.objectStore("Movie");
//open a cursor to read all the records
var request = store.openCursor();
request.onsuccess = function(e) {
//return the resultset
var cursor = e.target.result;
if (cursor) {
MovieObj[cursor.key] = cursor.value;
// process another record
cursor.continue();
}
// are there existing Movie records?
if (!$.isEmptyObject(MovieObj)) {
// yes there are. pass them off to be displayed
app.displayMovie(MovieObj);
} else {
// nope, just show the placeholder
$('#pgMovieList').html(MovieHdr + noMovie).listview('refresh');
}
}
$.mobile.loading("hide");
// an error was encountered
request.onerror = function(e) {
$.mobile.loading("hide");
// just show the placeholder
$('#pgMovieList').html(MovieHdr + noMovie).listview('refresh');
}
};
这会在 Movie 表中创建一个只读事务,因为我们只想获取记录。我们还定义了一个对象来存储表中的所有电影信息,我们称之为 MovieObj,并使用 cursor.continue 遍历每部电影,将每部电影的详细信息分配给 MovieObj 对象内的键对象。之后,我们将此传递给 app.displayMovie 以在列表视图中显示记录。
//display records in listview during runtime.
app.displayMovie = function (MovieObj) {
$.mobile.loading("show", {
text: "Displaying records...",
textVisible: true,
textonly: false,
html: ""
});
// create an empty string to contain html
var html = '';
// make sure your iterators are properly scoped
var n;
// loop over records and create a new list item for each
//append the html to store the listitems.
for (n in MovieObj) {
//get the record details
var MovieRec = MovieObj[n];
// clean the primary key
var pkey = MovieRec.MovieName;
pkey = pkey.split('-').join(' ');
MovieRec.MovieName = pkey;
//define a new line from what we have defined
var nItem = MovieLi;
nItem = nItem.replace(/Z2/g,n);
//update the title to display, this might be multi fields
var nTitle = '';
//assign cleaned title
nTitle = n.split('-').join(' ');
//replace the title;
nItem = nItem.replace(/Z1/g,nTitle);
//there is a count bubble, update list item
var nCountBubble = '';
nCountBubble += MovieRec.MovieYear;
//replace the countbubble
nItem = nItem.replace(/COUNTBUBBLE/g,nCountBubble);
//there is a description, update the list item
var nDescription = '';
nDescription += MovieRec.MovieGenre;
//replace the description;
nItem = nItem.replace(/DESCRIPTION/g,nDescription);
html += nItem;
}
//update the listview with the newly defined html structure.
$('#pgMovieList').html(MovieHdr + html).listview('refresh');
$.mobile.loading("hide");
};
在这里,我们遍历从 checkStorage 获取的每部电影记录,用空格替换我们的“-”。然后,我们获取电影属性,并根据变量构建列表视图项。在构建完所有项后,我们使用我们的内容刷新列表视图。
6. 从记录生成 HTML 表,并且
从任何屏幕点击“报告”都会生成一个存储电影的 HTML 表。这是通过运行 app.MoviRpt 完成的。
//display records in table during runtime.
app.MovieRpt = function () {
$.mobile.loading("show", {
text: "Loading report...",
textVisible: true,
textonly: false,
html: ""
});
//clear the table and leave the header
$('#RptMovie tbody tr').remove();
// create an empty string to contain all rows of the table
var n, MovieRec;
//get records from IndexedDB.
//define a transaction to read the records from the table
var tx = dbMoviesDatabase.transaction(["Movie"], "readonly");
//get the object store for the table
var store = tx.objectStore("Movie");
//open a cursor to read all the records
var request = store.openCursor();
request.onsuccess = function(e) {
//return the resultset
var cursor = e.target.result;
if (cursor) {
n = cursor.key;
//clean primary keys
n = n.split('-').join(' ');
//get each record
MovieRec = cursor.value;
//create each row
var eachrow = '<tr>';
eachrow += '<td class="ui-body-c">' + n + '</td>';
eachrow += '<td class="ui-body-c">' + MovieRec.MovieYear + '</td>';
eachrow += '<td class="ui-body-c">' + MovieRec.MovieGenre + '</td>';
eachrow += '</tr>';
//append each row to the table;
$('#RptMovie').append(eachrow);
// process another record
cursor.continue();
}
// update the table
//$('#RptMovie').append(newrows);
// refresh the table with new details
$('#RptMovie').table('refresh');
}
$.mobile.loading("hide");
};
与 checkStorage 类似,这以只读模式从表中读取电影。对于读取的每个电影值,我们将其分配给 MovieRec 变量,然后从中生成每个表行。这会逐条记录追加到表中,生成如下所示的表:
可以通过单击标题栏中的“导出”按钮将此 HTML 表导出到 Excel。
7. 从 HTML 表将记录导出到 Excel 文件。
这会执行 JavaScript 将 HTML 表导出到 Excel。它使用了 excellentexport.min.js 文件。让我们看看报告的定义。
<div id="pgRptMovie" data-role="page">
<header id="pgRptMovieHdr" data-role="header" data-position="fixed" data-tap-toggle="false">
<h1>Movies Database 1.00</h1>
<a data-role="button" id="pgRptMovieBack" data-icon="carat-l" class="ui-btn-left">Back</a>
<div data-role="controlgroup" data-type="horizontal" class="ui-btn-right" style="margin-top:0;border-top:0;">
<a data-role="button" download="Movie.xls" onclick="return ExcellentExport.excel(this, 'RptMovie', 'Movie');" id="pgRptMovieExport" data-icon="exportexcel" href="#">Export</a>
<a data-role="button" id="pgRptMovieNew" data-icon="plus" data-theme="b" data-transition="slide" href="#pgAddMovie">New</a>
</div>
</header>
<div id="pgRptMovieCnt" data-role="content">
<table id="RptMovie" data-column-btn-text="Columns To Display" data-column-btn-theme="b" data-role="table" data-mode="columntoggle" data-column-popup-theme="a" class="ui-responsive table-stroke table-stripe ui-shadow">
<caption>Movies Report</caption>
<thead>
<tr class="ui-bar-a">
<th class="ui-bar-a ui-body-c">Movie Name</th>
<th data-priority="2" class="ui-bar-a ui-body-c">Movie Year</th>
<th data-priority="3" class="ui-bar-a ui-body-c">Movie Genre</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr><td></td></tr>
<tr><td class='ui-body-c' colspan="3">Powered by JQM.Show - https://play.google.com/store/apps/details?id=com.b4a.JQMShow</td></tr></tfoot>
</table>
</div>
</div>
标题栏中的两个按钮在 onclick 方法触发点击“导出”按钮创建 Excel 工作簿时,通过 control-group 创建。
<div data-role="controlgroup" data-type="horizontal" class="ui-btn-right" style="margin-top:0;border-top:0;">
<a data-role="button" download="Movie.xls" onclick="return ExcellentExport.excel(this, 'RptMovie', 'Movie');" id="pgRptMovieExport" data-icon="exportexcel" href="#">Export</a>
<a data-role="button" id="pgRptMovieNew" data-icon="plus" data-theme="b" data-transition="slide" href="#pgAddMovie">New</a>
</div>
关注点
在这里 CodeProject 中查看 IndexedDB 入门:此处。