在ASP.NET MVC4中实现同页面增删改查,无弹出框02,增删改查界面设计
在上一篇"在ASP.NET MVC4中实现同页面增删改查,无弹出框01,Repository的搭建"中,已经搭建好了Repository层,本篇就剩下增删改查的界面了......今天的阳光真特么好,写完本篇,好出去在阳光下溜溜狗、散散步什么的,正所谓文武之道一张一弛,走神了,进入正题。
首先是一个View Model,在这里定义验证规则,提交和保存数据的时候还必须和领域模型映射。
using System;using System.ComponentModel.DataAnnotations;namespace MvcApplication3.Models{public class ProductVm{public int Id { get; set; }[Required(ErrorMessage = "必填")][Display(Name = "名称")][StringLength(6, ErrorMessage = "最大长度6位")]public string Name { get; set; }[Required(ErrorMessage = "必填")][Display(Name = "分类")][StringLength(6, ErrorMessage = "最大长度6位")]public string Category { get; set; }[Required(ErrorMessage = "必填")][Display(Name = "价格")][Range(typeof(Decimal), "0", "9999", ErrorMessage = "{0} 必须是数字介于 {1} 和 {2}之间.")]public decimal Price { get; set; }}}
创建HomeController
using System.Linq;using System.Web.Mvc;using MvcApplication3.Models;namespace MvcApplication3.Controllers{public class ProductController : Controller{static readonly IProductRepository repository = new ProductRepository();#region 显示查询/// <summary>/// 显示主界面/// </summary>/// <returns></returns>public ActionResult Index(){return View();}private string _name = string.Empty; //用来接收查询中有关Name的值private string _category = string.Empty;//用来接收查询中有关Category的值/// <summary>/// 根据查询参数获取所有产品,以json返回/// </summary>/// <returns></returns>public ActionResult GetProdutJson(){//page和rows是datagrid传递的默认参数int pageIndex = int.Parse(Request["page"]);int pageSize = int.Parse(Request["rows"]);//如果查询中包括Nameif (!string.IsNullOrEmpty(Request["name"])){_name = Request["name"];}//如果查询中包括Categoryif (!string.IsNullOrEmpty(Request["category"])){_category = Request["category"];}//初始化查询实例var queryParams = new ProductParam{PageIndex = pageIndex,PageSize = pageSize,Name = _name,Category = _category};//根据查询实例查找对应的数据int totalNum = 0;var products = repository.LoadProductPageData(queryParams, out totalNum);//投影出需要传递给datagrid的数据var result = from p in productsselect new {p.Id, p.Name, p.Category, p.Price};//datagrid所需要的格式{total:10, rows=[]}var jsonResult = new {total = totalNum, rows = result};return Json(jsonResult, JsonRequestBehavior.AllowGet);}#endregion#region 添加/// <summary>/// 添加显示,返回一个强类型部分视图/// </summary>/// <returns></returns>public ActionResult AddProduct(){return PartialView("_AddProduct", new ProductVm());}/// <summary>/// 添加提交/// </summary>/// <returns></returns>[HttpPost][ValidateAntiForgeryToken]public ActionResult AddProduct(ProductVm productVm){if (ModelState.IsValid){Product dbProduct = new Product();dbProduct.Name = productVm.Name;dbProduct.Category = productVm.Category;dbProduct.Price = productVm.Price;repository.Add(dbProduct);return Json(new { msg = true });}else{return PartialView("_AddProduct", new ProductVm());}}#endregion#region 修改/// <summary>/// 修改显示/// </summary>/// <returns></returns>public ActionResult EditProduct(int id){var dbProduct = repository.GetById(id);ProductVm productVm = new ProductVm();productVm.Id = dbProduct.Id;productVm.Name = dbProduct.Name;productVm.Category = dbProduct.Category;productVm.Price = dbProduct.Price;return PartialView("_EditProduct", productVm);}/// <summary>/// 修改提交/// </summary>/// <param name="productVm"></param>/// <returns></returns>[HttpPost][ValidateAntiForgeryToken]public ActionResult EditProduct(ProductVm productVm){if (ModelState.IsValid){var dbProduct = repository.GetById(productVm.Id);dbProduct.Name = productVm.Name;dbProduct.Category = productVm.Category;dbProduct.Price = productVm.Price;repository.Update(dbProduct);return Json(new { msg = true });}else{return PartialView("_EditProduct", productVm);}}#endregion#region 删除/// <summary>/// 删除/// </summary>/// <returns></returns>[HttpPost]public ActionResult DeleteProducts(){//获取前台传来的Id字符串var strIds = Request["ids"];//去除最后一位分隔符strIds = strIds.Substring(0, strIds.Length - 1);//把字符串转换成数组string[] ids = strIds.Split('_');repository.DeleteBatch(ids);return Json(new { msg = true });}#endregion}}
在前台页面会使用jQuery EasyUI的datagrid显示数据和分页,而datagrid在向服务端发送异步请求的时候默认带了page和rows这2个参数。GetProdutJson方法用来获取查询、分页后的json格式。在前台,当页面初次加载的时候,不带任何查询数据,服务端只要接收page和rows这2个参数,把它们封装成一个类。
namespace MvcApplication3.Models{public class PageParam{public int PageSize { get; set; }public int PageIndex { get; set; }}}
而当在前台输入搜索条件的时候,搜索条件参数以及page和rows这2个参数都传值给了服务端,为此我们再封装一个派生于PageParam的类。
namespace MvcApplication3.Models{public class ProductParam : PageParam{public string Name { get; set; }public string Category { get; set; }}}
所以,在GetProdutJson方法内,最终是把从前台接收到的分页参数或查询参数,实例化成ProductParam的一个实例,再把该实例作为实参传值给Repository的LoadProductPageData方法。
主界面Home/Index.cshtml
进入主界面Home/Index.cshtml之前,先设置_Layout.cshtml。
<!DOCTYPE html><html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width" /><title>@ViewBag.Title</title>@Styles.Render("~/Content/css")<link href="~/bootstrap/css/bootstrap.min.css" rel="stylesheet" /><link href="~/Content/themes/icon.css" rel="stylesheet" /><link href="~/Content/themes/gray/easyui.css" rel="stylesheet" />@RenderSection("styles", required:false)@Scripts.Render("~/bundles/modernizr")@Scripts.Render("~/bundles/jquery")@Scripts.Render("~/bundles/jqueryui")@Scripts.Render("~/bundles/jqueryval")<script src="~/bootstrap/js/bootstrap.min.js"></script></head><body>@RenderBody()@RenderSection("scripts", required: false)</body></html>
用到了bootstrap, jQuery EasyUI, jQuery等相关的css,js文件,该有的要有,顺序要放对。
Product/Index.cshtml需要呈现的包括:
○ 添加区域:放在一个div中,div中再放一个iframe,需要的时候通过iframe链接到添加页面
○ 修改区域:放在一个div中,div中再放一个iframe,需要的时候通过iframe链接到修改页面
○ 搜索区域:使用bootstrap显示元素
○ 列表区域:使用datagrid显示,支持分页
Product/Index.cshtml需要承担的工作包括:
○ 页面加载的时候隐藏添加和修改区域
○ 限制搜索区域有关名称和类别的长度
○ 页面加载显示全部数据列表
○ 点击搜索按钮显示符合搜素条件的数据列表
○ 点击清空按钮让搜索条件清空
○ datagrid添加:让添加区域内的iframe指向ProductController的AddProduct方法,渲染强类型添加视图
○ datagrid修改:让修改区域内的iframe指向ProductController的EditProduct方法,渲染强类型修改视图,并传递当前Product的Id
○ datagrid删除:支持批量删除,把勾选行所在的Id拼接成"1_2_"的形式传递给PrductContrlller的DeleteProduct方法,返回json
○ 提供给子视图页面调用的添加成功、添加取消、修改成功、修改取消方法,Home/Index.cshtml为父页面,添加和修改区域内的iframe指向的视图页面是子页面
@{ViewBag.Title = "Index";Layout = "~/Views/Shared/_Layout.cshtml";}<!--添加开始--><div class="addContent" id="addContent"><iframe id="frameAdd" src="" scrolling="yes" frameborder="0" height="100%" width="100%" onload="this.height=this.contentWindow.document.documentElement.scrollHeight"></iframe></div><!--添加结束--><!--编辑开始--><div class="editContent" id="editContent"><iframe id="frameEdit" src="" scrolling="yes" frameborder="0" height="100%" width="100%" onload="this.height=this.contentWindow.document.documentElement.scrollHeight"></iframe></div><!--编辑结束--><!--搜索开始--><div id="tb" style="padding: 3px"><form class="form-inline" role="form"><div class="form-group"><label class="sr-only" for="name">名称</label><input type="text" class="form-control" name="name" id="name" placeholder="输入名称" maxlength="6" /></div><div class="form-group"><label class="sr-only" for="category">分类</label><input type="text" class="form-control" name="category" id="category" placeholder="输入分类" maxlength="6" /></div><input type="button" id="btnSearch" class="btn btn-primary" value="搜索" /> <input type="button" id="emptySearch" class="btn btn-default" value="清空" /></form></div><!--搜索结束—><!--表格开始--><div class="ctable" id="ctable"><table id="tt"></table></div><!--表格结束-->@section scripts{<script src="~/Scripts/jquery.easyui.min.js"></script><script src="~/Scripts/easyui-lang-zh_CN.js"></script><script type="text/javascript">$(function() {//隐藏元素initialHide();//限制搜索条件中名称和分类的长度limitInputLength($('#name'));limitInputLength($('#category'));//显示列表initData();//搜索$('#tb').on("click", "#btnSearch", function () {initData(initQuery());});//清空搜索$('#emptySearch').on("click", function () {emptySearch();});});//显示列表function initData(params) {$('#tt').datagrid({url: '@Url.Action("GetProdutJson", "Product")',height: 360,width: 900,fitColumns: false,nowrap: true,showFooter: true,idField: 'ID',loadMsg: '正在加载信息...',pagination: true,singleSelect: false,queryParams: params,pageSize: 10,pageNumber: 1,pageList: [10, 20, 30],columns: [//p.Id, p.Name, p.Category, p.Price[{ field: 'ck', checkbox: true, align: 'center', width: 30 },{ field: 'Id', title: '编号', align: 'center' },{ field: 'Name', title: '名称', align: 'center' },{ field: 'Price', title: '价格', align: 'center' },{ field: 'Category', title: '分类', align: 'center' }]],toolbar: [{id: 'btnAdd',text: '添加',iconCls: 'icon-add',handler: function () {showAdd();}}, '-', {id: 'btnUpdate',text: '修改',iconCls: 'icon-edit',handler: function () {var rows = $('#tt').datagrid("getSelections");if (rows.length != 1) {$.messager.alert("提示", "只能选择一行进行编辑");return;}showEdit(rows[0].Id);}}, '-', {id: 'btnDelete',text: '删除',iconCls: 'icon-remove',handler: function () {var rows = $('#tt').datagrid("getSelections");if (rows.length < 1) {$.messager.alert("提示", "请选一行删除");return;}$.messager.confirm("提示信息", "确定要删除吗?", function (r) {if (r) {var strIds = "";for (var i = 0; i < rows.length; i++) {strIds += rows[i].Id + '_'; //1_2_3}$.post("@Url.Action("DeleteProducts", "Product")", { ids: strIds }, function (data) {if (data.msg) {$.messager.alert("提示", "删除成功");initData();$('#tt').datagrid("clearSelections");}});}});}}],OnBeforeLoad: function (param) {return true;}});}//添加function showAdd() {$("#frameAdd").attr("src", "@Url.Action("AddProduct", "Product")");$("#addContent").css("display", "block");}//修改function showEdit(productId) {var url = "/Product/EditProduct?id=" + productId;$("#frameEdit").attr("src", url);$('.addContent').css("display", "none");$("#editContent").css("display", "block");}//隐藏元素function initialHide() {$('.addContent').css("display", "none");$('.editContent').css("display", "none");}//限制文本框长度function limitInputLength(_input) {var _max = _input.attr('maxlength');_input.bind('keyup change', function () {if ($(this).val().length > _max) {($(this).val($(this).val().substring(0, _max)));}});}//子窗口添加成功后调用function refreshAfterAdd() {initData();$('#tt').datagrid("clearSelections");$("#addContent").css("display", "none");}//子窗口取消添加调用function cancelAdd() {$('.addContent').css("display", "none");$('#tt').datagrid("clearSelections");}//子窗口修改成功后调用function refreshAfterEdit() {initData();$('#tt').datagrid("clearSelections");$("#editContent").css("display", "none");}//子窗口取消修改调用function candelEdit() {$("#editContent").css("display", "none");$('#tt').datagrid("clearSelections");}//获取查询表单的值组成jsonfunction initQuery() {var queryParams = {name: $('#name').val(),category: $('#category').val()};return queryParams;}//清空搜索条件function emptySearch() {$('#name').val("");$('#category').val("");}</script>}
添加强类型视图页Product/_AddProduct.cshtml
是一个针对ProductVm强类型视图。添加成功或取消都会调用父视图提供的方法。
@model MvcApplication3.Models.ProductVm@{ViewBag.Title = "_AddProduct";Layout = "~/Views/Shared/_Layout.cshtml";}@section styles{<style type="text/css">.errormsg {color: red;}.vcenter {display: table-cell;vertical-align: middle;float: none;border: 0px solid red;height: 38px;}.bk {background-color: #F8F8F8;padding: 2px;border-radius: 5px;}form {width: 900px;/*height: 450px;*/}.control-label {position: relative;top: 5px;}.col-sm-6 {height: 40px;}.col-sm-2 {height: 40px;}.col-sm-4 {height: 40px;}#wrapper {width: 550px;clear: both;float: left;margin-top: 10px;}</style>}<div id="wrapper">@using (Html.BeginForm("AddProduct", "Product", FormMethod.Post, new { id = "addForm", @class = "form-horizontal", role = "form" })){@Html.AntiForgeryToken()<div class="form-group">@Html.LabelFor(s => s.Name,new { @class="col-sm-2 control-label" })<div class="col-sm-6">@Html.TextBoxFor(s => s.Name,new { @class="form-control input-sm"}) </div><div class="col-sm-4 vcenter">@Html.ValidationMessageFor(s => s.Name)</div></div><div class="form-group bk">@Html.LabelFor(s => s.Price,new { @class="col-sm-2 control-label" })<div class="col-sm-6">@Html.TextBoxFor(s => s.Price,new { @class="form-control input-sm"}) </div><div class="col-sm-4 vcenter">@Html.ValidationMessageFor(s => s.Price)</div></div><div class="form-group">@Html.LabelFor(s => s.Category,new { @class="col-sm-2 control-label" })<div class="col-sm-6">@Html.TextBoxFor(s => s.Category,new { @class="form-control input-sm"}) </div><div class="col-sm-4 vcenter">@Html.ValidationMessageFor(s => s.Category)</div></div><div class="form-group bk"><div style="text-align: center;" class="col-sm-6 col-sm-offset-2"><input type="button" id="up" class="btn btn-primary" value="添加"/> <input type="button" id="cancel" class="btn btn-default" value="取消"/></div></div>}</div>@section scripts{<script type="text/javascript">$(function() {//提交$('#up').on('click', function () {if ($('#addForm').valid()) {$.ajax({cache: false,url: '@Url.Action("AddProduct","Product")',type: 'POST',dataType: 'json',data: $('#addForm').serialize(),success: function (data) {if (data.msg) {$('input[type=text]').val("");self.parent.refreshAfterAdd();}},error: function (xhr, status) {alert("添加失败,状态码:" + status);}});}});//点击取消按钮关闭窗口$('#cancel').on('click', function () {$('input[type=text]').val("");self.parent.cancelAdd();});});</script>}
如果添加验证失败,客户端适时报错:
添加验证成功,添加区域隐藏,列表多了新添加的数据:
修改强类型视图页Product/_EditProduct.cshtml
是一个针对ProductVm强类型视图。修改成功或取消都会调用父视图提供的方法。
@model MvcApplication3.Models.ProductVm@{ViewBag.Title = "_EditProduct";Layout = "~/Views/Shared/_Layout.cshtml";}@section styles{<style type="text/css">.errormsg {color: red;}.vcenter {display: table-cell;vertical-align: middle;float: none;border: 0px solid red;height: 38px;}.bk {background-color: #F8F8F8;padding: 2px;border-radius: 5px;}form {width: 900px;/*height: 450px;*/}.control-label {position: relative;top: 5px;}.col-sm-6 {height: 40px;}.col-sm-2 {height: 40px;}.col-sm-4 {height: 40px;}#wrapper {width: 550px;clear: both;float: left;margin-top: 10px;}</style>}<div id="wrapper">@using (Html.BeginForm("EditProduct", "Product", FormMethod.Post, new { id = "editForm", @class = "form-horizontal", role = "form" })){@Html.AntiForgeryToken()@Html.HiddenFor(s => s.Id)<div class="form-group">@Html.LabelFor(s => s.Name,new { @class="col-sm-2 control-label" })<div class="col-sm-6">@Html.TextBoxFor(s => s.Name,new { @class="form-control input-sm"}) </div><div class="col-sm-4 vcenter">@Html.ValidationMessageFor(s => s.Name)</div></div><div class="form-group bk">@Html.LabelFor(s => s.Price,new { @class="col-sm-2 control-label" })<div class="col-sm-6">@Html.TextBoxFor(s => s.Price,new { @class="form-control input-sm"}) </div><div class="col-sm-4 vcenter">@Html.ValidationMessageFor(s => s.Price)</div></div><div class="form-group">@Html.LabelFor(s => s.Category,new { @class="col-sm-2 control-label" })<div class="col-sm-6">@Html.TextBoxFor(s => s.Category,new { @class="form-control input-sm"}) </div><div class="col-sm-4 vcenter">@Html.ValidationMessageFor(s => s.Category)</div></div><div class="form-group bk"><div style="text-align: center;" class="col-sm-6 col-sm-offset-2"><input type="button" id="up" class="btn btn-primary" value="修改"/> <input type="button" id="cancel" class="btn btn-default" value="取消"/></div></div>}</div>@section scripts{<script type="text/javascript">$(function() {//提交$('#up').on('click', function () {if ($('#editForm').valid()) {$.ajax({cache: false,url: '@Url.Action("EditProduct","Product")',type: 'POST',dataType: 'json',data: $('#editForm').serialize(),success: function (data) {if (data.msg) {self.parent.refreshAfterEdit();}},error: function (xhr, status) {alert("修改失败,状态码:" + status);}});}});//点击取消按钮关闭窗口$('#cancel').on('click', function () {self.parent.candelEdit();});});</script>}
如果修改验证失败,客户端适时报错:
修改验证成功,修改区域隐藏,取消勾选,列表中是修改的数据:
在ASP.NET MVC4中实现同页面增删改查,无弹出框02,增删改查界面设计的更多相关文章
- 在ASP.NET MVC4中实现同页面增删改查,无弹出框01,Repository的搭建
通常,在同一个页面上实现增删改查,会通过弹出框实现异步的添加和修改,这很好.但有些时候,是不希望在页面上弹出框的,我们可能会想到Knockoutjs,它能以MVVM模式实现同一个页面上的增删改查,再辅 ...
- asp.net mvc4中自定义404页面
原文地址:http://www.chuchur.com/asp-net-mvc4-404/ 定义404 方法当然有很多种.不同的方法所展现的形式也不一样,用户所体验也不一样.以下提供2两种 方法一: ...
- asp.net在应用母版的页面下采用了ModalPopupExtender弹出窗中应用autocomplete
autocomplete是jqueryUI的一个插件,可以实现自动填充的功能. 要点:1.应用了母版页,所以取页面上控件的ID时与一般方法不同 2.由于用了ajax的updatepanel,所以会出现 ...
- [js]uploadify结合jqueryUI弹出框上传,js中的冒出的bug,又被ie坑了
引言 最近在一个项目中,在用户列表中需要对给没有签名样本的个别用户上传签名的样本,就想到博客园中上传图片使用弹出框方式,博客园具体怎么实现的不知道,只是如果自己来弄,想到两个插件的结合使用,在弹出框中 ...
- ASP.NET MVC4中的bundles特性引发服务器拒绝访问(403错误)
在ASP.NET MVC4中微软引入了bundles特性,这个特性可以将服务器端的多个Javascript或多个css文件捆绑在一起作为一个单一的URL地址供客户端浏览器调用,从而减少了页面上Http ...
- ASP.NET MVC4中使用NHibernate
ASP.NET MVC4中使用NHibernate 1:下载安装NHibernate 打开 VS 2012新建一个 MVC4项目. 在项目名称上右击选择Manage NuGet Packages.你会 ...
- C#面试题(转载) SQL Server 数据库基础笔记分享(下) SQL Server 数据库基础笔记分享(上) Asp.Net MVC4中的全局过滤器 C#语法——泛型的多种应用
C#面试题(转载) 原文地址:100道C#面试题(.net开发人员必备) https://blog.csdn.net/u013519551/article/details/51220841 1. . ...
- ASP.NET MVC4中的App_start中BundleConfig的介绍使用
在BundleConfig.cs中,指定CSS和JS,主要用来压缩JS和CSS 在ASP.NET MVC4中(在WebForm中应该也有),有一个叫做Bundle的东西,它用来将js和css进行压 ...
- Asp.Net MVC4开发二: Entity Framework在Asp.Net MVC4中的应用
ORM作为一种数据库訪问机制已广泛地应用于各种项目其中,在.Net开发中,应用比較广泛的ORM框架大致有以下几个: 官方支持的有:Linq to SQL.Entity Framework.三方的有:N ...
随机推荐
- python报错IndexError: list index out of range
今天写个ping vpn的python脚本,报错IndexError: list index out of range 最后查看是python读取文件中出现空格 去掉空格即可
- grep和sed匹配多个字符关键字的用法
GNU sed和UNIX sed 写法不一样 匹配多个关键词,打印出匹配的行,效果类似于 grep grep hello\|world file > output 或者用扩展正则 grep -E ...
- C#: +(特性 ) + Attitude C#(类)前面或者(方法)前面 (中括号)定义
首先要说的是,可能一些刚接触C#的朋友常常容易把属性(Property)跟特性(Attribute)弄混淆,其实这是两种不同的东西.属性就是面向对象思想里所说的封装在类里面的数据字段,其形式为: 1: ...
- [转]如何取得当前正在执行的shell脚本的绝对路径?
来源:http://sexywp.com/bash-how-to-get-the-basepath-of-current-running-script.htm 如题,一般我们写Shell脚本的时候,都 ...
- vector的reserve和resize(转)
转自:http://www.cnblogs.com/qlee/archive/2011/05/16/2048026.html vector 的reserve增加了vector的capacity,但是它 ...
- LeetCode(17):电话号码的字母组合
Medium! 题目描述: 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合. 给出数字到字母的映射如下(与电话按键相同).注意 1 不对应任何字母. 示例: 输入:"23& ...
- 2019寒假练题计划——LibreOJ刷题计划 &《信息学奥赛一本通》提高版题目
目录 2019.1.27 #10082. 「一本通 3.3 例 1」Word Rings 题意 思路 #10083. 「一本通 3.3 例 2」双调路径 题意 思路 #10084. 「一本通 3.3 ...
- Action的模型绑定
- 你真的会用Action的模型绑定吗? 在QQ群或者一些程序的交流平台,经常会有人问:我怎么传一个数组在Action中接收.我传的数组为什么Action的model中接收不到.或者我在ajax的 ...
- 基于nopCommerce的开发框架(附源码)
.NET的开发人员应该都知道这个大名鼎鼎的高质量b2c开源项目-nopCommerce,基于EntityFramework和MVC开发,拥有透明且结构良好的解决方案,同时结合了开源和商业软件的最佳特性 ...
- jquery下载,实时更新jquery1.2到最新3.3.1所有版本下载
描述:jquery下载,实时更新jquery1.2到最新3.3.1所有版本下载 https://www.jb51.net/zt/jquerydown.htm (注意:jquery-2.0以上版本不再支 ...