使用 Backbone.js 进行 CRUD 操作






4.80/5 (4投票s)
通过基本 CRUD 操作学习 backbone.js
引言
在本帖中,我们将学习如何使用 backbone.js 进行基本的用户创建、读取、更新和删除操作。我们将创建一个基本应用程序,以帮助我们理解如何使用 backbone.js 进行编码。
什么是 Backbone.js
当我们使用 JavaScript 应用程序时,我们往往会投入巨大的精力来组织代码并把正确的部分放在正确的位置。但这可能并不总是易于维护或可扩展。来自 backbone 官方网站的介绍:
创建 JavaScript 应用程序很容易变得混乱,充斥着 jQuery 选择器和回调函数,它们都在拼命地尝试在 HTML UI、JavaScript 逻辑以及服务器上的数据库之间同步数据。对于丰富的客户端应用程序,通常需要更结构化的方法。
这时就出现了 Backbone.js。它是一个非常轻量级的框架,允许你以 MV* 的方式组织你的应用程序。MV 代表我们通常意义上的基本模型(Model)和视图(Views)。但重要的是 *。坦白说,我不是 Backbone 的开发者,所以很难说它是否严格符合 MVC 框架。我认为它可能是 Controller,在这种情况下,它允许你通过哈希标签 URL 来保存 JavaScript 应用程序的状态。更多详情请参见 Stackoverflow。
示例
让我印象最深刻的第一个例子是 Gmail,当你登录时,它会下载大量数据。然后一切都变得顺畅简单,后台进程会接管。页面从未刷新过。太棒了!
注意
目前我将忽略后端代码。假设我们已经有了现成的 Create/Read/Update/Delete API。
在本教程中,我使用了 ASP.NET MVC API 进行 CRUD 操作,并使用了 toastr 进行通知。我非常喜欢这个库,它太棒了。
我们将做什么
我们将尝试创建一个应用程序,允许创建用户、显示用户列表、编辑特定用户和删除特定用户。
环境设置
开始吧
- 创建一个新的 ASP.NET MVC 项目(例如,
UserManagement
)。 - 选择“Internet”作为模板。
- Backbone.js 默认不存在。因此,我们需要右键单击“References”,选择“Manage Nuget Packages”,然后搜索 Backbone。
- 从包列表中安装 Backbone.js。
- 转到 BundleConfig.cs 并添加一个新包,然后在 _Layout.cshtml 中引用它,或者你可以将其放在一边,并将你的 backbone.js 添加到 _layout.cshtml 中已引用的现有包中。
当你添加一个新包时
public static void RegisterBundles(BundleCollection bundles)
{
// existing code
bundles.Add(new ScriptBundle("~/bundles/backbone").Include(
"~/Scripts/underscore.js",
"~/Scripts/backbone.js"));
// existing code
}
运行你的应用程序,检查控制台是否抛出任何错误。
设置用户模型(类)
完成上述步骤后,让我们开始为我们的用户管理应用程序创建一个新的模型类。
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
}
假设有上述类,我们将继续客户端脚本。你可以创建你的 CRUD API。
显示用户列表
创建路由
var Router = Backbone.Router.extend({
routes: {
'': 'home',//Default route, when no hash tag is available
'create': 'create',// Create User, when user clicks on Create button
'edit/:id': 'edit'
}
});
现在,我们为用户的列表/编辑/创建创建路由。当我们的 URL 是
https:///#
我们将显示用户列表。
https:///#create
我们将显示用于创建用户的用户详情屏幕,即,我们需要显示用户创建页面。
https:///#edit
我们将显示带有填充数据的用户详情屏幕,用于编辑/更新用户。
定义路由
var route = new Router();
// When hash tag has localhost# register the below route
route.on('route:home', function () {
// Todo: code to render create page
console.log("Display Create Page");
});
// When hash tag has localhost#create register the below route
route.on('route:create', function () {
// Todo: code to render create page
console.log("Display Create Page");
});
//When hash tag has localhost#edit/1 register the below route
route.on('route:edit', function () {
// Todo: code to render edit page and render user details
console.log("Display Edit Page");
});
现在,我们按照上面的方式定义路由,它的作用是,当 URL 中的哈希标签发生变化时,相应的路由定义将被触发。我们很快就会回到这一部分。
运行你的应用程序,检查控制台是否抛出任何错误。将 URL 改为 /#
或 /#create
或 /#edit/1
,你应该会看到相应的控制台语句被打印出来。
创建模型
官方的说法是:
Models are the heart of any JavaScript application, containing the interactive data as well as a large part of the logic surrounding it: conversions, validations, computed properties, and access control. You extend
Backbone.Model
with your domain-specific methods, and Model provides a basic set of functionality for managing changes.
所以,废话不多说,让我们为我们的 Backbone
创建一个 User Model。
var userModel = Backbone.Model.extend({
defaults: {
Id: null,
FirstName: null,
LastName: null,
Age: null,
Email: null,
Phone: null
},
initialize: function () {
// Do stuff's which you want at the time of model creation
}
});
创建集合
官方的说法是:
Collections are ordered sets of models. You can bind "change" events to be notified when any model in the collection has been modified, listen for "add" and "remove" events, fetch the collection from the server, and use a full suite of Underscore.js methods.
集合中的任何模型触发的事件也会直接在集合上触发,以便于使用。例如,这允许你监听集合中任何模型特定属性的变化:
documents.on("change:selected", ...)
。
Backbone
集合是模型的集合,毫无疑问,它还有更多功能。它拥有或多或少 28 个 Underscore 方法 支持它,并允许你监视它们并在某些内容被修改或更改时执行必要的操作。此外,它还支持 RESTful API。太棒了!!
var listOfUsers = Backbone.Collection.extend({
model: userModel,
url: '/api/User'
});
代码简洁明了。我们提供了之前创建的模型和默认 API,它可以返回 List<User>
。如果你懒得创建后端,甚至可以尝试硬编码你的数据。一切都没问题。
创建视图
官方的说法是:
Backbone views are almost more convention than they are code — they don't determine anything about your HTML or CSS for you, and can be used with any JavaScript templating library. The general idea is to organize your interface into logical views, backed by models, each of which can be updated independently when the model changes, without having to redraw the page. Instead of digging into a JSON object, looking up an element in the DOM, and updating the HTML by hand, you can bind your view's render function to the model's "change" event — and now everywhere that model data is displayed in the UI, it is always immediately up to date.
听起来不错且信息丰富。 更多详情。
在我们开始创建视图之前,我们先决定我们的 el
元素。“el
”属性引用浏览器中创建的 DOM 对象。每个 Backbone.js 视图都有一个“el
”属性,如果未定义,Backbone.js 将构建自己的,即一个空的 div
元素。
转到你想要渲染用户列表页面的页面,并添加一个指定类名或 ID 的 div
标签。在我的例子中,我删除了 Home/Index.cshtml 的代码并添加了下面这行:
@{
ViewBag.Title = "Home Page";
}
<div class="user-management"></div>
我们已经完成了元素的设置。让我们回到创建将渲染数据的视图。
var listOfUsersView = Backbone.View.extend({
el: '.user-management',// The element we defined in HTML
render: function () {
var self = this;// Saving the scope object
var _userList = new listOfUsers();
_userList.fetch({
success: function (data) {
var _userTemplate = _.template($('#user-list-template').html(),
{ users: data.models });
self.$el.html(_userTemplate);
}
});
}
});
我的做法是,我创建了一个 Backbone View,它有一个“el
”属性,用于保存 DOM 对象的引用。其次,“render()
”函数将使用 jQuery 将我们的模板加载到视图的“el
”属性中。此方法负责获取数据并使用从服务器获取的数据创建模板(render
方法不应包含从服务器获取数据的逻辑,我们有办法重构它,目前可以先这样)。现在的问题是,什么是
_.template($('#user-list-template').html(), { users: data.models });
这是 Backbone.js,它依赖于 Underscore.js,其中包括自己的模板解决方案。它将 JavaScript 模板编译成函数,可以用来进行渲染。适用于从 JSON 数据源渲染复杂的 HTML 片段。 了解更多关于 underscore 模板的信息。
创建模板
现在创建模板将非常直接,如果你使用过 Knockout 或 Angular,你就能很好地理解它。如果你没有……没关系……看看模板:
<script type="text/template" id="user-list-template">
<h3>Following are the user's:</h3>
<form class="list-group-form">
<a href="/#create" class="btn btn-default ">Create User</a>
<table class="table striped">
<thead>
<tr>
<th></th>
<th>Id</th>
<th>FirstName</th>
<th>LastName</th>
<th>Age</th>
<th>Actions</th>
</tr>
</thead>
<% _.each(users,function(user){%>
<tr>
<td>
<input type="checkbox" /></td>
<td><%= user.get('Id')%></td>
<td><%= user.get('FirstName')%></td>
<td><%= user.get('LastName')%></td>
<td><%= user.get('Age')%></td>
<td>
<a href="/#edit/<%= user.get('Id')%>"
class="btn btn-primary">Edit</a></td>
</tr>
<%});%>
</table>
</form>
</script>
Underscore 模板是一个 <script>
标签,其 type 为“text/template
”,这实际上可以防止浏览器一开始就渲染它。它不是浏览器可以识别的脚本,所以浏览器会忽略它。你可以在这里放置任何可以用作模板的内容。你可以使用许多其他库,如 Handlebar
/Mustache
等。
<script type="text/template" id="user-list-template">
</script>
我们几乎完成了创建用户模型、然后创建用户集合。然后我们创建了一个视图,它会创建一个用户集合对象并发出一个 fetch 请求。当 API 返回数据时,我们获取我们的模板并将数据绑定到它。最后,显示它。
最后,我们需要从 /#
路由(我们的默认路由)调用我们的 render
方法。所以,让我们稍微回到我们之前的地方:
// When hash tag has localhost# register the below route
route.on('route:home', function () {
// Todo: code to render create page
console.log("Display Create Page");
});
现在,我们需要做的是调用我们视图的 render
方法。
// When hash tag has localhost# register the below route
route.on('route:home', function () {
var objList = new listOfUsersView();
objList.render();
});
运行你的应用程序,检查控制台是否抛出任何错误。如果一切正常,你应该能看到数据库中的用户列表。
注意:如果看不到,请打开网络选项卡并检查 API 返回的响应。检查 API 返回的字段,并将其属性与模板中定义的属性进行匹配。
用户创建
现在,当我们准备好显示用户列表时,就实际创建用户并更新数据库吧。回到列表应用程序,检查用户是否已创建。
当你点击“Create User”链接时,你会看到 /#create
被附加到 URL,控制台会返回“Display Create Page”。所以我们需要在那里开始编写代码。在此之前,我们需要一个 Create View,它将负责显示我们的用户模型。
创建模板和基本视图
让我们开始创建用户视图。我们将为 Create
和 Update
使用相同的视图。
var specificUserView = Backbone.View.extend({
el: '.user-management',// The element we defined in HTML
render: function() {
}
});
好的。现在怎么办??
我们先不直接跳到表单字段,我喜欢先创建一个带有简单文本的另一个模板。当用户点击“Create User”超链接时,我们将显示它。
<script type="text/template" id="user-detail-template">
<h3>User Details:</h3>
@* Todo: Add the form tag and more *@
</script>
我想渲染这个模板,看看当我点击“Create User Hyperlink”时会发生什么。
var specificUserView = Backbone.View.extend({
el: '.user-management',// The element we defined in HTML
render: function() {
var _userDetailTemplate = _.template($('#user-detail-template').html());
this.$el.html(_userDetailTemplate);
}
});
在这里,我只是渲染模板,没有做任何其他事情。当 URL 更改为 /#Create
时调用此视图。所以,为此,我们回到定义创建路由的地方,现在是时候调用 specificUserView
的 render
方法了。
// When hash tag has localhost#create register the below route
route.on('route:create', function () {
var _objUser = new specificUserView();
_objUser.render();
});
运行你的应用程序,检查控制台是否抛出任何错误。如果一切正常,你应该能看到我们的文本被打印出来。让我们添加表单元素,并创建接受所需属性的表单。当我们添加输入字段时,表单看起来会非常像:
<script type="text/template" id="user-detail-template">
<h3>User Details</h3>
<table>
<tr>
<td>First Name</td>
<td>:</td>
<td>
<input type="text" class="first-name" /></td>
</tr>
<tr>
<td>Last Name</td>
<td>:</td>
<td>
<input type="text" class="last-name" /></td>
</tr>
<tr>
<td>Age</td>
<td>:</td>
<td>
<input type="text" class="age" /></td>
</tr>
</table>
<input type="button" value="Save" id="saveUserDetails"/>
</script>
创建保存事件
每个 Backbone View 都允许我们定义事件。例如:
var DocumentView = Backbone.View.extend({
events: {
"dblclick": "open",
"click .icon.doc": "select"
}
});
delegateEvents 接收你视图实例的事件:{ ... }
声明,并将指定的事件绑定到指定的 DOM 元素,并指定用于处理事件的回调方法。
我们需要一个提交按钮点击事件。所以我们在我们的
specificUserView
中为它创建一个事件,如下所示:
var specificUserView = Backbone.View.extend({
el: '.user-management',// The element we defined in HTML
events: {
'click #saveUserDetails': 'saveUserDetails'
},
render: function() {
var _userDetailTemplate = _.template($('#user-detail-template').html());
this.$el.html(_userDetailTemplate);
},
saveUserDetails: function() {
console.log("Save user details");
}
});
运行你的应用程序,检查控制台是否抛出任何错误。点击“Save”按钮,检查控制台消息是否反映在 **F12** 控制台窗口中。
现在,我们需要处理 saveUserDetails
方法。此方法负责使用当前填写的属性创建一个 model
对象并将详细信息保存到服务器。
我试图保持简单,就像
saveUserDetails: function () {
// Create a user model to fill the form details
var model = new userModel({
id: null,
FirstName: $('.first-name').val(),
LastName: $('.last-name').val(),
Age: $('.age').val()
});
model.save({}, {
success: function () {
console.log('Data Saved');
route.navigate('', { trigger: true });// Navigate back to listing page
}
});
}
编辑用户
现在,最大的挑战来了。我们将使用我们的创建表单来显示编辑信息。为此,我们需要对代码进行一些更改。
路由
//When hash tag has localhost# register the below route
route.on('route:edit', function (userId) {
var _objUserEdit = new specificUserView();
_objUserEdit.render(userId);
});
我们的路由将接受 Id
作为参数,并将其传递给 render
方法。
视图和模板
现在,我们需要更改我们的视图以接受用户 ID 并根据用户 ID 获取数据。此外,我们需要更新我们的视图,以便在提供值时显示值。因此,我更改了我的 specificUser View var
specificUserView
如下:
render: function (userId) {
var userDetails=null;
if (userId) {
var self = this;
// User update. We need to fetch user details from server and
// render the template.
var _userModel = new userModel({ id: userId });
_userModel.fetch({
data: {id:userId},
success: function (data) {
var _userDetailTemplate = _.template($('#user-detail-template').html(),
{ user: data });
self.$el.html(_userDetailTemplate);
}
});
}
else
{
// User is created
var _userDetailTemplate = _.template($('#user-detail-template').html(),
{ user: null });
this.$el.html(_userDetailTemplate);
}
}
同样,我们将 HTML 与模型数据绑定。
<tr>
<td>First Name</td>
<td>:</td>
<td>
<input type="text" class="first-name"
value="<%= user ? user.get('FirstName') : '' %>"/></td>
</tr>
<tr>
<td>Last Name</td>
<td>:</td>
<td>
<input type="text" class="last-name"
value="<%= user ? user.get('LastName') : '' %>"/></td>
</tr>
<tr>
<td>Age</td>
<td>:</td>
<td>
<input type="text" class="age" value="<%= user ? user.get('Age') : '' %>"/></td>
</tr>
所以,如果你看,现在所有输入元素都与值绑定了。
user.get('FirstName')
不一定需要像上面那样保持你的模板文件,你还有其他选择,如 handlebar.js 等,它们允许你在不同的文件中创建模板。可以预编译或在运行时编译。 更多关于 Handlebars 的信息。
重构的领域
上面的代码是使用 Backbone.js 的一个非常原始的版本。我们需要对上面提到的代码进行重构。例如,当你保存用户时,我们应该导航回列表页面以显示用户列表,例如:
route.navigate('', { trigger: true });
其次,一件重要的事情是销毁视图。
kill: function() {
// COMPLETELY UNBIND THE VIEW
this.undelegateEvents();
this.$el.removeData().unbind();
// Remove view from DOM
this.remove();
Backbone.View.prototype.remove.call(this);
}
我将尝试写更多关于内存泄漏的内容。
反馈和改进建议
这是我的第一篇帖子,我衷心欢迎你们的改进建议和指出我的错误。我在撰写此页面时可能失去了耐心,请随时批评。