当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,暂时没有找到很好的办法来解决。不知道,有解决办法的没?

 

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。