使用 Backbone 和 ASP.NET Web API 进行数据库 CRUD 操作的单页应用程序






4.78/5 (21投票s)
使用 Backbone 进行单页应用程序开发,并使用 Web API 服务执行数据库操作。
引言
本教程是关于使用 Backbone 创建单页应用程序,并使用 ASP.NET Web API 作为 RESTful 服务执行数据库 CRUD(创建、读取、更新和删除)操作。
本文内容列表
- 单页应用程序简述。
- Backbone 简述。
- ASP.NET Web API 简述。
- 使用 Backbone 和 Web API 的示例单页应用程序。
第一部分:单页应用程序简述。
如今,单页应用程序越来越受欢迎。它给人一种桌面应用程序的印象。基本上,单页应用程序使用 AJAX 与服务器通信,因此无需刷新整个页面即可花费更少的时间更新网页的一部分,给人一种桌面应用程序的印象。
第二部分:Backbone 简述。
Backbone 是一个 JavaScript 库,提供用于构建单页应用程序的 API。它通过内置的 JavaScript 类 Model、View、Collection、Router 和 Event 为应用程序提供结构。
第三部分:ASP.NET Web API 简述。
简而言之,ASP.NET Web API 是 Microsoft 开发的一个框架,随 Visual Studio 2012 一起提供。它提供了 RESTful Web 服务的所有功能,特别是响应 HTTP 请求调用(GET、POST、PUT 和 DELETE),并返回 JSON 序列化数据。
第四部分:使用 Backbone 和 Web API 的示例单页应用程序。
几乎所有应用程序都执行一些数据库操作来显示或创建、更新或删除数据库中的数据。此示例应用程序展示了如何使用 Backbone 和 ASP.NET Web API 从单页应用程序执行 CRUD(创建、读取、更新和删除)操作。此示例应用程序显示一个锦标赛列表,可以添加、编辑或删除锦标赛。
步骤 1:首先使用 Web API 模板创建 Web 应用程序项目,如下图所示的解决方案资源管理器。
描述:此应用程序仅包含一个 HTML 页面 tournaments.html,所有操作均在此页面上执行。Controllers 文件夹包含 Controller 类。这里我只使用一个控制器 TournamentController 来提取、添加、编辑和删除锦标赛。其他文件夹包含 Backbone、Underscore 和 jQuery 所需的图像和 JavaScript 文件。这里的 BackboneDB 是用作数据访问层的 ADO.NET Entity Framework。
步骤 2:接下来创建一个包含 Tournament 表的数据库。表结构如下图所示。
步骤 3:接下来创建一个 HTML 页面 tournaments.html。
此页面是唯一的单页,其中包含所有 HTML 和 JavaScript。使用 Backbone 的单页应用程序,所有 HTML 均由 Backbone View 类生成。页面正文的初始内容如下所示。
<body>
<div id="page">
<div id="tournamentAddContainer"></div>
<div id="tournamentlistContainer"></div>
</div>
<div class="loading"><img class="loadingImage" src="/Images/loading.gif" alt="Loading" /></div>
</body>
众所周知,View 对象生成页面的 HTML。Model 包含应用程序数据,并与 View 绑定以显示信息。Collection 对象包含同类型 Model 对象的列表,Router 对象更改浏览器历史记录。
下面显示了为此应用程序创建的自定义 View。
var tournamentAddView = Backbone.View.extend({
events: {
'click #btnAddTournament': "addtournament"
},
addtournament: function () {
var tournamentName = this.$el.find("#txtName").val();
var tournament= new tournamentModel({ Name: tournamentName });
tournament.save({}, {
success: function () {
tableVeiw.collection.add(tournament);
},
error: function () { alert('add error'); }
});
},
render: function () {
var templateTemp = $("#tempTournamentAdd").html();
this.$el.html(_.template(templateTemp, { Name: this.model.get('Name') }));
return this;
}
});
这里的 tournamentAddView是通过扩展 Backbone 内置的 View 类创建的。此 View 用于渲染一个 Underscore 模板,其中包含添加锦标赛所需的 HTML。Underscore 模板如下所示。
<script type="text/html" id="tempTournamentAdd">
<table>
<tr>
<td> <input type="text" id="txtName" value="<%= Name %>"/> </td>
<td> <input type="submit" id="btnAddTournament" value="Add"/> </td>
</tr>
</table>
</script>
这里的 "<%= Name %>" 语法显示了 Underscore 代码块,此语句渲染了绑定数据 Model 的 Name 属性的值。
下面显示了用于将表渲染到浏览器的自定义 View
var CustomTableView = Backbone.View.extend({
tagName: 'table',
attributes: { class: "tables" },
initialize: function () {
this.collection.on('add', this.addCollection);
tableviewref = this;
this.$el.html('<tr><th>Tournament Name</th><th></th><th></th></tr>');
},
render: function () {
_(this.collection.models).each(function (tour) {
this.$el.append(new CustomRowView({ model: tour }).render().el);
}, this);
return this;
},
addCollection: function (model, collection) {
tableviewref.$el.append(new CustomRowView({ model: model }).render().el);
}
});
这里的 tagName 属性指示了此 View 正在渲染的 HTML 元素类型。此 View 返回一个表。以下列表描述了其部分属性。
属性 |
描述 |
tagName |
View 将渲染的 HTML 元素的类型。 |
attributes |
接受一个 JavaScript 对象来设置 HTML 元素的属性。 |
initialize |
此属性包含一个函数引用,该函数在 View 实例化时执行。 |
render |
此函数渲染此 View 的内容。 |
addCollection |
这是一个回调函数,当 Model 被添加到 View 的 Collection 中时调用,这意味着一行被添加到表中。 |
下面显示了将一行渲染到表中的自定义 View
var CustomRowView = Backbone.View.extend({
tagName: 'tr',
attributes: { class: "rows" },
initialize: function () {
this.model.on('change', this.render, this);
},
events: {
'click #btnDeleteBook': "deleteTournament",
'click #btnEditBook': "editTournament",
'click #btnSave': "updateTournament",
'click #btnCancel': "cancelTournament"
},
deleteTournament: function () {
var self = this;
this.model.destroy({
success: function () {
tableVeiw.collection.remove(self.model);
self.remove();
self.render();
},
error: function () { alert('delete error'); }
});
},
editTournament: function () {
var templateTemp = $("#tempTournamentEdit").html();
this.$el.html(_.template(templateTemp, { Name: this.model.get('Name') }));
},
updateTournament: function () {
var tournamentNameEdit = this.$el.find("#txtNameEdit").val();
if (tournamentNameEdit != "") {
this.model.save({ Name: tournamentNameEdit }, {
success: function () {
var templateTemp = $("#tempTournamentView").html();
this.$el.html(_.template(templateTemp, { Name: this.model.get('Name') }));
},
error: function () { alert('update error'); }
});
}
},
cancelTournament: function () {
var templateTemp = $("#tempTournamentView").html();
this.$el.html(_.template(templateTemp, { Name: this.model.get('Name') }));
},
render: function () {
var templateTemp = $("#tempTournamentView").html();
this.$el.html(_.template(templateTemp, { Name: this.model.get('Name') }));
return this;
}
});
下面显示了一些属性的描述
属性 |
描述 |
tagName |
View 将渲染的 HTML 元素的类型。 |
attributes |
接受一个 JavaScript 对象来设置 HTML 元素的属性。 |
events |
定义事件并将其与事件处理程序绑定。 |
deleteTournament |
删除事件触发时执行此函数。 |
editTournament |
编辑事件触发时执行此函数。 |
saveTournament |
保存事件触发时执行此函数。 |
cancelTournament |
取消更新时执行此函数。 |
渲染行的 Underscore 模板如下所示
<script type="text/html" id="tempTournamentView">
<td><%=Name></td>
<td class="tdInput"> <input type="submit" id="btnEditBook" value=""/> </td>
<td class="tdInput"> <input type="submit" id="btnDeleteBook" value=""/> </td>
</script>
点击编辑按钮时用于渲染 HTML 的 Underscore 模板如下所示
<script type="text/html" id="tempTournamentEdit">
<td> <input type="text" id="txtNameEdit" value="<%= Name %>"/> </td>
<td class="tdInput"> <input type="submit" id="btnSave" value=""/> </td>
<td class="tdInput"> <input type="submit" id="btnCancel" value=""/> </td>
</script>
用于显示列表中每一行的自定义 Model 如下所示
var tournamentModel = Backbone.Model.extend({
idAttribute: 'Id',
urlRoot: '/api/Tournament',
defaults: {
Name: 'default',
Address: 'default'
}
});
下面显示了一些属性的描述
属性 |
描述 |
idAttribute |
此属性用于为 Model id 指定自定义名称。 |
urlRoot |
这是用于从数据库添加、编辑和删除锦标赛的服务器 URL。 |
defaults |
分配给新 Model 对象 的默认值。 |
下面显示了自定义 Collection 类型
var CustomCollection = Backbone.Collection.extend({
model: tournamentModel,
url: '/api/Tournament'
});
下面显示了一些属性的描述
属性 |
描述 |
model |
此 Collection 包含的 Model 的类型。 |
url |
这是用于获取锦标赛列表的服务器 URL。 |
下面显示了自定义 Backbone Router 类型
var CustomRouter = Backbone.Router.extend({
routes: {
'tournaments': 'tournaments'
},
tournaments: function () {
$('#tournamentlistContainer').html(tableVeiw.render().el);
}
});
下面显示了一些属性的描述
属性 |
描述 |
routes |
它包含导航 URL 列表及其在导航发生时触发的函数。 |
tournaments |
这是一个回调函数,当导航匹配路由时调用。 |
步骤 4:当浏览器首次请求页面时,由于需要渲染整个页面,加载可能需要几秒钟。这里我显示了一个加载动画,以指示页面在首次加载时。初始请求的 URL 是 https://:5334/tournaments.html 加载页面后,由于 Backbone 的路由,Backbone 会将其转换为 https://:5334/tournaments。页面加载事件如下所示。
显示和隐藏加载效果的 JavaScript 代码如下所示
$(".loading").css({ display: 'inline'});
tournamentCollection.fetch({
success: function (bookResponse) {
$(".loading").css({ display: 'none' });
}
});
在开始获取数据之前显示动画,在成功获取数据后隐藏。
步骤 5:下面显示锦标赛列表页面。
从服务器提取锦标赛列表的 JavaScript 代码如下所示
var tournamentCollection = new CustomCollection();
tournamentCollection.fetch({
success: function (bookResponse) {
Backbone.history.start({ pushState: true });
initialrouter.navigate('tournaments', { trigger: true });
},
silent: true
});
此 JavaScript 代码首先创建一个 Collection 对象,然后调用 Collection 的 fetch 函数来获取锦标赛列表。fetch 函数接受一个 JavaScript 对象作为参数,该对象包含一个 success 回调函数和一个 silent 属性(设置为 true),表示不会触发事件。
从服务器获取列表时,Backbone 发送一个 HTTP GET 请求到 URL 'http://servername/api/Tournament’,该 URL 在 Collection 对象的 url 属性中设置。
响应 Backbone GET 请求的服务器端代码如下所示。
// GET api/tournaments
public List<Tournament> Get()
{
List<Tournament> tourList;
using (BackboneDBEntities entity = new BackboneDBEntities())
{
tourList = entity.Tournaments.ToList();
entity.SaveChanges();
}
return tourList;
}
当服务器收到来自客户端的 HTTP GET 请求时,它会执行 Controller 的 Get 函数。此函数使用 ADO.NET Entity Framework 从数据库提取锦标赛列表,并将列表作为 JSON 序列化数据发送到客户端。
步骤 6:下面显示添加锦标赛页面。
当用户点击“添加”按钮时,Backbone 使用 AJAX 调用请求在服务器数据库中保存一个新的锦标赛,并将新锦标赛添加到视图中,而无需刷新整个页面。
添加锦标赛的 JavaScript 代码如下所示。
addtournament: function () {
var tournamentName = this.$el.find("#txtName").val();
var tournament= new tournamentModel({ Name: tournamentName });
tournament.save({}, {
success: function () {
tableVeiw.collection.add(tournament);
},
error: function () { alert('add error'); }
});
}
此函数首先从文本框读取锦标赛名称,然后创建一个新的 Model 对象,然后调用 Model 的 save 函数将 Model 保存到数据库。Backbone 用于保存 Model 的 URL 在 Model 的 urlRoot 属性中提供,URL 是 'http://servername /api/Tournament'。
将锦标赛添加到数据库的服务器端代码如下所示。
// POST api/tournaments
public Tournament Post(Tournament tour)
{
using (BackboneDBEntities entity = new BackboneDBEntities())
{
entity.Tournaments.Add(tour);
entity.SaveChanges();
}
return tour;
}
当服务器收到来自客户端的 HTTP POST 请求时,它会执行 Controller 的 Post 函数。此函数接收一个 Tournament Entity 作为参数,使用 ADO.NET Entity Framework 将 Tournament Entity 添加到数据库,并将新创建的锦标赛作为 JSON 对象返回给客户端。
步骤 7:下面显示编辑锦标赛页面。
在列表中的锦标赛上点击编辑按钮后,View 会被一个文本框以及保存和取消按钮替换。点击保存按钮会更新锦标赛,点击取消按钮会取消更新。
点击编辑按钮时执行的 JavaScript 代码如下所示。
editTournament: function () {
var templateTemp = $("#tempTournamentEdit").html();
this.$el.html(_.template(templateTemp, { Name: this.model.get('Name') }));
}
此函数使用新的 Underscore 模板‘ tempTournamentEdit’替换 View。
点击保存按钮时执行的 JavaScript 代码如下所示。
updateTournament: function () {
var tournamentNameEdit = this.$el.find("#txtNameEdit").val();
if (tournamentNameEdit != "") {
this.model.save({ Name: tournamentNameEdit }, {
success: function () {
var templateTemp = $("#tempTournamentView").html();
this.$el.html(_.template(templateTemp, { Name: this.model.get('Name') }));
},
error: function () { alert('update error'); }
});
}
}
此函数读取更新值,并调用 Model 的 save 函数,将更新后的值作为参数。Backbone 发送一个 HTTP PUT 请求到 URL 'http://servername /api/Tournament/5'。其中 5 是数据库中锦标赛的 Id。
更新锦标赛的服务器端代码如下所示。
// PUT api/tournaments/5
public void Put(int Id, Tournament tourpara)
{
using (BackboneDBEntities entity = new BackboneDBEntities())
{
Tournament tour= entity.Tournaments.First(t => t.Id == Id);
tour.Name = tourpara.Name;
entity.SaveChanges();
}
}
当服务器收到来自客户端的 HTTP PUT 请求时,它会执行 Controller 的 Put 函数。此函数接收一个 Id 号和一个 Tournament Entity 作为参数,并使用 ADO.NET Entity Framework 更新数据库记录。
取消更新的 JavaScript 代码如下所示。
cancelTournament: function () {
var templateTemp = $("#tempTournamentView").html();
this.$el.html(_.template(templateTemp, { Name: this.model.get('Name') }));
}
当点击取消按钮时执行此函数。点击取消按钮后,Backbone 会取消更新并返回到列表 View。
步骤 8:下面显示删除锦标赛页面。
点击删除按钮后,Backbone 向服务器发送一个 HTTP DELETE 请求以从数据库删除锦标赛记录,并在成功删除后,Backbone 从列表视图中删除该锦标赛。
删除锦标赛的 JavaScript 代码如下所示。
deleteTournament: function () {
var self = this;
this.model.destroy({
success: function () {
tableVeiw.collection.remove(self.model);
self.remove();
self.render();
},
error: function () { alert('delete error'); }
});
}
此函数调用 Model 的 destroy 函数从数据库删除锦标赛。Backbone 向 URL 发送一个 HTTP DELETE 请求 'http://servername /api/Tournament/93'. 其中 93 是需要从数据库中删除的锦标赛的 Id。
删除锦标赛的服务器端代码如下所示。
// DELETE api/tournaments/93
public void Delete(int Id)
{
using (BackboneDBEntities entity = new BackboneDBEntities())
{
Tournament tour = entity.Tournaments.First(t => t.Id == Id);
entity.Tournaments.Remove(tour);
entity.SaveChanges();
}
}
当服务器收到来自客户端的 HTTP DELETE 请求时,它会执行 Controller 的 Delete 函数。此函数接收锦标赛的 Id 号作为参数,并使用 ADO.NET Entity Framework 从数据库中删除该锦标赛。
结论
以上就是关于如何使用 Backbone 开发单页应用程序并使用 ASP.NET Web API 执行数据库 CRUD 操作的所有内容。完整的代码请下载 zip 文件并在 Visual Studio 2012 中运行。
我发布的文章
- https://codeproject.org.cn/Articles/661878/Implementation-of-MVC-Patterns-in-ASP-NET-Web-form
- https://codeproject.org.cn/Articles/674959/MVC-Patterns-Active-and-Passive-Model-and-its
- https://codeproject.org.cn/Articles/691691/Apply-Here-Map-in-Windows-Phone-HTML-Apps
- https://codeproject.org.cn/Articles/740703/Easy-way-to-learn-the-Asp-Net-Built-in-Membership