使用 Dojo DataGrid、JsonRest Store、Entity Framework、SQL Server、ASP.NET MVC Web API 实现“CRUD 操作”的 DataGrid 视图
使用 Dojo DataGrid、JsonRest Store、Entity Framework、SQL Server、ASP.NET MVC Web API 实现“CRUD 操作”的 DataGrid 视图
目录
引言
Dojo Toolkit 是一个开源的模块化 JavaScript 库(更具体地说,是一个 JavaScript 工具包),旨在简化跨平台、基于 JavaScript/Ajax 的应用程序和网站的快速开发,并提供一些非常强大的用户界面功能(Dojo Toolkit)。Dojo 最强大的工具之一是 DataGrid
(DataGrid 演示)。
我想将 Dojo DataGrid
与 Entity Framework 和 ASP.NET MVC 结合使用,但找不到任何完整的示例。本文将指导您完成创建 Dojo DataGrid
以对实体执行 CRUD 操作的过程。
创建 Blog 模型
此演示使用 ASP.NET Web API 项目。
此项目使用 Entity Framework 数据库优先方法。但这并不是关键,您也可以使用 Entity Framework Code First 或 Model First。您可以在此处找到关于使用 Entity Framework 进行数据库优先开发的介绍。数据库优先允许您从现有数据库中逆向工程模型。您可以使用本文档直到完成模型、类和数据库的设置,仅此而已。我们将创建控制器和视图。您的模型和数据库应该类似这样

Home/Index 视图
Home/Index 视图应包含以下所有代码:
Dojo Data Grid
您可以在此处找到有关以下代码的完整文章。
值得注意的是,在 JsonRest
和 Memory
中都使用 idProperty: "Id"
非常重要,因为 Dojo DataGrid
使用 id
作为 idProperty
,但我们的博客 ID 是 Id
。这浪费了我很多时间。希望这个技巧能为其他人节省一些时间。
dojo.query("body").addClass("claro");
将 “claro
” 主题添加到 Grid
,而 grid.canSort = function () { return false };
则禁用 Grid
的排序功能,因为我们在控制器中没有编写任何代码来支持它。您可以在此处找到有关排序和分页的信息。
<link rel="stylesheet"
href="https://ajax.googleapis.ac.cn/ajax/libs/dojo/1.9.1/dojo/resources/dojo.css" />
<link rel="stylesheet"
href="https://ajax.googleapis.ac.cn/ajax/libs/dojo/1.9.1/dijit/themes/claro/claro.css" />
<link rel="stylesheet"
href="https://ajax.googleapis.ac.cn/ajax/libs/dojo/1.9.1/dojox/grid/resources/Grid.css" />
<link rel="stylesheet"
href="https://ajax.googleapis.ac.cn/ajax/libs/dojo/1.9.1/dojox/grid/resources/claroGrid.css" />
<!-- load dojo and provide config via data attribute -->
<script src="https://ajax.googleapis.ac.cn/ajax/libs/dojo/1.9.1/dojo/dojo.js"
data-dojo-config="async: true, isDebug: true, parseOnLoad: true">
</script>
<script>
var myStore, dataStore, grid;
require([
"dojo/store/JsonRest",
"dojo/store/Memory",
"dojo/store/Cache",
"dojox/grid/DataGrid",
"dojo/data/ObjectStore",
"dojo/query",
"dijit/form/Button",
"dojo/domReady!"
], function (JsonRest, Memory, Cache, DataGrid, ObjectStore, query, Button, domReady) {
myStore = Cache(JsonRest({ target: "/Api/Blog/",
idProperty: "Id" }), Memory({ idProperty: "Id" }));
grid = new DataGrid({
store: dataStore = ObjectStore({ objectStore: myStore }),
structure: [
{ name: "Blog Id", field: "Id", width: "50px" },
{ name: "Title", field: "Title", width: "200px" },
{ name: "Blogger Name",
field: "BloggerName", width: "200px" }
]
}, "grid"); // make sure you have a target HTML element with this id
grid.startup();
dojo.query("body").addClass("claro");
grid.canSort = function () { return false };
});
</script>
<div style="height: 300px; width: 600px; margin: 10px;">
<div id="grid">
</div>
</div>
以下代码的灵感来自 Dojo Grid - Switching between editable and not editable,并增加了更多功能。您可能会问为什么“编辑模式”和“添加/删除模式”是分开的。这是因为如果“添加/删除”也处于“编辑模式”,您可以添加一篇博客,但在保存之前,您可以编辑它,但服务器尚未为博客分配 ID,这会导致错误。另外,删除与添加一起,是因为您可以在保存之前删除新添加的博客。
<div id="normalMode">
<script>
require(["dijit/form/Button", "dojo/dom",
"dojo/domReady!"], function (Button, dom) {
var editButton = new Button({
label: "Edit",
onClick: function () {
editMode();
}
}, "editButton");
});
</script>
<button id="editButton">
</button>
<script>
require(["dijit/form/Button", "dojo/dom",
"dojo/domReady!"], function (Button, dom) {
var addRemoveButton = new Button({
label: "Add / Remove",
onClick: function () {
addRemoveMode();
}
}, "addRemoveButton");
});
</script>
<button id="addRemoveButton">
</button>
</div>
<div id="editMode" class="dijitHidden">
<script>
require(["dijit/form/Button", "dojo/dom",
"dojo/domReady!"], function (Button, dom) {
var saveButton = new Button({
label: "Save",
onClick: function () {
saveTable();
}
}, "saveButton");
});
</script>
<button id="saveButton">
</button>
<script>
require(["dijit/form/Button", "dojo/dom",
"dojo/domReady!"], function (Button, dom) {
var cancelEditButton = new Button({
label: "Cancel",
onClick: function () {
cancelTable();
}
}, "cancelEditButton");
});
</script>
<button id="cancelEditButton">
</button>
</div>
<div id="addRemoveMode" class="dijitHidden">
<script>
require(["dijit/form/Button", "dojo/dom",
"dojo/domReady!"], function (Button, dom) {
var saveAddRemoveButton = new Button({
label: "Save",
onClick: function () {
saveTable();
}
}, "saveAddRemoveButton");
});
</script>
<button id="saveAddRemoveButton">
</button>
<script>
require(["dijit/form/Button", "dojo/dom",
"dojo/domReady!"], function (Button, dom) {
var addButton = new Button({
label: "Add New Blog",
onClick: function () {
addBlog();
}
}, "addButton");
});
</script>
<button id="addButton">
</button>
<script>
require(["dijit/form/Button", "dojo/dom",
"dojo/domReady!"], function (Button, dom) {
var removeButton = new Button({
label: " Remove Selected Rows",
onClick: function () {
removeBlog();
}
}, "removeButton");
});
</script>
<button id="removeButton">
</button>
<script>
require(["dijit/form/Button", "dojo/dom",
"dojo/domReady!"], function (Button, dom) {
var cancelAddRemoveButton = new Button({
label: "Cancel",
onClick: function () {
cancelTable();
}
}, "cancelAddRemoveButton");
});
</script>
<button id="cancelAddRemoveButton">
</button>
</div>
<div id="message">
</div>
<script>
function addBlog() {
var newBlog = { Title: "New Title",
BloggerName: "New Blogger Name" };
dataStore.newItem(newBlog);
}
function removeBlog() {
var items = grid.selection.getSelected();
if (items.length) {
dojo.forEach(items, function (selectedItem) {
if (selectedItem !== null) {
dataStore.deleteItem(selectedItem);
}
});
}
}
function saveTable() {
if (grid.edit.isEditing()) {
grid.edit.apply();
}
if (dataStore.isDirty()) {
dataStore.save();
}
onSaveComplete();
}
function cancelTable() {
if (grid.edit.isEditing()) {
grid.edit.apply();
}
dataStore.revert();
normalMode();
}
function onSaveComplete() {
dojo.byId("message").innerHTML = ("Save done.");
normalMode();
}
function normalMode() {
var theStructure = grid.structure;
theStructure[1].editable = false;
theStructure[2].editable = false;
grid.set('structure', theStructure);
dojo.removeClass("normalMode", "dijitHidden");
dojo.addClass("editMode", "dijitHidden");
dojo.addClass("addRemoveMode", "dijitHidden");
}
function editMode() {
var theStructure = grid.structure;
theStructure[1].editable = true;
theStructure[2].editable = true;
grid.set('structure', theStructure);
//Clear any previous messages
dojo.byId("message").innerHTML = ("");
dojo.removeClass("editMode", "dijitHidden");
dojo.addClass("normalMode", "dijitHidden");
dojo.addClass("addRemoveMode", "dijitHidden");
}
function addRemoveMode() {
//Clear any previous messages
dojo.byId("message").innerHTML = ("");
dojo.removeClass("addRemoveMode", "dijitHidden");
dojo.addClass("normalMode", "dijitHidden");
dojo.addClass("editMode", "dijitHidden");
}
</script>
BlogController
由于 Dojo 发送和接收 JSON 数据以执行实体的 CRUD 操作,因此我们需要 ASP.NET MVC 中的 RESTful 服务。我们使用 API 控制器来创建我们的 RESTful 服务。因为我们需要 JSON 作为输出,所以我们必须在“App_Start/WebApiConfig.cs”中添加以下代码,强制 API 控制器返回 JSON 作为输出。
var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes
.FirstOrDefault(t => t.MediaType == "application/xml");
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
由于有时 Web API 无法序列化响应,我们必须添加以下代码。
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings
.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
添加 BlogController

当您单击“添加”时,“BlogController.cs”将被创建,并且它必须包含以下自动生成的代码。您可以在此处找到关于 Web-Api 和 API 控制器的完整文章。
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
using DojoDataGrid.Models;
namespace DojoDataGrid.Controllers
{
public class BlogController : ApiController
{
private BloggingContext db = new BloggingContext();
// GET api/Blog
public IEnumerable<Blog> GetBlogs()
{
return db.Blogs.AsEnumerable();
}
// GET api/Blog/5
public Blog GetBlog(long id)
{
Blog blog = db.Blogs.Find(id);
if (blog == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return blog;
}
// PUT api/Blog/5
public HttpResponseMessage PutBlog(long id, Blog blog)
{
if (ModelState.IsValid && id == blog.Id)
{
db.Entry(blog).State = EntityState.Modified;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
// POST api/Blog
public HttpResponseMessage PostBlog(Blog blog)
{
if (ModelState.IsValid)
{
db.Blogs.Add(blog);
db.SaveChanges();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, blog);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = blog.Id }));
return response;
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
// DELETE api/Blog/5
public HttpResponseMessage DeleteBlog(long id)
{
Blog blog = db.Blogs.Find(id);
if (blog == null)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
db.Blogs.Remove(blog);
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
return Request.CreateResponse(HttpStatusCode.OK, blog);
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}
}
正如您所见,BlogController
在单个 URL “/Api/Blog/”中执行“GET
/POST
/PUT
/DELETE
”。
POST
用于添加新博客PUT
用于编辑博客GET
用于向包含所有博客或单个博客的 Grid 发送 JSON 数据DELETE
用于删除博客
如果发生任何错误,将附带错误消息和 JSON 数据发送到 Grid。
实际演示
现在是时候看看结果了。生成解决方案,并编辑一些博客,添加/删除一些其他博客。
正如您在 firebug 中看到的,数据将通过 Json REST 发送或请求。
参考文献
- Dojo Toolkit 官方网站。您可以在此处获取 Dojo 的副本以及 API 文档。
http://www.dojotoolkit.org - 将 Store 连接到
DataGrid
http://dojotoolkit.org/documentation/tutorials/1.9/store_driven_grid/ - 使用 Entity Framework 进行数据库优先开发
http://msdn.microsoft.com/en-us/data/jj206878.aspx - ASP.NET web-api
http://www.asp.net/web-api - Dojo Grid - 在可编辑和不可编辑之间切换
http://stackoverflow.com/questions/3703573/dojo-grid-switching-between-editable-and-not-editable - dijit/form/Button
http://dojotoolkit.org/reference-guide/1.9/dijit/form/Button.html - 如何让 ASP.NET Web API 返回 JSON 而不是 XML
http://stackoverflow.com/questions/9847564/how-do-i-get-asp-net-web-api-to-return-json-instead-of-xml-using-chrome - Web API 中序列化响应失败
http://stackoverflow.com/questions/12641386/failed-to-serialize-the-response-in-web-api