前言:上篇介绍了下ko增删改查的封装,确实节省了大量的js代码。博主是一个喜欢偷懒的人,总觉得这些基础的增删改查效果能不能通过一个什么工具直接生成页面效果,啥代码都不用写了,那该多爽。于是研究了下T4的语法,虽然没有完全掌握,但是算是有了一个大致的了解,给需要自定义模板的园友们提供一个参考。于是乎有了今天的这篇文章:通过T4模板快速生成页面。
KnockoutJS系列文章:
- JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(一)
- JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(二)
- JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(三):两个Viewmodel搞定增删改查
- JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(四):自定义T4模板快速生成页面
本文原创地址:http://www.cnblogs.com/landeanfen/p/5667022.html
我们知道,MVC里面在添加视图的时候可以自动生成增删改查的页面效果,那是因为MVC为我们内置了基础增删改查的模板,这些模板的语法就是使用T4,那么这些模板在哪里呢?找了下相关文章,发现MVC4及以下的版本模板位置和MVC5及以上模板的位置有很大的不同。
- MVC4及以下版本的模板位置:VS的安装目录+\ItemTemplates\CSharp\Web\MVC 2\CodeTemplates。比如博主的D:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\ItemTemplates\CSharp\Web\MVC 4\CodeTemplates。找到cshtml对应的模板,里面就有相应的增删改查的tt文件
- MVC5及以上版本的模板位置:直接给出博主的模板位置D:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\Extensions\Microsoft\Web\Mvc\Scaffolding\Templates
知道了这个,那么接下来就是改造模板,添加自己的生成内容了。可以直接将List和Edit模板拷贝到过来自行改造,但是最后想了想,还是别动MVC内置的东西了,我们自己来建自己的模板不是更好。
在当前Web项目的根目录下面新建一个文件夹,命名为CodeTemplates,然后将MVC模板里面的MvcControllerEmpty和MvcView两个模板文件夹拷贝到CodeTemplates文件夹下面,去掉它里面的原始模板,然后新建几个自己的模板,如下图:
这样我们在添加新的控制器和新建视图的时候就可以看到我们自定义的模板了:
二、T4代码介绍
上面介绍了如何新建自己的模板,模板建好之后就要开始往里面塞相应的内容了,如果T4的语法展开了说,那一篇是说不完的,有兴趣的园友可以去园子里找找,文章还是挺多的。这里主要还是来看看几个模板内容。还有一点需要说明下,貌似从MVC5之后,T4的模板文件后缀全部改成了t4,而之前的模板一直是tt结尾的,没有细究它们语法的区别,估计应该差别不大。
1、Controller.cs.t4
为什么要重写这个空的控制器模板呢?博主觉得增删改查的好多方法都需要手动去写好麻烦,写一个模板直接生成可以省事很多。来看看模板里面的实现代码:
<#@ template language="C#" HostSpecific="True" #> <#@ output extension="cs" #> <#@ parameter type="System.String" name="ControllerName" #> <#@ parameter type="System.String" name="ControllerRootName" #> <#@ parameter type="System.String" name="Namespace" #> <#@ parameter type="System.String" name="AreaName" #> <# var index = ControllerName.LastIndexOf("Controller"); var ModelName = ControllerName.Substring(0, index); #> using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using TestKO.Models; namespace <#= Namespace #> { public class <#= ControllerName #> : Controller { public ActionResult Index() { return View(); } public ActionResult Edit(<#= ModelName #> model) { return View(model); } [HttpGet] public JsonResult Get(int limit, int offset) { return Json(new { }, JsonRequestBehavior.AllowGet); } //新增实体 [HttpPost] public JsonResult Add(<#= ModelName #> oData) { <#= ModelName #>Model.Add(oData); return Json(new { }, JsonRequestBehavior.AllowGet); } //更新实体 [HttpPost] public JsonResult Update(<#= ModelName #> oData) { <#= ModelName #>Model.Update(oData); return Json(new { }, JsonRequestBehavior.AllowGet); } //删除实体 [HttpPost] public JsonResult Delete(List<<#= ModelName #>> oData) { <#= ModelName #>Model.Delete(oData); return Json(new { }, JsonRequestBehavior.AllowGet); } } }
这个内容不难理解,直接查看生成的控制器代码:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using TestKO.Models; namespace TestKO.Controllers { public class UserController : Controller { public ActionResult Index() { return View(); } public ActionResult Edit(User model) { return View(model); } [HttpGet] public JsonResult Get(int limit, int offset) { return Json(new { }, JsonRequestBehavior.AllowGet); } //新增实体 [HttpPost] public JsonResult Add(User oData) { UserModel.Add(oData); return Json(new { }, JsonRequestBehavior.AllowGet); } //更新实体 [HttpPost] public JsonResult Update(User oData) { UserModel.Update(oData); return Json(new { }, JsonRequestBehavior.AllowGet); } //删除实体 [HttpPost] public JsonResult Delete(List<User> oData) { UserModel.Delete(oData); return Json(new { }, JsonRequestBehavior.AllowGet); } } }
2、KoIndex.cs.t4
这个模板主要用于生成列表页面,大致代码如下:
<#@ template language="C#" HostSpecific="True" #> <#@ output extension=".cshtml" #> <#@ include file="Imports.include.t4" #> <# // The following chained if-statement outputs the file header code and markup for a partial view, a view using a layout page, or a regular view. if(IsPartialView) { #> <# } else if(IsLayoutPageSelected) { #> @{ ViewBag.Title = "<#= ViewName#>"; <# if (!String.IsNullOrEmpty(LayoutPageFile)) { #> Layout = "<#= LayoutPageFile#>"; <# } #> } <# } else { #> @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title><#= ViewName #></title> <link href="~/Content/bootstrap/css/bootstrap.min.css" rel="stylesheet" /> <link href="~/Content/bootstrap-table/bootstrap-table.min.css" rel="stylesheet" /> <script src="~/scripts/jquery-1.9.1.min.js"></script> <script src="~/Content/bootstrap/js/bootstrap.min.js"></script> <script src="~/Content/bootstrap-table/bootstrap-table.min.js"></script> <script src="~/Content/bootstrap-table/locale/bootstrap-table-zh-CN.js"></script> <script src="~/scripts/knockout/knockout-3.4.0.min.js"></script> <script src="~/scripts/knockout/extensions/knockout.mapping-latest.js"></script> <script src="~/scripts/extensions/knockout.index.js"></script> <script src="~/scripts/extensions/knockout.bootstraptable.js"></script> <script type="text/javascript"> $(function () { var viewModel = { bindId: "div_index", tableParams : { url : "/<#=ViewDataTypeShortName#>/Get", pageSize : 2, }, urls : { del : "/<#=ViewDataTypeShortName#>/Delete", edit : "/<#=ViewDataTypeShortName#>/Edit", add : "/<#=ViewDataTypeShortName#>/Edit", }, queryCondition : { } }; ko.bindingViewModel(viewModel); }); </script> </head> <body> <# PushIndent(" "); } #> <div id="toolbar"> <button data-bind="click:addClick" type="button" class="btn btn-default"> <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>新增 </button> <button data-bind="click:editClick" type="button" class="btn btn-default"> <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>修改 </button> <button data-bind="click:deleteClick" type="button" class="btn btn-default"> <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>删除 </button> </div> <table data-bind="bootstrapTable:bootstrapTable"> <thead> <tr> <th data-checkbox="true"></th> <# IEnumerable<PropertyMetadata> properties = ModelMetadata.Properties; foreach (PropertyMetadata property in properties) { if (property.Scaffold && !property.IsPrimaryKey && !property.IsForeignKey) { #> <th data-field="<#= GetValueExpression(property) #>"><#= GetValueExpression(property) #></th> <# } }#> </tr> </thead> </table> <# // The following code closes the tag used in the case of a view using a layout page and the body and html tags in the case of a regular view page #> <# if(!IsPartialView && !IsLayoutPageSelected) { ClearIndent(); #> </body> </html> <# } #> <#@ include file="ModelMetadataFunctions.cs.include.t4" #>
添加一个视图Index,然后选择这个模板
得到的页面内容
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> <link href="~/Content/bootstrap/css/bootstrap.min.css" rel="stylesheet" /> <link href="~/Content/bootstrap-table/bootstrap-table.min.css" rel="stylesheet" /> <script src="~/scripts/jquery-1.9.1.min.js"></script> <script src="~/Content/bootstrap/js/bootstrap.min.js"></script> <script src="~/Content/bootstrap-table/bootstrap-table.min.js"></script> <script src="~/Content/bootstrap-table/locale/bootstrap-table-zh-CN.js"></script> <script src="~/scripts/knockout/knockout-3.4.0.min.js"></script> <script src="~/scripts/knockout/extensions/knockout.mapping-latest.js"></script> <script src="~/scripts/extensions/knockout.index.js"></script> <script src="~/scripts/extensions/knockout.bootstraptable.js"></script> <script type="text/javascript"> $(function () { var viewModel = { bindId: "div_index", tableParams : { url : "/User/Get", pageSize : 2, }, urls : { del : "/User/Delete", edit : "/User/Edit", add : "/User/Edit", }, queryCondition : { } }; ko.bindingViewModel(viewModel); }); </script> </head> <body> <div id="toolbar"> <button data-bind="click:addClick" type="button" class="btn btn-default"> <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>新增 </button> <button data-bind="click:editClick" type="button" class="btn btn-default"> <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>修改 </button> <button data-bind="click:deleteClick" type="button" class="btn btn-default"> <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>删除 </button> </div> <table data-bind="bootstrapTable:bootstrapTable"> <thead> <tr> <th data-checkbox="true"></th> <th data-field="Name">Name</th> <th data-field="FullName">FullName</th> <th data-field="Age">Age</th> <th data-field="Des">Des</th> <th data-field="Createtime">Createtime</th> <th data-field="strCreatetime">strCreatetime</th> </tr> </thead> </table> </body> </html>