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

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (21投票s)

2014年4月2日

CPOL

9分钟阅读

viewsIcon

34844

downloadIcon

1320

使用 Backbone 进行单页应用程序开发,并使用 Web API 服务执行数据库操作。

引言

本教程是关于使用 Backbone 创建单页应用程序,并使用 ASP.NET Web API 作为 RESTful 服务执行数据库 CRUD(创建、读取、更新和删除)操作。

本文内容列表

  1. 单页应用程序简述。
  2. Backbone 简述。
  3. ASP.NET Web API 简述。
  4. 使用 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 中运行。

我发布的文章

  1. https://codeproject.org.cn/Articles/661878/Implementation-of-MVC-Patterns-in-ASP-NET-Web-form
  2. https://codeproject.org.cn/Articles/674959/MVC-Patterns-Active-and-Passive-Model-and-its
  3. https://codeproject.org.cn/Articles/691691/Apply-Here-Map-in-Windows-Phone-HTML-Apps
  4. https://codeproject.org.cn/Articles/740703/Easy-way-to-learn-the-Asp-Net-Built-in-Membership

© . All rights reserved.