BootstrapTable+KnockoutJS自定义T4模板快速生成增删改查页面


Posted in Javascript onAugust 01, 2016

前言:上篇介绍了下ko增删改查的封装,确实节省了大量的js代码。博主是一个喜欢偷懒的人,总觉得这些基础的增删改查效果能不能通过一个什么工具直接生成页面效果,啥代码都不用写了,那该多爽。于是研究了下T4的语法,虽然没有完全掌握,但是算是有了一个大致的了解。于是乎有了今天的这篇文章:通过T4模板快速生成页面。

KnockoutJS系列文章:

一、T4的使用介绍

我们知道,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文件

BootstrapTable+KnockoutJS自定义T4模板快速生成增删改查页面

•MVC5及以上版本的模板位置:直接给出博主的模板位置D:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\Extensions\Microsoft\Web\Mvc\Scaffolding\Templates

BootstrapTable+KnockoutJS自定义T4模板快速生成增删改查页面

知道了这个,那么接下来就是改造模板,添加自己的生成内容了。可以直接将List和Edit模板拷贝到过来自行改造,但是最后想了想,还是别动MVC内置的东西了,我们自己来建自己的模板不是更好。

在当前Web项目的根目录下面新建一个文件夹,命名为CodeTemplates,然后将MVC模板里面的MvcControllerEmpty和MvcView两个模板文件夹拷贝到CodeTemplates文件夹下面,去掉它里面的原始模板,然后新建几个自己的模板,如下图:

BootstrapTable+KnockoutJS自定义T4模板快速生成增删改查页面

这样我们在添加新的控制器和新建视图的时候就可以看到我们自定义的模板了:

BootstrapTable+KnockoutJS自定义T4模板快速生成增删改查页面

二、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,然后选择这个模板

BootstrapTable+KnockoutJS自定义T4模板快速生成增删改查页面

得到的页面内容

@{
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>
Index.cshtml

我们将上篇说的viewmodel搬到页面上面来了,这样每次就不用从controller里面传过来了。稍微改一下表格的列名,页面就可以跑起来了。

这里有待优化的几点:

(1)查询条件没有生成,如果将T4的语法研究深一点,可以在需要查询的字段上面添加特性标识哪些字段需要查询,然后自动生成对应的查询条件。

(2)表格的列名似乎也可以通过属性的字段特性来生成。这点和第一点类似,都需要研究T4的语法。

3、KoEdit.cs.t4

第三个模板页就是编辑的模板了,它的大致代码如下:

<#@ template language="C#" HostSpecific="True" #>
<#@ output extension=".cshtml" #>
<#@ include file="Imports.include.t4" #>
@model <#= ViewDataTypeName #>
<#
// "form-control" attribute is only supported for all EditorFor() in System.Web.Mvc 5.1.0.0 or later versions, except for checkbox, which uses a div in Bootstrap
string boolType = "System.Boolean";
Version requiredMvcVersion = new Version("5.1.0.0");
bool isControlHtmlAttributesSupported = MvcVersion >= requiredMvcVersion;
// 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#>";
<#
}
#>
}
<h2><#= ViewName#></h2>
<#
} else {
#>
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title><#= ViewName #></title>
</head>
<body>
<#
PushIndent(" ");
}
#>
<#
if (ReferenceScriptLibraries) {
#>
<#
if (!IsLayoutPageSelected && IsBundleConfigPresent) {
#>
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/jqueryval")
<#
}
#>
<#
else if (!IsLayoutPageSelected) {
#>
<script src="~/Scripts/jquery-<#= JQueryVersion #>.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
<#
}
#>
<#
}
#>
<form id="formEdit" class="form-horizontal">
@Html.HiddenFor(model => model.Id)
<div class="modal-body">
<#
IEnumerable<PropertyMetadata> properties = ModelMetadata.Properties;
foreach (PropertyMetadata property in properties) {
if (property.Scaffold && !property.IsPrimaryKey && !property.IsForeignKey) {
#>
<div class="form-group">
@Html.LabelFor(model => model.<#= GetValueExpression(property) #>, "<#= GetValueExpression(property) #>", new { @class = "control-label col-xs-2" })
<div class="col-xs-10">
@Html.TextBoxFor(model => model.<#= GetValueExpression(property) #>, new { @class = "form-control", data_bind = "value:editModel.<#= GetValueExpression(property) #>" })
</div>
</div>
<#
}
}
#>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span>关闭</button>
<button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span>保存</button>
</div>
</form>
<#
var index = ViewDataTypeName.LastIndexOf(".");
var ModelName = ViewDataTypeName.Substring(index+1, ViewDataTypeName.Length-index-1);
#>
<script src="~/Scripts/extensions/knockout.edit.js"></script>
<script type="text/javascript">
$(function () {
var model = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model));
var viewModel = {
formId: "formEdit",
editModel : model,
urls :
{
submit : model.id == 0 ? "/<#= ModelName #>/Add" : "/<#= ModelName #>/Update"
},
validator:{
fields: { 
Name: {
validators: {
notEmpty: {
message: '名称不能为空!'
}
}
}
}
}
};
ko.bindingEditViewModel(viewModel); 
});
</script>
<#
if(IsLayoutPageSelected && ReferenceScriptLibraries && IsBundleConfigPresent) {
#>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
<#
}
#>
<#
else if(IsLayoutPageSelected && ReferenceScriptLibraries) {
#>
<script src="~/Scripts/jquery-<#= JQueryVersion #>.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
<#
}
#>
<#
// 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" #>

BootstrapTable+KnockoutJS自定义T4模板快速生成增删改查页面

生成的代码:

@model TestKO.Models.User
<form id="formEdit" class="form-horizontal">
@Html.HiddenFor(model => model.Id)
<div class="modal-body">
<div class="form-group">
@Html.LabelFor(model => model.Name, "Name", new { @class = "control-label col-xs-2" })
<div class="col-xs-10">
@Html.TextBoxFor(model => model.Name, new { @class = "form-control", data_bind = "value:editModel.Name" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.FullName, "FullName", new { @class = "control-label col-xs-2" })
<div class="col-xs-10">
@Html.TextBoxFor(model => model.FullName, new { @class = "form-control", data_bind = "value:editModel.FullName" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Age, "Age", new { @class = "control-label col-xs-2" })
<div class="col-xs-10">
@Html.TextBoxFor(model => model.Age, new { @class = "form-control", data_bind = "value:editModel.Age" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Des, "Des", new { @class = "control-label col-xs-2" })
<div class="col-xs-10">
@Html.TextBoxFor(model => model.Des, new { @class = "form-control", data_bind = "value:editModel.Des" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Createtime, "Createtime", new { @class = "control-label col-xs-2" })
<div class="col-xs-10">
@Html.TextBoxFor(model => model.Createtime, new { @class = "form-control", data_bind = "value:editModel.Createtime" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.strCreatetime, "strCreatetime", new { @class = "control-label col-xs-2" })
<div class="col-xs-10">
@Html.TextBoxFor(model => model.strCreatetime, new { @class = "form-control", data_bind = "value:editModel.strCreatetime" })
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span>关闭</button>
<button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span>保存</button>
</div>
</form>
<script src="~/Scripts/extensions/knockout.edit.js"></script>
<script type="text/javascript">
$(function () {
var model = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model));
var viewModel = {
formId: "formEdit",
editModel : model,
urls :
{
submit : model.id == 0 ? "/User/Add" : "/User/Update"
},
validator:{
fields: { 
Name: {
validators: {
notEmpty: {
message: '名称不能为空!'
}
}
}
}
}
};
ko.bindingEditViewModel(viewModel); 
});
</script>
Edit.cshtml

当然,代码也需要做稍许修改。通过添加自定义的模板页,只要后台对应的实体模型建好了,在前端只需要新建两个自定义视图,一个简单的增删改查即可完成,不用写一句js代码。

三、select组件的绑定

上面介绍了下T4封装增删改查的语法,页面所有的组件基本都是文本框,然而,在实际项目中,很多的查询和编辑页面都会存在下拉框的展示,对于下拉框,我们该如何处理呢?不卖关子了,直接给出解决方案吧,比如编辑页面我们可以在后台将下拉框的数据源放在实体里面。

用户的实体

[DataContract]
public class User
{
[DataMember]
public int id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string FullName { get; set; }
[DataMember]
public int Age { get; set; }
[DataMember]
public string Des { get; set; }
[DataMember]
public DateTime Createtime { get; set; }
[DataMember]
public string strCreatetime { get; set; }
[DataMember]
public string DepartmentId { get; set; }
[DataMember]
public object Departments { get; set; }
}

然后编辑页面

public ActionResult Edit(User model)
{
model.Departments = DepartmentModel.GetData();
return View(model);
}

然后前端绑定即可。

<div class="form-group">
<label for="txt_des">所属部门</label>
<select id="sel_dept" class="form-control" data-bind="options: editModel.Departments,
optionsText: 'Name',
optionsValue: 'Id',
value:editModel.DepartmentId"></select>
</div>

JS代码不用做任何修改,新增和编辑的时候部门字段就能自动添加到viewmodel里面去。

当然,我们很多项目使用的下拉框都不是单纯的select,因为单纯的select样式实在是难看,于是乎出了很多的select组件,比如博主之前分享的select2、MultiSelect等等。当使用这些组件去初始化select时,审核元素你会发现,这个时候界面上的下拉框已经不是单纯的select标签了,而是由组件自定义的很多其他标签组成。我们就以select2组件为例来看看直接按照上面的这样初始化是否可行。

我们将编辑页面初始化的js代码增加最后一句:

<script type="text/javascript">
$(function () {
var model = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model));
var viewModel = {
formId: "formEdit",
editModel : model,
urls :
{
submit : model.id == 0 ? "/User/Add" : "/User/Update"
},
validator:{
fields: {
Name: {
validators: {
notEmpty: {
message: '名称不能为空!'
}
}
}
}
}
};
ko.bindingEditViewModel(viewModel);
$("#sel_dept").select2({});
});
</script>

通过新增和编辑发现,这样确实可行!分析原因,虽然初始化成select2组件之后,页面的html发生了变化,但是组件最终还是会将选中值呈现在原始的select控件上面。不知道除了select2,其他select初始化组件会不会这样,待验证。但是这里有一点需要说明下,在初始化select2之前,下拉框的options必须先绑定值,也就是说,组件的初始化必须要放在ko.applyBinding()之后。

四、总结

至此,ko结合bootstrapTable的模板生成以及select控件的使用基本可用,当然,还有待完善。后面如果有时间,博主会整理下其他前端组件和ko的联合使用,比如我们最常见的日期控件。如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
jQuery对象和DOM对象使用说明
Jun 25 Javascript
自己编写的类似JS的trim方法
Oct 09 Javascript
jQuery中element选择器用法实例
Dec 29 Javascript
JavaScript编写连连看小游戏
Jul 07 Javascript
jQuery实现Email邮箱地址自动补全功能代码
Nov 03 Javascript
学习JavaScript设计模式之策略模式
Jan 12 Javascript
JS 实现 ajax 异步浏览器兼容问题
Jan 21 Javascript
javascript作用域链与执行环境详解
Mar 25 Javascript
使用socket.io制做简易WEB聊天室
Jan 02 Javascript
vue实现微信分享功能
Nov 28 Javascript
深入理解Node内建模块和对象
Mar 12 Javascript
vue 重塑数组之修改数组指定index的值操作
Aug 09 Javascript
AngularJS基础 ng-href 指令用法
Aug 01 #Javascript
jQuery实现点击行选中或取消CheckBox的方法
Aug 01 #Javascript
AngularJS基础 ng-hide 指令用法及示例代码
Aug 01 #Javascript
使用JS中的exec()方法构造正则表达式验证
Aug 01 #Javascript
javascript创建含数字字母的随机字符串方法总结
Aug 01 #Javascript
jQuery简单实现彩色云标签效果示例
Aug 01 #Javascript
jQuery实现的简单百分比进度条效果示例
Aug 01 #Javascript
You might like
PHP URL路由类实例
2013/11/12 PHP
PHP使用mysqli操作MySQL数据库的简单方法
2017/02/04 PHP
Laravel中前端js上传图片到七牛云的示例代码
2017/09/04 PHP
Javascript的匿名函数小结
2009/12/31 Javascript
javascript是怎么继承的介绍
2012/01/05 Javascript
javascript 循环调用示例介绍
2013/11/20 Javascript
js自动生成的元素与页面原有元素发生堆叠的解决方法
2014/09/04 Javascript
javascript 常见功能汇总
2015/06/11 Javascript
jQuery控制frames及frame页面JS的方法
2016/03/08 Javascript
AngularJS控制器之间的通信方式详解
2016/11/03 Javascript
angular和BootStrap3实现购物车功能
2017/01/25 Javascript
js中el表达式的使用和非空判断方法
2018/03/28 Javascript
JS实现的缓冲运动效果示例
2018/04/30 Javascript
D3.js实现拓扑图的示例代码
2018/06/30 Javascript
微信小程序地图实现展示线路
2020/07/29 Javascript
详解如何在Javascript中使用Object.freeze()
2020/10/18 Javascript
python自然语言编码转换模块codecs介绍
2015/04/08 Python
Python栈类实例分析
2015/06/15 Python
Python正则表达式和re库知识点总结
2019/02/11 Python
Python 处理文件的几种方式
2019/08/23 Python
Python SELENIUM上传文件或图片实现过程
2019/10/28 Python
浅谈spring boot 集成 log4j 解决与logback冲突的问题
2020/02/20 Python
曼联官方网上商店:Manchester United Direct
2017/07/28 全球购物
草莓网化妆品日本站:Strawberrynet日本
2017/10/20 全球购物
销售员自我评价怎么写
2013/09/19 职场文书
编辑找工作求职信范文
2013/12/16 职场文书
《美丽的彩虹》教学反思
2014/02/25 职场文书
《地震中的父与子》教学反思
2014/04/10 职场文书
解除劳动合同证明书模板
2014/11/20 职场文书
2014年小学语文工作总结
2014/12/20 职场文书
个人事迹材料怎么写
2014/12/30 职场文书
丽江古城导游词
2015/02/03 职场文书
趵突泉导游词
2015/02/03 职场文书
《失物招领》教学反思
2016/02/20 职场文书
详解MySQL 联合查询优化机制
2021/05/10 MySQL
mysql使用instr达到in(字符串)的效果
2022/04/03 MySQL