zoukankan      html  css  js  c++  java
  • 当ASP.NET MVC模型验证遇上CKEditor

    项目需要,使用到了CKEditor编辑器。这是个很不错的富文本编辑器,但是当它绑定的字段需要进行模型验证的时候,却会出现验证失效的问题。因此本文旨在记录这个问题和给出解决办法。以下以ValidationAttribute和jQuery Validate2中验证方式为例。测试项目包含3个页面:Index.cshtml(包含2个部分视图)、Add.cshtml(添加页)、Companies(列表页,仅为展示数据);一个模型(Company)。项目功能截图如下:

    ---------------------------------------------------------------------------List

    ---------------------------------------------------------------------------Add

    Part 1 使用ValidationAttribute进行验证

    模型字段的属性设置就不说了,所有字段均不为空,测试Company代码如下:

    public class Company
    {
        public Guid CompanyID { get; set; }
        [Display(Name = "公司名")]
        [Required(ErrorMessage = "公司名称不能为空")]
        public string CompanyName { get; set; }
        [Display(Name = "公司地址")]
        [Required(ErrorMessage = "公司地址不能为空")]
        public string CompanyAddress { get; set; }
        [Display(Name = "公司简介")]
        [Required(ErrorMessage = "公司简介不能为空")]
        public string CompanyProfile { get; set; }
    
        public static List<Company> Companies = new List<Company>() {
                    new Company{CompanyID=new Guid("{CC1088BC-EF53-45C0-8D95-54F7665834C0}") ,CompanyName="百度",CompanyAddress="北京市海淀区上地十街10号",CompanyProfile="百度"},
                    new Company{CompanyID=new Guid("{6DC7545F-CA7C-49E8-B88E-447F43CBA7AE}") ,CompanyName="腾讯",CompanyAddress="中国广东省深圳市南山区深南大道10000号腾讯大厦",CompanyProfile="腾讯"},
                    new Company{CompanyID=new Guid("{EFA27D91-44CE-477E-8B19-BB6A1A785EC1}"), CompanyName="阿里巴巴",CompanyAddress="杭州市滨江区网商路699号",CompanyProfile="阿里"}
        };
    }
    View Code

    Add.cshtml代码如下:

    @model Laibxw.Models.Company
    @{
        ViewBag.Title = "CompanyAdd";
        Layout = null;
    }
    
    <h4></h4>
    @using (Html.BeginForm("Add", "Test", FormMethod.Post, new { @class = "form-horizontal", role = "form", id = "add" }))
    {
        @Html.AntiForgeryToken()
        <div class="form-horizontal">
            @*@Html.ValidationSummary(true)*@
            <div class="form-group">
                @Html.LabelFor(model => model.CompanyName, new { @class = "control-label col-md-2" })
                <div class="col-md-10" style="margin-top:5px;">
                    @Html.TextBoxFor(model => model.CompanyName)
                    @Html.ValidationMessageFor(model => model.CompanyName)
                </div>
            </div>
    
            <div class="form-group">
                @Html.LabelFor(model => model.CompanyAddress, new { @class = "control-label col-md-2" })
                <div class="col-md-10" style="margin-top:5px;">
                    @Html.TextBoxFor(model => model.CompanyAddress)&nbsp;&nbsp;
                    @Html.ValidationMessageFor(model => model.CompanyAddress)
                </div>
            </div>
    
            <div class="form-group">
                @Html.LabelFor(model => model.CompanyProfile, new { @class = "control-label col-md-2" })
                <div class="col-md-10" style="margin-top:5px;">
                    @Html.TextAreaFor(model => model.CompanyProfile, new { @class = "ckeditor" })
                    @Html.ValidationMessageFor(model => model.CompanyProfile)
                </div>
            </div>
            <div class="form-group">
                <div class="col-md-offset-2 col-md-10">
                    <input type="submit" value="提交" class="btn btn-default" />
                </div>
            </div>
        </div>
    }
    <style>
        .error {
            color: #b94a48;
            font-weight: inherit;
        }
    </style>
    
    <script src="~/Scripts/jquery.validate.js"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            $("#add").submit(function () {
                var _this = $(this);
                $.post(_this.attr('action'), _this.serialize(), function (data) {
                    if (data.error == "0") {
                        location.href = "/Test/Index";
                    } else {
                        $('#tab2').html(data) //3.出来样式改变了
                    }
                });
                return false;
            });
        });
    </script>
    View Code

    后台Controller如下:

        public class TestController : Controller
        {
            //
            // GET: /Test/
            public ActionResult Index()
            {
                return View();
            }
    
            public ActionResult Companies()
            {
                var companies = Company.Companies;
                return View(companies);
            }
    
            public ActionResult Add()
            {
                return View();
            }
    
            [HttpPost]
            public ActionResult Add(Company model)
            {
                if (!ModelState.IsValid) return PartialView(model);
                model.CompanyID = new Guid("{F419EC35-F070-491C-B38D-73FC4A24F253}");
                Company.Companies.Add(model);
                return Json(new { error = 0 });
            }
        }
    View Code

    运行示例添加一个Company:

    发现果真无法验证,而且由于使用了部分视图,CKEditor被还原成了默认的textarea。有点糟糕!

    Part 2 使用ValidationAttribute进行验证

    那么使用jQuery Validate呢?于是添加了jq脚本:

    <script src="~/Scripts/jquery.validate.js"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            $("#add").submit(function () {
                var _this = $(this);
                if (validater.form()) { //验证成功
                    $.post(_this.attr('action'), _this.serialize(), function (data) {
                        if (data.error == "0") {
                            location.href = "/Test/Index";
                        } else {
                            $('#tab2').html(data) //3.出来样式改变了
                        }
                    });
                }
                return false;
            });
    
            //验证
            var validater = $("#add").validate({ //1.解决所谓的jquery.validate.js失效的说法
                rules: {
                    CompanyName: {
                        required: true,
                    },
                    CompanyAddress: {
                        required: true,
                    },
                    CompanyProfile: {
                        required: true,
                    }
                },
                messages: {
                    CompanyName: {
                        required: "公司名称不能为空",
                    },
                    CompanyAddress: {
                        required: "公司地址不能为空",
                    },
                    CompanyProfile: {
                        required: "公司简介不能为空",
                    }
                }
            });
        });
    </script>
    View Code

    运行效果和上面的是一样的。

    那么之所以出现这个状况,其实是由于CKEditor没有及时将用户输入更新到相应的textarea。如果你测试会发现,当你第二次提交表单的时候,是可以获取获取到值的。那么解决这个问题的办法是这个样子的,当提交表单之前的时候及时更新textarea的值:

    //先更新textarea值
    for (instance in CKEDITOR.instances)
        CKEDITOR.instances[instance].updateElement();
    

    值是获取到了,但是用jQuery.validate仍然没有效果(网上看了很多解决办法,貌似只看到一个回到是对的),解决办法是在客户端验证的时候加上ignore: '':

    另外当使用纯后端验证的时候,CKEditor会被“打回原形”。解决的办法是在验证不通过返回部分视图之后加上这么一句:

    $('textarea.ckeditor').each(function () {
        var $textarea = $(this);
        CKEDITOR.replace($textarea.attr("id"))
    });
    

    但是这样还是会有一个小的缺陷:

    就是先显示之前的textarea标签,然后立即变为CKEditor。开始以为是因为这个是在返回部分视图并将数据展示到#tab2之后发生的,后来发现,即使是在部分视图中加载好也会出现这样的问题。

    单独测试CKEditor不难发现经过服务器端验证使用ValidationMessageFor来显示错误消息就会出现这个状况,其实原因很简单,你刷新页面就会发现是由于CKEditor插件略微有些延迟。所以它首先会显示原生态的textarea(没通过验证会有错误提示),然后CKEditor才会应用于textarea。这确实是个头痛的问题。

    这个问题暂时没有找到合适的解决方案。只有采取jQuery.validate.js配合手写验证规则的前端验证来解决。关于服务器端验证的办法提供一个不是很完美的办法:

    $('textarea.ckeditor').each(function () {
        var $textarea = $(this);
        var error = $textarea.parent().find('span');
        error.hide();
        CKEDITOR.replace($textarea.attr("id"));
        $textarea.hide(); //暂时隐藏掉error
        setTimeout(function () {
            error.show();
        }, 20);
    });
    

    问题处理代码:

    @model Models.Company
    @{
        ViewBag.Title = "CompanyManagement";
        Layout = null;
    }
    @Styles.Render("~/Content/Plan_admin.css")
    @Styles.Render("~/Content/lbw_css.css")
    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/exp-js")
    @Scripts.Render("~/bundles/bootstrap")
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
    @Scripts.Render("~/bundles/bootstrap.min")
    <div class="ProductAdd">
        <div class='container-fluid' style=" 650px;float:left;margin-left:100px; margin-top:100px;">
            <div class='tabbable tabs-left'>
                <ul class='nav nav-tabs  nav-pills  top-size' role="tablist">
                    <li class='active' role="presentation"><a href='#tab1' data-toggle='tab' style="font-size:14px;font-family:Arial;">公司列表</a></li>
                    <li><a href='#tab2' data-toggle='tab' style="font-size:14px;">添加公司</a></li>
                </ul>
                <div class='tab-content'>
                    <div class='tab-pane active' id='tab1'>
                        @Html.Action("Companies", "Test")
                    </div>
                    <div class='tab-pane' id='tab2'>
                        @Html.Action("Add", "Test")
                    </div>
                </div>
                <div style="clear:both"></div>
            </div>
        </div>
    </div>
    <script src="~/Scripts/jquery.validate.js"></script>
    
    <script type="text/javascript">
        $(document).ready(function () {
            $("#tab2 form").submit(function () {
                //先更新textarea值
                for (instance in CKEDITOR.instances)
                    CKEDITOR.instances[instance].updateElement();
                var _this = $(this);
                if (validater.form()) {
                    $.post(_this.attr('action'), _this.serialize(), function (data) {
                        if (data.error == "0") {
                            location.href = "/Test/Index";
                        } else {
                            //问题出现在ckeditor对textare的改变有延迟
                            $('#tab2').empty().html(data) //3.出来样式改变了
                            $('textarea.ckeditor').each(function () {
                                var $textarea = $(this);
                                var span = $textarea.parent().find('span');
                                span.hide();
                                CKEDITOR.replace($textarea.attr("id"));
                                $textarea.hide();
                                setTimeout(function () {
                                    span.show();
                                }, 100);
                            });
                        }
                    });
                }
                return false;
            });
            //验证
            var validater = $("#tab2 form").validate({
                rules: {
                    CompanyName: {
                        required: true,
                    },
                    CompanyAddress: {
                        required: true,
                    },
                    CompanyProfile: {
                        required: true,
                    }
                },
                ignore: '',  //不能少
                messages: {
                    CompanyName: {
                        required: "公司名称不能为空",
                    },
                    CompanyAddress: {
                        required: "公司地址不能为空",
                    },
                    CompanyProfile: {
                        required: "公司简介不能为空",
                    }
                }
            })
        });
    </script>
    View Code

     

  • 相关阅读:
    c# 命令行下编译c#文件 // c# file类读写文件
    C#读取Xml
    System.IO.Path
    System.IO.Path 操作
    关于C#操作INI文件的总结
    C# 文件的一些基本操作(转)//用C#读写ini配置文件
    使用匿名类型做为ComboBox的DataSource
    C# Path 有关于文件路径等问题类(转)
    自考新教材--p60_5_2
    自考新教材--p60_5_1
  • 原文地址:https://www.cnblogs.com/fengchengjushi/p/4231994.html
Copyright © 2011-2022 走看看