ExtJS表单验证与ASP.NET MVC视图模型验证
带过下传统的ASP.NET MVC程序中的模型验证。在MVC程序中的视图模型是一种具有自我描述能力的实体类,而这个自我描述的能力是可以我们开发人员进行扩展的。在这些描述信息中,很重要的一部分就是对模型本身进行校验的信息,这些信息可以供框架校验用户反馈的模型的合法性,当然这个校验的过程在后台是少不了的。当然前台的校验工作也不能缺少,为了让用户有更好的体验以及减轻服务器端不必要的校验工作,ASP.NET MVC会将一部分的校验工作放在前台界面,让js来承担这部分的责任。这就是传统的模型验证的大概,详细的,可以参考MicrosoftMvcValidation.debug.js的代码以及在.aspx页面中输出的模型验证元数据。
使用传统的模型验证,不仅具有很好的可扩展性,恐怕其易用性以及简洁的代码的威力也是不可小觑的,相信有接触过的人都有感受。用过ExtJS的可能也有接触过extjs的表单验证,对于它的优点及用法我不是很清楚,总不至于是个差的东西,应该是不错的。尽管它可能很好用,但是它终归是一个独立的框架,无法直接去使用ASP.NET MVC为MicrosoftMvcValidation.debug.js生成的前台模型验证元数据。有一个很明显的事实那就是前台页面上已经有了模型验证元数据了,这些元数据是一些很有价值的东西,我总得想个办法使用它吧,1、要么打extjs的主意,扩展它的验证器;2、要么就是打MicrosoftMvcValidation的主意,让它的验证结果最终变成extjs的验证结果。出于对extjs庞大的体系的恐惧,我最终选择了后者。我重写了MicrosoftMvcValidation.debug.js中的Sys.Mvc.FieldContext.prototype._displayError,让原本的显示逻辑转换为extjs的呈现错误的逻辑即可,很干脆!
1 Ext.QuickTips.init();
2
3 Ext.form.Field.prototype.msgTarget = 'side';
4 Ext.form.Field.prototype.validationDelay = 0;
5 Ext.form.Field.prototype.validateOnBlur = false;
6 Ext.form.Field.prototype.validationEvent = 'keydown';
7 Sys.Mvc.FieldContext.prototype._displayError = function () {
8 if (this._errors.length > 0) {
9 Ext.getCmp($(this.elements).attr('id')).markInvalid(this._errors[0]);
10 }
11 }
到现在还完全不够,因为extjs的表单的是用Ext.form.FormPanel来构建的,而ASP.NET MVC只会在Html.EndForm()的时候为这个form进行表单域的验证信息的输出,这完全是不符合extjs的需求的。我们需要一种手段,不需要在Html.EndForm()的时候输出一段验证信息,而是指定为某个表单输出某个或者某几个视图模型的验证信息,那么extjs的FormPanel才能很好的工作。这需要额外的工作来为extjs以另外一种不同于ASP.NET MVC原生的模型验证元数据输出方式输出验证信息。下面是一个辅助类,有两个扩展方法
1 /// <summary>
2 /// 输出验证信息的辅助类
3 /// </summary>
4 public static class ValidateHelper
5 {
6 private const string _clientValidationScript = @"<script type=""text/javascript"">
7 //<![CDATA[
8 if (!window.mvcClientValidationMetadata) {{ window.mvcClientValidationMetadata = []; }}
9 //]]>
10 </script>";
11 private const string _clientMetadatas= @"<script type=""text/javascript"">
12 //<![CDATA[
13 window.mvcClientValidationMetadata.push({0});
14 //]]>
15 </script>";
16 private const string _enableKey = "*&^enable*UY^";
17 //开启客户端验证
18 public static void EnableExtJsValidation(this HtmlHelper html)
19 {
20 var httpContext = HttpContext.Current;
21 if (!httpContext.Items.Contains(_enableKey))
22 {
23 httpContext.Items[_enableKey] = true;
24 httpContext.Response.Write(_clientValidationScript);
25 }
26 }
27
28 /// <summary>
29 /// 向客户端输出模型验证元数据
30 /// </summary>
31 /// <param name="formId">需要进行验证的表单</param>
32 /// <param name="modelTypes">提供元数据的视图模型的类型</param>
33 public static void OutputClientValidationMetadata(this HtmlHelper html, string formId, params Type[] modelTypes)
34 {
35 if (HttpContext.Current.Items[_enableKey] != null &&
36 HttpContext.Current.Items[_enableKey] is bool &&
37 (bool)HttpContext.Current.Items[_enableKey])
38 {
39
40 FormContext formContext = new FormContext {FormId = formId};
41 html.ViewContext.FormContext = formContext;
42 var dataAnnotationsModelMetadataProvider = new DataAnnotationsModelMetadataProvider();
43 foreach (var modelType in modelTypes)
44 {
45 var metadatas = dataAnnotationsModelMetadataProvider.GetMetadataForType(null, modelType);
46 foreach (var prop in metadatas.Properties)
47 {
48 var fieldMetadata = formContext.GetValidationMetadataForField(prop.PropertyName, true);
49 var validators = ModelValidatorProviders.Providers.GetValidators(prop, html.ViewContext);
50 foreach (
51 ModelClientValidationRule rule in validators.SelectMany(v => v.GetClientValidationRules()))
52 {
53 fieldMetadata.ValidationRules.Add(rule);
54 }
55 }
56 }
57
58 html.ViewContext.HttpContext.Response.Write(string.Format(_clientMetadatas,
59 formContext.GetJsonValidationMetadata()));
60 }
61 }
62 }
OK,一切工作就绪。下面是示例代码
extjs构建formPanel
1 $(function () {
2 new Ext.form.FormPanel({
3 title: 'form1',
4 formId: 'form01',
5 items: [{
6 id: 'Name',
7 fieldLabel: '姓名',
8 xtype:'textfield'
9 }, {
10 id: 'Age',
11 fieldLabel: '年龄',
12 xtype: 'textfield'
13 }],
14 renderTo: document.body
15 })
16 });
开启客户端验证
1 <%
2 Html.EnableExtJsValidation();
3 Html.OutputClientValidationMetadata("form01", typeof(StudentModel));
4 %>
视图模型
1 public class StudentModel
2 {
3 [Required]
4 [StringLength(10,ErrorMessage = "姓名的长度不能超过10个字符")]
5 public string Name { get; set; }
6 [RegularExpression(@"\d{1,3}",ErrorMessage = "年龄必须是3位数字")]
7 public int Age { get; set; }
8 }
结果:
当然还有很多细节工作需要完善,例如form表单提交时的验证等(可能的话明天再补充)。上面只是一种解决的思路,采用它是因为微软的模型验证机制比较容易扩展,并且这样扩展不仅能应用于传统的ASP.NET MVC的开发同时也可以应用extjs同mvc的结合开发。