Backbone.js 在 asp.net mvc razor 第 2 部分






4.25/5 (4投票s)
使用 backbone 添加、更新、删除对象
引言
这是 我第一个 backbone 教程 的升级版。 在第一个教程中,我们对要更新/插入的数据进行了硬编码,现在我们将进行实时更新。
背景
流派更新屏幕 -
流派插入屏幕 -
流派删除屏幕 -
使用代码
Genre.cshtml-
现在我们将添加两个与上一个教程类似的模板。 '#Genre-Edit-Template' 用于插入和更新,以及 '#Genre-Delete-Template' 用于删除。
@{
ViewBag.Title = "Genres";
}
<script src="@Url.Content("~/Scripts/StoreManager/Genre.js")" type="text/javascript"></script>
<div class="styler">
<fieldset class="ui-widget">
<legend class="ui-state-legend-default ui-corner-top ui-corner-bottom">Genre List -
Using Backbone</legend>
<div id="Genre_Container">
<input type="button" value="Create New" class="Add" id="btnCreateNew" />
<table id="Genre_List">
<tr>
<th>
Name
</th>
<th>
Description
</th>
<th>
</th>
</tr>
</table>
<div id="GenreEditDialog" style="height: 350">
<div class="styler">
<fieldset class="ui-widget" id="Genre_Edit" style="height: 350">
<legend class="ui-state-legend-default ui-corner-top ui-corner-bottom"> </legend>
<br />
<div id="valSum" >
</div>
</fieldset>
</div>
</div>
</div>
<script id='Genre-Template' type='text/template'>
<td><%=Name%></td> <td><%=Description%></td>
<td><input type="button" value= "Edit" class="Edit" /> | <input type="button" value= "Delete" class="Delete" /> </td>
</script>
<script id='Genre-Edit-Template' type='text/template'>
<tr>
<td>
<div class="editor-label">
Name</div>
<div class="editor-field">
<input type="text" class="gName" value='<%=Name%>' />
</div>
</td>
<td>
<div class="editor-label">
Description</div>
<div class="editor-field">
<input type="text" class="gDesc" value='<%=Description%>' />
</div>
</td>
</tr>
<tr>
<td colspan="2">
<input type="button" value="Save" class="Update" /> | <input type="button" id="btnCancel" value="Cancel" class="Cancel" />
</td>
</tr>
</script>
<script id='Genre-Delete-Template' type='text/template'>
<div class="ui-state-highlight ui-corner-all" style="margin-top: 20px; padding: 0 .7em;
width: 500px">
<p>
<span class="ui-icon ui-icon-info" style="float: left; margin-right: .3em;"></span>
<strong>Confirm</strong><br />
Are you sure you want to delete this Album - <%=Name%> ?</p>
</div><br/>
<table>
<tr>
<td>
<div class="editor-label">
Name</div>
<div class="editor-field">
<%=Name%>
</div>
</td>
<td>
<div class="editor-label">
Description</div>
<div class="editor-field">
<%=Description%>
</div>
</td>
</tr>
<tr>
<td colspan="2">
<input type="button" value="Delete" class="Update" /> | <input type="button" id="btnCancel" value="Cancel" class="Cancel" />
</td>
</tr>
</table>
</script>
</fieldset>
</div>
Genre.js -
好吧,这并不是什么高深的东西,你现在应该已经猜到了,我们需要有两个视图 EditView 和 DeleteView,并在单击更新和删除按钮时分别实例化它们(传递流派模型)。 在创建的情况下,我们将使用新的流派模型实例化 EditView。 然后渲染视图,以便我们可以查看它们。
流派模型 -
我们添加了一个新函数 validateModel。 此函数将验证模型并显示验证摘要。
同样,在我上一个教程中,我曾指出,在创建新流派后,单击相应的“编辑”会触发 Http Post 而不是 put。 原因是,即使添加了新流派,该流派也被标识为新对象(genre.isNew() 为 true),backbone 总是对新对象执行 post。 原因? 好吧,我们数据库中的流派实体没有名为 id 的属性。 Backbone 需要此属性来验证对象是新的还是旧的。 我们通过在 GenreId 属性更改时将 GenreId 设置为 id 来处理这个问题。
EditView -
在 Editview 中请注意,我们直接绑定了名称和描述文本框('.gName', '.gDesc'),并将值设置为模型。 通过这样做,我们充分利用了 backbone.js,我们正在使视图和模型相互交互。 难道不酷吗。
在渲染视图(render 函数)时,我们打开一个包含编辑视图的对话框,设置标题“创建”或“编辑”。 否则,它与在“el”上应用模板并在视图容器“#Genre_Edit”上附加相同。
在更新模型时,我们调用模型的 validateModel() 函数,在出错时显示验证摘要,否则保存模型。 请注意,在“创建新”时,我们将新流派添加到集合中并保存模型。
通过这个,我们能够使用单个视图完成插入和编辑流派。
DeleteView -
我感觉这个视图是不言自明的。 我们唯一做的不同的是,我们正在销毁模型。
$(function () {
$("#GenreEditDialog").dialog({
autoOpen: false,
show: "blind",
hide: "explode",
modal: true,
height: 250,
width: 600,
open: function (event, ui) {
$('.ui-dialog-titlebar', ui.dialog || ui).hide();
}
});
// Genre Model
var Genre = Backbone.Model.extend({
url: function () {
var base = '/StoreManager/GenreList/';
if (this.isNew())
return base;
return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + this.id;
},
initialize: function () {
console.log('Genre Constructor Triggered');
this.bind("change:GenreId", function () {
var genreId = this.get('GenreId');
this.set({ id: genreId });
});
},
defaults: {
GenreId: 0,
Name: '',
Description: ''
},
validateModel: function () {
var validate = true;
var divHtml = "<div class='ui-state-error ui-corner-all' style='display: block; padding-top: 0px; padding-bottom: 0px; width: 500px; padding-left: 0.7em; padding-right: 0.7em;'>";
divHtml += "<p><span class='ui-icon ui-icon-alert' style='float: left; margin-right: .3em;'></span><strong>Please correct these Errors</strong> </p><div class='validation-summary-errors' data-valmsg-summary='true'>";
divHtml += "</div><p></p></div>";
var ulHtml = "<ul></ul>";
var div = $(divHtml);
var ul = $(ulHtml);
if (this.get('Name') == "") {
ul.append("<li>Name is Mandatory</li>");
validate = false;
}
if (this.get('Description') == "") {
ul.append("<li>Description is Mandatory</li>");
validate = false;
}
div.find(".validation-summary-errors").append(ul);
if (!validate) {
$('#valSum').show("slow");
$('#valSum').html(div);
}
else {
$('#valSum').hide("slow");
$('#valSum').html("");
}
return validate;
}
});
// Genre Collection
var GenreCollection = Backbone.Collection.extend({
model: Genre,
url: '/StoreManager/GenreList/'
});
//Genre-Edit view
var EditView = Backbone.View.extend({
template: _.template($('#Genre-Edit-Template').html()),
tagName: "table",
container: "#Genre_Edit",
initialize: function () {
console.log('Genre Edit View Constructor Triggered');
},
events: {
"change .gName": 'NameChange',
"change .gDesc": 'DescChange',
"click .Update": 'UpdateGenre',
"click .Cancel": 'CancelGenre'
},
NameChange: function () {
// Directly bind the name change to the model, best use of back bone.
// Otherwise set the data during save which ever you prefer
this.model.set({ Name: $(".gName").val() });
},
DescChange: function () {
this.model.set({ Description: $(".gDesc").val() });
},
render: function () {
$(this.container).append($(this.el).html(this.template(this.model.toJSON())));
$("input:button", $(this.el)).button();
$("#GenreEditDialog").dialog("open");
var title = 'Create Genre';
if (this.collection == null)
title = 'Edit Genre - ' + this.model.get('Name');
$(this.container).find('legend').html(title);
$('.ui-dialog-titlebar').hide();
$('#valSum').hide("slow");
return this;
},
unrender: function () {
$(this.el).remove();
return this;
},
UpdateGenre: function () {
if (this.model.validateModel()) {
var self = this;
if (this.collection != null) {
this.collection.add(this.model);
}
this.model.save(this.model, { success: function () {
self.unrender();
$("#GenreEditDialog").dialog("close");
var genreId = self.model.get('GenreId');
$("input:button", '#Genre_List').button();
}
});
}
$("input:button", '#Genre_List').button();
},
CancelGenre: function () {
this.model.fetch({ success: function () {
$("input:button", '#Genre_List').button();
}
});
this.unrender();
$("#GenreEditDialog").dialog("close");
}
});
// Genre Delete View
var DeleteView = Backbone.View.extend({
template: _.template($('#Genre-Delete-Template').html()),
container: "#Genre_Edit",
initialize: function () {
console.log('Genre Edit View Constructor Triggered');
},
events: {
"click .Update": 'DeleteGenre',
"click .Cancel": 'CancelGenre'
},
render: function () {
$(this.container).append($(this.el).html(this.template(this.model.toJSON())));
$("input:button", $(this.el)).button();
$("#GenreEditDialog").dialog("open");
$('.ui-dialog-titlebar').hide();
var title = 'Delete Genre - ' + this.model.get('Name');
$(this.container).find('legend').html(title);
return this;
},
unrender: function () {
$(this.el).remove();
return this;
},
CancelGenre: function () {
this.unrender();
$("#GenreEditDialog").dialog("close");
},
DeleteGenre: function () {
var self = this;
this.model.destroy({ success: function () {
self.unrender();
$("#GenreEditDialog").dialog("close");
}
});
}
});
// Genre View - el returns the template enclosed within a tr
var GenreView = Backbone.View.extend({
template: _.template($('#Genre-Template').html()),
tagName: "tr",
initialize: function () {
console.log('GenreView Constructor Triggered');
this.model.bind('change', this.render, this);
this.model.bind('remove', this.unrender, this);
},
render: function () {
console.log('Rendering...');
$(this.el).html(this.template(this.model.toJSON()));
return this;
},
unrender: function () {
console.log('Un-Rendering...');
$(this.el).remove();
return this;
},
events: {
"click .Edit": 'EditGenre',
"click .Delete": 'DeleteGenre'
},
EditGenre: function () {
var editView = new EditView({ model: this.model });
editView.render();
},
DeleteGenre: function () {
var deleteView = new DeleteView({ model: this.model });
deleteView.render();
}
});
// Actual App view
var AppView = Backbone.View.extend({
initialize: function () {
this.collection.bind('add', this.AppendGenre, this);
},
el: '#Genre_Container',
events: {
"click #btnCreateNew": "AddNewGenre"
},
AddNewGenre: function () {
console.log('Add Genre....');
var newGenre = new Genre();
var editView = new EditView({ model: newGenre, collection: this.collection });
editView.render();
},
AppendGenre: function (genre) {
var genreView = new GenreView({ model: genre });
$(this.el).find('#Genre_List').append(genreView.render().el);
},
render: function () {
if (this.collection.length > 0) {
this.collection.each(this.AppendGenre, this);
}
$("input:button", "#Genre_List").button();
}
});
var genres = new GenreCollection();
var view = new AppView({ collection: genres });
genres.fetch({ success: function () {
view.render();
}
});
});
控制器方法 -
请注意,我们现在只处理 httppost 操作中的插入。 我们之前的代码同时处理插入和更新。 由于我们在模型上添加了 id 属性,因此不再需要处理更新。
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Genres(int? Id)
{
return View();
}
// Get Genres
[AcceptVerbs(HttpVerbs.Get)]
public JsonResult GenreList (int? Id)
{
if (Id.HasValue)
{
var genre = db.Genres
.Where(x => x.GenreId == Id.Value)
.Select(x => new { id = x.GenreId, GenreId = x.GenreId, Name = x.Name, Description = x.Description })
.FirstOrDefault();
return Json(genre, JsonRequestBehavior.AllowGet);
}
var genres = db.Genres
.Select(x => new { id = x.GenreId, GenreId = x.GenreId, Name = x.Name, Description = x.Description })
.ToList();
var result = Json(genres, JsonRequestBehavior.AllowGet);
return result;
}
// Update Genre
[AcceptVerbs(HttpVerbs.Put)]
public JsonResult GenreList(int Id, Genre UpdatedGenre)
{
var genre = db.Genres.Where(x => x.GenreId == Id).FirstOrDefault();
genre.Name = UpdatedGenre.Name;
genre.Description = UpdatedGenre.Description;
db.SaveChanges();
return Json(genre, JsonRequestBehavior.DenyGet);
}
//Add Genre
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult GenreList(Genre CreateGenre)
{
db.Genres.Add(CreateGenre);
db.SaveChanges();
return Json(CreateGenre, JsonRequestBehavior.DenyGet);
}
//Delete Genre
[AcceptVerbs(HttpVerbs.Delete)]
public JsonResult GenreList(int Id)
{
var genre = db.Genres.Where(x => x.GenreId == Id).FirstOrDefault();
db.Genres.Remove(genre);
db.SaveChanges();
return Json(genre, JsonRequestBehavior.DenyGet);
}
值得关注的点
我们没有在流派模型中使用 Backbone.Validate 函数,仅仅是因为我们想在验证摘要上显示错误。 模型上的 Validate 函数在保存和设置对象时被触发,但它不会验证整个模型。 它只会验证模型中已更改的属性。 因此,使用 Validate 函数实现验证摘要具有很大的挑战性。
还有一件事,在我们的“validateModel”函数中,我们正在进行一些 DOM 操作。 好吧,这是一个不好的做法。 最好将我们的模型与任何 DOM 操作代码分开。 所有 DOM 操作都应该在视图内部进行。 我在这个教程中只是稍微放纵了一下。 这样做更好的方法是在 EditView.UpdateGenre 函数内部处理验证摘要。 好吧,我把它留给你来弄清楚。 我猜这应该很容易。
编码愉快....
-- ANON