客户端数据网格,支持 JSON 和 Web Service






4.95/5 (21投票s)
在 asp.net 中插入、删除、更新网格,无需任何回发事件/页面加载。
- 下载 Client_Grid_with_JSONWebService-noexe.zip - 76 KB
- 下载 Client_Grid_with_JSONWebService.zip - 83.4 KB
引言
数据网格是 Web 应用程序中最重要的部分。大多数时候我们需要在数据网格中添加更多值,再次也需要更新/删除已存储在网格中的数据。但问题在于回发。在每次我们想从网格插入/更新/删除的情况下,页面都会触发回发事件,结果就是速度变慢。在本文中,我使用 Web 服务和 JSON 开发了一个数据网格。我将 JSON 与 Ajax 一起使用。结果是,我可以避免回发事件,并且可以快速地在网格中插入/删除/更新。但首先,我将讨论一些与本文相关的概念。
- 什么是 JSON
- JSON 与 Ajax
- XML
- Web 服务如何工作
- JSON 如何与 Web 服务连接
- 我创建的客户端网格是如何工作的
- 重要性/局限性
什么是 JSON
JSON 意思是 JavaScript 对象表示法,是一种轻量级的数据交换格式。它易于人类阅读和编写。易于机器解析和生成。JSON 是一种纯文本格式,完全独立于语言,但使用了 C 语言家族的程序员(包括 C、C++、C#、Java、JavaScript、Perl、Python 等)熟悉的约定。这些特性使 JSON 成为理想的数据交换语言。
JSON 构建于两种结构之上
- 一组名/值对。在各种语言中,这可以实现为对象、记录、结构、字典、哈希表、键列表或关联数组。
- 有序的值列表。在大多数语言中,这可以实现为数组、向量、列表或序列。
这些是通用的数据结构。几乎所有现代编程语言都以某种形式支持它们。一个与编程语言可互换的数据格式也基于这些结构是合理的。
JSON 与 Ajax
我们经常在 Ajax 中使用 JSON。Ajax 是指网页在加载到 Web 浏览器后请求新数据的能力,通常是响应显示网页上的用户操作。
作为 Ajax 模型的一部分,新数据通常在从服务器返回时动态地合并到用户界面显示中。这方面的一个例子可能是,当用户在搜索框中输入时,客户端代码会将他们已输入的内容发送到服务器,服务器会从其数据库中响应可能的完整搜索词列表。这些可能会显示在搜索框下方的下拉列表中,以便用户可以停止输入并直接选择一个完整且常用的搜索字符串。当它在 2000 年代中期首次被描述时,Ajax 通常使用 XML 作为数据交换格式,但许多开发人员也使用 JSON 来在服务器和客户端之间传递 Ajax 更新。
XML
XML 是任何操作系统熟悉的通用数据存储格式。XML 以结构化和序列化的方式存储其数据。存在各种基于 XML 的协议来表示与 JSON 相同类型的数据结构,用于相同的数据交换目的。当数据以 XML 编码时,结果通常比等效的 JSON 编码要大,这主要是因为 XML 的闭合标签。在 XML(和 JSON)中,存在编码相同信息的替代方法,因为某些值可以表示为子节点和属性。XML 中的示例数据格式
<Country> <name>Bangladesh</name> <capitalName>Dhaka</capitalName> <shortDetails> <Independence>1971</Independence> <Language>Bangla</Language> <popularSports> Cricket </popularSports> </shortDetails> </Country>
Web 服务如何工作
Web 服务是一种技术框架,它允许在网络上进行机器对机器 (M2M) 交互。它可以调用应用程序组件。该协议使用 XML 进行通信。它不绑定到任何一个操作系统或编程语言。它主要与以下协议协同工作:
- SOAP (简单对象访问协议)
- UDDI (通用描述、发现和集成)
- WSDL (Web 服务描述语言)
有关 Web 服务的更多知识,请阅读这些 文章
JSON 如何与 Web 服务连接
首先,JSON 会发送一个带有 Web 服务引用的请求到 Web 服务器。Web 服务器执行
Web 服务,根据参数进行处理。执行后,Web 服务将 SOAP 消息(XML 格式的序列化数据)发送到浏览器(通过 Web 服务器)。然后 JSON 获取 XML 格式的数据并使用它。
下图将清楚地说明 JSON 和 Web 服务之间的通信
首先,创建一个 Web 应用程序并添加一个 Web 服务 (.asmx) 文件。现在创建一个 Get 方法从数据库中获取值。
[WebMethod] public List<Employee> GetEmployeeDetails() { // getting connection string string conStr = WebConfigurationManager.ConnectionStrings["TESTDB"].ConnectionString; DataTable dt = new DataTable(); SqlDataReader dr = null; // Creating Sql Connection using (SqlConnection conn = new SqlConnection(conStr)) { // Creating insert statement string sql = string.Format(@"Select * from [SampleInfoTable]"); SqlCommand cmd = new SqlCommand(); cmd.Connection = conn; cmd.CommandText = sql; cmd.CommandType = CommandType.Text; conn.Open(); dr = cmd.ExecuteReader(); dt.Load(dr); conn.Close(); cmd = null; } int countRow = dt.Rows.Count; foreach (DataRow drEmp in dt.Rows) { Employee objemployee = new Employee(); objemployee.id = Convert.ToInt32(drEmp["id"].ToString()); objemployee.Name = drEmp["Name"].ToString(); objemployee.Email = drEmp["Email"].ToString(); objemployee.Phone = drEmp["Phone"].ToString(); objemployee.Address = drEmp["Address"].ToString(); list.Add(objemployee); } return list; }
此方法将通过 SOAP 消息将数据发送到 Web 服务器
现在添加一个 Web 页面,并在 JavaScript 区域创建使用 JSON 和 Ajax 的方法。 要检查,请浏览 Web 服务,您将获得 JSON 所需的 XML 数据。查看
Ajax URL 的引用。
function GetEmployeeDetails() { $.ajax({ type: "POST", url: "hr_webservice.asmx/GetEmployeeDetails", data: "{}", contentType: "application/json; charset=utf-8", dataType: "json", success: function (response) { BindTable(response.d); }, failure: function (msg) { alert(msg); } }); }
url: "hr_webservice.asmx/GetEmployeeDetails",
Web 服务在该链接中准备好数据
这就是 JSON 和 Web 服务之间的实际通信。 在获取数据后,我用 JavaScript 绑定了一个表。还添加了编辑和删除控件以及事件。
从 Web 服务获得的输出是通过 XML 序列化过来的。当 JSON 的 success 属性获取到数据时,它会调用另一个带有 result 对象的方法。实际上,整个表被视为一个名为 Employee 的对象。
我创建了一个表头值的表,另一个 div 用于通过 JavaScript 生成表。
在 BindTable 中,我首先检查动态生成的表是否存在。如果存在,则移除该表,因为我将用更新的数据重新生成它。
<span class="Apple-tab-span" style="white-space: pre;"> // // The method used to generate the grid with table, This method fire when the // JSON find the well serealized data from webService. // <span class="Apple-tab-span" style="white-space: pre;"> function BindTable(Employees) { var root = document.getElementById('mydiv'); try { var tblId = document.getElementById('tblGridValue'); if (tblId != null) { root.removeChild(tblId); } } catch (e) { }
然后,在 div 中创建一个表。还为表添加了 ID,以及宽度、样式和其他属性。
var tab = document.createElement('table'); tab.setAttribute("id", "tblGridValue"); tab.setAttribute("class", "tableStyle"); tab.setAttribute("cellspacing", "3px"); var tbo = document.createElement('tbody');
让我们看看网格行是如何生成的:我应用了一个 foreach 循环到 LIST 对象以获取 LIST 的每一行,并且
var row, cell; // the list object now extract the value for each row $.each(Employees, function (index, employee) { row = document.createElement('tr'); row.setAttribute("class", "tableRaw");
我为每一行创建了一个行。由于我的每个列表单元格包含 5 列,因此我需要拆分/检测,这就是为什么我再次使用循环来处理每一列。在循环中,我为每一列创建了 <td>。
该行是从 result 对象获取的对象绑定的,因此生成的行数取决于每一行的行数。列的数量根据对象的值进行绑定。还为每列添加了属性。
for (var j = 0; j < 5; j++) { cell = document.createElement('td'); cell.setAttribute("width", "200px"); var empId = employee.id; var empName = employee.Name; var empEmail = employee.Email; var empPhone = employee.Phone; var empAddress = employee.Address; if (j == 0) { //Create an input type dynamically. var hiddenId = document.createElement("input"); //Assign different attributes to the element. hiddenId.setAttribute("type", "hidden"); hiddenId.setAttribute("id", "hfRow_" + employee.Id); hiddenId.setAttribute("value", employee.Id); cell.appendChild(hiddenId); cell.appendChild(document.createTextNode(employee.Name)); } else if (j == 1) { var spanValue = document.createElement("span"); cell.setAttribute("width", "200px"); spanValue.setAttribute("display", "inline-block"); spanValue.appendChild(document.createTextNode(employee.Email)); cell.appendChild(spanValue); } else if (j == 2) { cell.setAttribute("width", "200px"); cell.appendChild(document.createTextNode(employee.Phone)); } else if (j == 3) { cell.setAttribute("width", "200px"); cell.appendChild(document.createTextNode(employee.Address)); }
此处使用了一些隐藏字段来存储主键和按钮的操作模式。主键用于更新和删除。操作模式用于标记按钮发送的是插入/更新/删除哪个操作请求。
还在最后一列动态创建了编辑和删除按钮。
// at the last column else if (j == 4) { // // in this state loop generates Edit and Delete button for each row // var element = document.createElement("img"); element.setAttribute("src", "images/edit-icon.gif"); element.setAttribute("width", "15px"); cell.setAttribute("width", "100px"); // // This loop also adding a click event EditMode() // element.setAttribute("onclick", "EditMode('" + empId + "','" + empName + "','" + empEmail + "','" + empPhone + "','" + empAddress + "')"); cell.appendChild(element); // // Same way the row created Delete button // var elementDelete = document.createElement("img"); elementDelete.setAttribute("src", "images/DeleteRed.png"); elementDelete.setAttribute("width", "15px"); // // Also created the Delete Method in onclick event // elementDelete.setAttribute("onclick", "return DeleteMode('" + empId + "')"); cell.appendChild(elementDelete); } // // //
按钮还添加了点击事件,将在点击时触发编辑模式。
因此,当编辑图像按钮点击时,EditMode() 方法会触发。
// // While the Edit button clicks, the values sets on entry fields, at the same time the button // name also changed to UPDATE and Hidden field value also changed to UPDATE. When I will sent the hidden field // to procedure as action mode, the procedure will work for update // function EditMode(empId, empName, empEmail, empPhone, empAddress) { var hfEditedId = document.getElementById('hfEditedId'); var txEditedName = document.getElementById('txEditedName'); var txEditedEmail = document.getElementById('txEditedEmail'); var txEditedPhone = document.getElementById('txEditedPhone'); var txEditedAddress = document.getElementById('txEditedAddress'); var hfActionMode = document.getElementById('hfActionMode'); var btnEntry = document.getElementById('btnEntry'); hfEditedId.value = empId; txEditedName.value = empName; txEditedEmail.value = empEmail; txEditedPhone.value = empPhone; txEditedAddress.value = empAddress; hfActionMode.value = "UPDATE"; btnEntry.value = "Update"; }
同时,模式以简单的方式改变。在此状态下,隐藏字段存储所选行的主键。
点击更新按钮后,edit 方法会触发,该方法会再次将编辑后的值直接发送到数据库。
// // Edit Employee Data // function EditEmployeeData() { var editedName = document.getElementById('txEditedName'); var editedEmail = document.getElementById('txEditedEmail'); var editedPhone = document.getElementById('txEditedPhone'); var editedAddress =document.getElementById('txEditedAddress'); var hfEditedId = document.getElementById('hfEditedId'); var hfActionMode = document.getElementById('hfActionMode'); // Blank validation check if (BlankValidation(editedName, editedEmail, editedPhone, editedAddress) == true) { // call ajax JSON method $.ajax({ type: "POST", url: "hr_webservice.asmx/EditEmployeeData", data: "{ 'employeeId': '" + hfEditedId.value + "','editedName': '" + editedName.value + "', 'editedEmail': '" + editedEmail.value + "','editedPhone':'" + editedPhone.value + "','editedAddress':'" + editedAddress.value + "','action_mode':'" + hfActionMode.value + "'}", contentType: "application/json; charset=utf-8", dataType: "json", success: function (response) { BindTable(response.d); }, failure: function (msg) { alert(msg); } }); ResetText(); } }
并且 Delete 方法也会在生成的图像按钮的 onclick 事件中被调用。
// // This is delete mode with JSON, When the button fire the onclick event the // method call with the hidden primary key. // actually the hidden field set on create the row. // function DeleteMode(deleteEmpId) { var agree = confirm("Are you sure you want to delete this information ?"); if (agree) { $.ajax({ type: "POST", url: "hr_webservice.asmx/DeleteEmployeeData", data: "{ 'employeeId': '" + deleteEmpId + "'}", contentType: "application/json; charset=utf-8", dataType: "json", success: function (response) { BindTable(response.d); }, failure: function (msg) { alert(msg); } }); } }
当 JSON 调用 Web 服务中的编辑方法时,Web 服务方法会被触发,并且该方法直接与数据库进行操作。
// // Edit Employee Data // [WebMethod] public List<Employee> EditEmployeeData(string employeeId, string editedName, string editedEmail, string editedPhone, string editedAddress, string action_mode) { // getting connection string string conStr = WebConfigurationManager.ConnectionStrings["TESTDB"].ConnectionString; int rowsInserted = 0; // Creating Sql Connection using (SqlConnection conn = new SqlConnection(conStr)) { SqlCommand cmd = new SqlCommand(); cmd.Connection = conn; cmd.CommandText = "sp_sample_info"; cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add("@id", employeeId); cmd.Parameters.Add("@Name", editedName); cmd.Parameters.Add("@Email", editedEmail); cmd.Parameters.Add("@Phone", editedPhone); cmd.Parameters.Add("@Address", editedAddress); cmd.Parameters.Add("@Action", action_mode); conn.Open(); rowsInserted = cmd.ExecuteNonQuery(); conn.Close(); cmd = null; } list = GetEmployeeDetails(); return list; }
删除操作以相同的方式完成。
客户端数据网格的示例输出
重要性/局限性
重要性
- 当将 JSON 与 Ajax 结合使用时,Web 应用程序可以仅请求需要更新的网格命令(插入/更新/删除),从而大大减少带宽使用,尤其缩短加载时间。
- Web 应用程序将运行得更快或响应更灵敏,即使应用程序在服务器端没有更改。
- Ajax 能够减少与服务器的连接,因为脚本和样式表只需请求一次。
- 状态可以在整个 Web 站点中保持,例如 JavaScript 变量。
任何不支持 Ajax 或 JavaScript 的浏览器用户,或者仅仅禁用了 JavaScript 的用户,将无法使用其功能。
历史
2012 年 11 月 25 日