主要学习内容:
1.jQuery在ASP.NET中的使用
当使用VS项目模板创建ASP.NET MVC项目的时候,会默认生成jQuery项目的一些内容,添加脚本引用:
<script src="~/Scripts/jquery-1.10.2.js"></script>
虽然简单的脚本引用(上面所示)是有效的,但是这种方法是依赖于版本的,如果想更新到新版本的jQuery,则我们就需要在/Views/Shared/_Layout.cshtml中中采用下面的引用方式:
@Scripts.Render("~/bundles/jquery")
除了简化脚本,这种卡捆绑还有其他好处,在发布模式下自动的使用微小版本,以及将版本引用集中到一个位置,从而只需要在一个位置更新。
注意:上面调用渲染的是在/App_Start/BundleConfig.cs中预定义的jquery脚本捆绑。如下:
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.validate*"));
//使用要用于开发和学习的 Modernizr 的开发版本。然后,当你做好生产准备就绪,请使用 https://modernizr.com 上的生成工具仅选择所需的测试。
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
"~/Scripts/modernizr-*"));
bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
"~/Scripts/bootstrap.js",
"~/Scripts/respond.js"));
bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/bootstrap.css",
"~/Content/site.css"));
}
自定义脚本的引用
为了区分哪些是自定义的,哪些是应用程序特定的脚本,常用的约定是把自定义的脚本放在/Scripts/App子目录中。
例如:我们自定义的一个MyScripts.js脚本就可以放在/Scripts/App目录中,要在应用程序中使用MyScripts.js,需要对其进行引入,因为MyScripts.js需要jQuery的支持,所以引用的scripts标签需要放在jQuery的后面。
如果脚本要包含在整个应用程序中则把脚本放在母版页_Layout视图中,如果在某一个页面使用,则只需要在该页面进行引用即可,如我们需要在首页使用MyScripts.js,我们可以把它添加到Index视图(/Views/Home/Index.cshtml)中,
我们可以在预定义的scipts节点中进行引入
母版页Layout.cshtml中的部分内容
Index.cshtml的引用方式:
<body class="launching">
@section scripts{
<script src = "~/Scripts/App/MyScript.js"></script>
}
</body>
注意:为什么不在Layout.cshtml顶部包含脚本引用,这样就可以在任何视图不用担心引用顺序的问题了,主要是为了考虑性能,一般推荐做法是把javascript脚本引用放在Html的结尾,body标签结束之前。
2.Ajax辅助方法
在后台,Ajax辅助方法依赖于非侵入的MVC的jQuery扩展,如果使用这些辅助方法,需要引入脚本jquery-unobtrusive-ajax.js.
注意:如果在使用Ajax辅助方法时放生问题,需要首先检查是否引入了该文件
添加非侵入式Ajax脚本:
通过Nuget Package对话框,搜索Microsoft jQuery Unobtrusive Ajax,或者通过VS工具中的包管理控制台输入Install-Package Microsoft.jQuery.Unobtrusive.Ajax,建议把脚本添加到单独的视图中,而不要添加到_Layout视图中(除非网站中大量引用了Ajax请求)
2.1ActionLink方法
Ajax辅助方法可以通过Ajax属性访问,与HTML辅助方法类似,Ajax辅助方法可以创建一个具有异步行为的锚标签
<div>
@Ajax.ActionLink("详细信息","DailyDeal",
null,//一个包含路由参数的对象。 通过检查对象的属性,利用反射检索参数。 此对象通常是使用对象初始值设定项语法创建的。
new AjaxOptions//提供异步请求选项的对象。
{
UpdateTargetId = "dailyddeal",//获取或设置要使用服务器响应来更新的 DOM 元素的 ID。
InsertionMode = InsertionMode.Replace,//获取或设置指定如何将响应插入目标 DOM 元素的模式。插入模式(“InsertAfter”、“InsertBefore”或“Replace”)。 默认值为“Replace”。
HttpMethod = "GET"//获取或设置 HTTP 请求方法(“Get”或“Post”)。
},
new //一个对象,其中包含要为该元素设置的 HTML 特性。class是保留字,所以加一个@进行转义
{
@class="btn btn-primary"
}
)
</div>
控制器中对应的DailyDeal方法:
public ActionResult DailyDeal()
{
var album = GetDailyDeal();
return PartialView("_DailyDeal",album);
}
private Album GetDailyDeal()
{
var album = storeDB.Albums.
OrderBy(a=>System.Guid.NewGuid())//随机排序
.First();
return album;
}
Ajax操作链接的返回值是纯文本或者HTML,在上面示例代码中,将通过渲染一个部分视图来返回HTML,下面的_DailyDeal.cshtml的Razor代码
@model MvcMusicStore.Models.Album
<div class="panel panel-primary">
<div class = "panel-heading">
<h3 class="panel-title">你的交易:@Model.Title</h3>
</div>
<div class="panel-body">
<p>
<img alt="@Model.Title" src="@Url.Content(@Model.AlbumArtUrl)" />
</p>
<div id="album-details">
<p>
<em>艺术家:</em>
@Model.Artist.Name
</p>
<p>
<em>价格:</em>
@String.Format("{0:F}",Model.Price)
</p>
@Html.ActionLink("加入购物车","AddToCart","ShoppingCart",new {id=Model.AlbumId},new {@class="btn btn-primary"})
</div>
</div>
</div>
Ajax.ActionLink生成的内容能够获取服务器端的响应,并可以直接把新内容移植到页面中
2.2 Html5特性
查看上面第一个ActionLink示例渲染后的代码:
<div id="dailyddeal">
<a class="btn btn-primary" data-ajax="true" data-ajax-method="GET" data-ajax-mode="replace"
data-ajax-update="#dailyddeal" href="/Home/DailydDeal">
详细信息
</a>
</div>
可以看到ActionLink中的所有设置被编码成了Html元素的特性,大多数都带有data-前缀,通常称为data-特性。Web浏览器不会尝试解析data-特性的内容,因此可以放心的把自己的数据交给它,这些数据也不会影响页面的显示渲染。所有的ASP.NET MVC Ajax特性默认使用的都是data-特性。
2.3 Ajax表单(异步表单)
如果需要在我们的音乐商店应用程序的首页添加一个查找艺术家的功能。需要用户的输入,所以需要一个form标签,他不是一个普通的表单,而是异步表单
<div class="panel panel-default">
<div class="panel-heading">搜索</div>
<div class="panel-body">
@using (Ajax.BeginForm("ArtistSearch","Home",
new AjaxOptions
{
UpdateTargetId = "searchresults",//获取或设置要使用服务器响应来更新的 DOM 元素的 ID。
OnFailure="searchFailed",//获取或设置在页面更新失败时要调用的 JavaScript 函数。
InsertionMode = InsertionMode.Replace,//获取或设置指定如何将响应插入目标 DOM 元素的模式。插入模式(“InsertAfter”、“InsertBefore”或“Replace”)。 默认值为“Replace”。
LoadingElementId="ajax-loader",//获取或设置在加载 Ajax 函数时要显示的 HTML 元素的 id 特性。
HttpMethod = "GET"//获取或设置 HTTP 请求方法(“Get”或“Post”)。
}))
{
<input type="text" name="q" />
<input type="submit" value="搜索"/>
<img id="jax-loader" src="@Url.Content("~/Image/ajax-loader.gif")" style="display:none">
<div id="searchresults"></div>
}
</div>
</div>
在渲染的表单中,当用户点击提交按钮时,浏览器就会向控制器HomeController的ArtistSearch操作发送异步GET请求,当发送异步请求时,客户端会自动显示LoadingElementId指定的元素,一般是一个遮罩层,此外还可以设置客户端事件如OnBegin、OnSuccess、OnComplete、OnFailure,可以给这些参数赋予一个JavaScript名称,当事件触发时,调用该函数,上面指定了OnFailure事件:
function searchFailed(){
$('#searchresults').html("请求失败!");
}
对于这个示例,控制器操作需要查询数据库并渲染一个部分视图,此外,操作要返回一个纯文本,但同时又把一个艺术家放在列表中,因此他要渲染一个部分视图。控制器代码
public ActionResult ArtistSearch(string q)
{
var artists = storeDbB.Artists.Where(a=>a.Name.COntains(q)).ToList();
return PartialView(artists);
}
渲染的部分视图利用模型构建列表,该部分视图的名称是ArtistSearch.cshtml位于项目的View/Home/中
@Model IEnumerable<MvcMusicStore.Models.Artists>
<div id="searchresults">
<ul>
@foreach(var item in Model){
<li>@item.Name</li>
}
</ul>
</div>
3.客户端验证
客户端验证依赖于jQuery验证插件,该插件(jquery.vallidate)默认情况下在MVC5应用程序项目的Scripts文件夹下,如果想实现客户端验证,那么在相应的视图中就需要包含jqueryval捆绑的引用:
@section scripts{
@Scripts.Render("~/bundles/jqueryval")
}
可以查看上面所展示的/App_Start/BundleConfig.cs,的代码会看到这个捆绑。jquery.validate*意味着该捆绑中将包含jquery.validate.js和jquery.validate.unobtrusive.js.
默认情况下,非侵入的JavaScript和客户端验证在ASp.NET MVC中时启用的,可通过web.config设置改变这些行为,查看appSetting节点会看到
<appSettings>
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>
可以把vaue设置为false来禁用这些特性,另外还可以在视图中控制这些特性,HTML辅助方法EnableEnableClientValidation和EnableEnableUnobtrusiveJavaScript在一个具体的视图中重写了这些配置,
禁用这些特性的主要原因是维护应用程序自定义脚本的向后兼容
假设模板中油这样的代码:
//属性
[Required (ErrorMessage="专辑标题是必须的")]
[StrinqLength (160)]
public string Title
{
get;set;
}
//客户端
<p>
@Html. LabelFor (model => model.Title)
@Html. TextBoxFor (model => model.Title)
@Html. ValidationMessageFor (model=> model.Title)
</p>
这里,辅助方法TextBoxFor是关键,当TextBoxFor 看到验证元数据(Title属性)上的Required 和StrinqLength 注解时,他会将这些算数据放入到HTML中,TiTle属性编辑器标记如下:
<input data-val="true" data-val-length="字段标题必须是一个最大长度为160的字符串"
data-val-length-max="160" data- val-required="专辑标题是必须的" id="Title"
name="Title" type="text" value="Greatest Hits" />。
上述代码中是jquery.validate.unobtrusive.js.脚本负责使用这个元数据(以data-val=”true”为标记),结合jquery验证插件来进行数据的验证。
3.1 自定义验证
我们编写一个验证字符串单词个数的特性MaxWordsAttribute,代码如下:
public class MaxWordsAttribute: ValidationAttribute
{
public MaxWordsAttribute(int maxwords):base("Too Many Words in {0}")
{
MaxWords = maxwords;
}
public int MaxWords
{
get;set;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null)
{
var wordCount = value.Tostring () .Split(' ).Length;
if (wordCount > MaxWords)
{
return new ValidationResult(FormatErrorMessage (validationContext. DisplayName);
}
}
return ValidationResult. Success;
}
}
可以这样使用该特性
[Required (ErrorMessage="专辑标题是必须的")]
[StrinqLength (160)]
[MaxWords(10)]
public string Title
{
get;set;
}
为了支持客户端验证,需要实现IClientValidatable接口,IClientValidatable定义了单个方法GetClientValidationRules,当ASP.NET MVC使用这个接口查找对象,它会调用GetClientValidationRules来检索ModelClientValidationRule对象序列。
public class MaxWordsAttribute: ValidationAttribute,IClientValidatable
{
public MaxWordsAttribute(int wordcount):base("Too Many Words in {0}")
{
WordCount = wordcount;
}
public int WordCount
{
get;set;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null)
{
var wordCount = value.Tostring () .Split(' ).Length;
if (wordCount > WordCount)
{
return new ValidationResult(FormatErrorMessage (validationContext. DisplayName);
}
}
return ValidationResult. Success;
}
//IClientValidatable接口实现方法
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata,ControllerCopntext context)
{
var rule = new ModelClientValidationRule();
rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
rule.VallidationParameters.Add("wordcount",WordCount );
rule.ValidationType = "maxwords"
yield return rule;
}
}
要实现客户端的执行验证,需要提供如下几点信息:
- 如果验证失败,要显示的提示信息;
- 允许的单词数的范围;
- 一段来计算单词数的JavaScript的代码标识。
这些信息就是上面代码放进反悔规则中的内容。如果要在客户端发出多种类型的验证,代码可以返回多个规则。
代码把错误提示信息放在了ErrorMessage中,这样可以使服务器端错误提示精确的匹配客户端错误提示,
VallidationParameters集合用来存放客户端需要的参数,像允许最大的单词数,还可以存放其他的参数,但要注意参数的名称是有意义的,他们要匹配在客户端脚本中看到的名称。ValidationType属性标识了客户端需要一段JavaScript脚本,
ASP.NET MVC框架在客户端上利用GetClientValidationRules方法返回的规则将信息序列化为data-特性。
<input data-val="true"
data-val-length="The field ritle must be a string with a maximum lengthof 160."
data-val-length-max="160"
data-val-maxwords="Too many words in Title"
data-val-maxwords-wordcount="10"
data-val-required="专辑标题是必须的" id-"Title" name="Title"
type-"text" values"For Those About To Rock We Salute You" />
注意,data-val-maxwords的maxwords是规则中ValidationType属性对应的值,验证类型和所有的验证参数的名称都要小写,因为它们的值必须作为合法的HTML特性标识使用。
尽管在客户端上有元数据,仍需要编写一些执行验证逻辑的脚本代码。
3.2自定义脚本验证代码
在客户端没有必要编写代码从data-特性中挖掘数据,为了执行验证需要以下两段脚本代码
- 适配器:适配器和非侵入式MVC扩展一道识别需要的元数据,然后非侵入式扩展帮助从data-特性中检索值,并且还帮助把数据转换为Jquery验证能够理解的格式。
- 验证规则:在jQuery用语中被称作验证器。
我们创建一个新的脚本命名为CustomValidators.js,改脚本文件的引用必须出现在jqueryval捆绑引用之后
@section scripts{
@Scripts.Render("~/bundles/jqueryval")
<script src="~/Scripts/App/CustomValidators.js"></script>
}
首先要编写的代码是适配器。MVC框架的非侵入式验证扩展存储了jQuery.validator.unobtrusive.adapters对象的所有适配器,这些适配器公开了一个API,我们可以用来添加新的适配器。
名称 | 描述 |
---|---|
addBool | 为”启用”或者“禁用”的验证规则创建适配器。改规则不需要额外的参数。 |
addSignleVal | 为需要从元数据中检索唯一参数值验证规则创建适配器 |
addMinMax | 创建一个映射到验证规则的适配器——一个用来检索最小值,另一个用来检索最大值,这两个规则中至少有一个要依靠得到的数据运行。 |
add | 创建一个不适合前面类别的适配器,因为它需要额外的参数或者额外的设置代码。 |
对于最大单词的情形可以使用addSingleVal或者addMinMax(或者add,因为它适用于任何场合)来创建适配器,因为不需要单词最小数量的验证,所以我们选择使用addSingeVal来创建适配器。
$.valildator.unobtrusive.adapters.addSingleVal("maxwords","wordcount");
第一个参数是适配器的名称与服务器端规则中ValidationType的值对应,第二个参数匹配服务器端VallidationParameters集合中的参数名称。适配器相对来说比较简单。适配器的主要目的是识别非侵入式扩展要定位的元数据。有了适配器,现在就可以编写验证器。
所有验证器都在jQuery.validator对象中,与adapters对象类似。validator对象也有一个API,可以添加验证器,该函数的名称是addMethod
$.valildator.addMehtod("maxwords",function(value, element, maxwords){
if(value){
if(value.split(' ').length>maxwords){
return false;
}
}
return true;
})
该方法中参数:
- 验证器名称:默认情况下验证器名称要匹配适配器名称,而适配器名称又要匹配服务器上ValidationType的值。
- 函数:当验证发生时调用。函数的第一个参数包含输入的值,第二个参数是输入的元素,其中也包含了验证的值(在value本身没有提供足够的信息的情况下),第三个参数包含了一个数组中所有的验证参数,在这个示例中就包含了单一的验证参数即最大的单词数量。
CustomValidators.js完整的代码
///<reference path="jquery.validate.js">
///<reference path="jquery.validate.unobtrusive.js">
$.valildator.unobtrusive.adapters.addSingleVal("maxwords","wordcount");
$.valildator.addMehtod("maxwords",function(value, element, maxwords){
if(value){
if(value.split(' ').length>maxwords){
return false;
}
}
return true;
})