ASP.NET MVC with JQuery,JQuery Validate&引导,验证不便

时间:2015-09-23 21:34:45

标签: jquery asp.net-mvc-4 jquery-ui twitter-bootstrap-3

根据标题,我的项目有一个主页索引,显示从数据层(使用Nhibernate的DLL)提供的员工数据表。表具有典型的添加新员工按钮和编辑/删除链接。我的_Layout引用了所有必要的包(CSS& JS),但为了清楚起见,我将显示链接供参考:

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>Empleados</title>
    <link href="/Content/bootstrap.css" rel="stylesheet"/>
<link href="/Content/bootstrap-datetimepicker.css" rel="stylesheet"/>
<link href="/Content/site.css" rel="stylesheet"/>

    <script src="/Scripts/modernizr-2.5.3.js"></script>


    <link href="/Content/themes/base/core.css" rel="stylesheet"/>
<link href="/Content/themes/base/resizable.css" rel="stylesheet"/>
<link href="/Content/themes/base/selectable.css" rel="stylesheet"/>
<link href="/Content/themes/base/accordion.css" rel="stylesheet"/>
<link href="/Content/themes/base/autocomplete.css" rel="stylesheet"/>
<link href="/Content/themes/base/button.css" rel="stylesheet"/>
<link href="/Content/themes/base/dialog.css" rel="stylesheet"/>
<link href="/Content/themes/base/slider.css" rel="stylesheet"/>
<link href="/Content/themes/base/tabs.css" rel="stylesheet"/>
<link href="/Content/themes/base/datepicker.css" rel="stylesheet"/>
<link href="/Content/themes/base/progressbar.css" rel="stylesheet"/>
<link href="/Content/themes/base/theme.css" rel="stylesheet"/>

    <script src="/Scripts/jquery-1.9.1.js"></script>

    <script src="/Scripts/jquery-ui-1.11.4.js"></script>

    <script src="/Scripts/jquery.validate-vsdoc.js"></script>
<script src="/Scripts/jquery.validate.js"></script>
<script src="/Scripts/jquery.validate.min.js"></script>
<script src="/Scripts/jquery.validate.unobtrusive.js"></script>
<script src="/Scripts/jquery.validate.unobtrusive.min.js"></script>
<script src="/Scripts/jquery.unobtrusive-ajax.js"></script>
<script src="/Scripts/jquery.unobtrusive-ajax.min.js"></script>
<script src="/Scripts/jquery.unobtrusive-ajax.min.js.map"></script>

    <script src="/Scripts/moment-with-locales.js"></script>
<script src="/Scripts/bootstrap.js"></script>
<script src="/Scripts/bootstrap-datetimepicker.js"></script>
<script src="/Scripts/bootbox.js"></script>

    <script src="/Scripts/site.js"></script>
</head>

_Layout with bundles:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")

    @Styles.Render("~/Content/themes/base/css")
    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/jqueryui")
    @Scripts.Render("~/bundles/jqueryval")
    @Scripts.Render("~/bundles/bootstrap")
    <script src="@Url.Content("~/Scripts/site.js")"></script>
</head>
<body>
    @RenderBody()

    @RenderSection("scripts", required: false)
</body>
</html>

Site.js只是一个脚本,用于配置样式和执行ajax表单提交,如下所示:

$(function () {
    /*  Define mecanismo de validación de fechas y sobreescribe el mecanismo por defecto definido.
        Sólo en el caso de las fechas, existe un problema de formatos debido a problemas en la cultura configurada.
        Esta definición es también útil con páginas que realizan validaciones con base en DataAnnotations definidos en el
        modelo y que ClientValidationEnabled=true y UnobtrusiveJavaScriptEnabled=true. Microsoft usa los recursos de validación
        propios de jquery-validate.js a través del uso de atributos data-*. Cuando se ingresa la fecha,
        Microsoft ejecuta la validación "date" propia de JQuery.validator y genera siempre un error del tipo:
        "El campo <nombrecampo> debe ser una fecha". Generando con esto que el ModelState.IsValid = false.
        Si se quitan los DataAnnotations que validan, la fecha se quedará sin ningún tipo de validación y tampoco
        es posible validar mediante javascript debido a que se ejecuta obligatoriamente el form action definido y por
        consiguiente, todas las validaciones necesarias se ejecutan, mostrando los errores.
        Debido a lo anterior, es necesario reescribir la validación date para hacerla personalizada y
        obligar a que cuando se genere el error descrito arriba, Microsoft marca el campo con la clase input-validation-error
        y si existe Html.ValidationMessageFor, coloca el texto del error en el span creado y que se asocia al campo
        mediante el atributo data-valmsg-for='nombrecampo' y agrega el error en el ModelState. Al eliminar el texto del error
        y la clase que marca el campo con error, la validación del campo dependerá del código definido aquí abajo.
        Observar como se quita la validación estandar de fechas usada por Microsoft en _Layout.cshtml.
        Este código es útil para formularios cuya validación se base en DataAnnotations. Las validaciones a nivel de
        javascript personalizadas ya manejan sus propios mecanismos de validación y este código no es necesario (Ver Create2.cshtml).*/
    $.validator.addMethod(
        'date', function (value, element, params) {
            if (this.optional(element)) {
                return true;
            };
            var result = false;
            try {
                $.datepicker.parseDate('dd/mm/yy', value);
                var name = element.name;
                $(element).closest('form').find("span[data-valmsg-for='" + element.name + "']").first().empty();
                $(element).removeClass('input-validation-error');
                result = true;
            } catch (err) {
                bootbox.alert('Se presentó error date validator: ' + err.message);
            }
            return result;
        }, ''
    );

    // cualquier resumen de validación (validation summary) se encapsula con alert y alert-danger
    $('.validation-summary-errors').each(function () {
        $(this).addClass('alert');
        $(this).addClass('alert-danger');
    });

    // Verifica cada form-group para ver si hay errores
    $('form').each(function () {
        $(this).find('div.form-group').each(function () {
            if ($(this).find('span.field-validation-error').length > 0) {
                $(this).addClass('has-error');
            }
        });
    });
});

function configuracion() {
    var formData = $('form');

    formData.submit(function () {
        if ($(this).valid()) {
            // Actualiza los campos de validación al hacer submit en la forma
            $(this).find('div.form-group').each(function () {
                if ($(this).find('span.field-validation-error').length == 0) {
                    $(this).removeClass('has-error');
                    $(this).addClass('has-success');
                }
            });

            bootbox.confirm('Está seguro?', function (result) {
                if (result === true) {
                    $.ajax({
                        url: formData.attr('action'),
                        type: formData.attr('method'),
                        data: formData.serialize()
                    })
                    .success(function (result) {
                        window.location.href = "/Home/Index";
                    })
                    .error(function (request, status, error) {
                        bootbox.dialog({
                            title: 'Error',
                            message: '<div class="row">' +
                                     '  <div class="col-md-12">' +
                                     '      <textarea rows="50" cols="50" style="overflow-y: scroll; height: 100px; width: 100%; resize: none;">' +
                                                'Se presentó error: ' + request.responseText +
                                     '      </textarea>' +
                                     '  </div>' +
                                     '</div>',
                            buttons: {
                                success: {
                                    label: 'Aceptar',
                                    className: 'btn btn-error'
                                }
                            }
                        });
                    });
                }
            });
        }
        else {
            // Actualiza los campos de validación si no hay datos validos
            $(this).find('div.form-group').each(function () {
                if ($(this).find('span.field-validation-error').length > 0) {
                    $(this).removeClass('has-success');
                    $(this).addClass('has-error');
                }
            });
            $('.validation-summary-errors').each(function () {
                if ($(this).hasClass('alert-danger') == false) {
                    $(this).addClass('alert');
                    $(this).addClass('alert-danger');
                }
            });
        }

        return false;
    });

    $('.date').each(function () {
        $(this).datetimepicker({
            locale: 'es', format: 'DD/MM/YYYY',
            minDate: new Date('01/01/1920'),
            maxDate: new Date()
        });
        $(this).tooltip({
            placement: 'top',
            trigger: 'focus',
            title: 'dd/mm/yyyy'
        });
        // Esto solo aplica cuando hay validaciones a nivel de DataAnnotations para quitar la validación
        // estandar de fechas:
        $(this).find('input').first().removeAttr('data-val-date');
    });

    // Seleccionar por defecto el primer item de los ComboBox y si existe opción por defecto --- Seleccione ---
    // entonces deshabilitar la posibilidad de que el usuario pueda seleccionarlo después de que haya seleccionado
    // otra opción.
    $('select').each(function () {
        var element = $(this).find('option').first();
        element.attr('selected', true);

        if (element.html() === "--- Seleccione ---")
            element.attr('disabled', true);
    });
    $('.form-group input').addClass('form-control');
    $('.form-group select').addClass('form-control');
}

var page = function () {
    alert("page");
    //Actualiza el validador
    $.validator.setDefaults({
        highlight: function (element) {
            $(element).closest('.form-group').addClass('has-error');
            $(element).closest('.form-group').removeClass('has-success');
        },
        unhighlight: function (element) {
            $(element).closest('.form-group').removeClass('has-error');
            $(element).closest('.form-group').addClass('has-success');
        }
    });
}();

Index.cshtml是:

@using Datos.Entities
@model IEnumerable<Empleado>

@{
    ViewBag.Title = "Empleados";
}

<div class="container">
    <div class="panel panel-primary">
        <div class="panel-heading"><h2>Empleados</h2></div>
        <div class="panel-body">
            <p>
                @Html.ActionLink("Crear Empleado", "Create", null, new { @class = "btn btn-info glyphicon-plus modal-link" })
            </p>
            <table class="table table-striped table-bordered table-hover table-condensed">
                <thead>
                    <tr class="info">
                        <th>
                            @Html.DisplayNameFor(model => model.Nombres)
                        </th>
                        <th>
                            @Html.DisplayNameFor(model => model.Apellidos)
                        </th>
                        <th>
                            @Html.DisplayNameFor(model => model.FechaNacimiento)
                        </th>
                        <th>
                            @Html.DisplayNameFor(model => model.TipoDocumento)
                        </th>
                        <th>
                            @Html.DisplayNameFor(model => model.NumeroDocumento)
                        </th>
                        <th></th>
                        <th></th>
                    </tr>
                </thead>

                <tbody>
                    @foreach (var item in Model) {
                        <tr>
                            <td>
                                @Html.DisplayFor(modelItem => item.Nombres)
                            </td>
                            <td>
                                @Html.DisplayFor(modelItem => item.Apellidos)
                            </td>
                            <td>
                                @Html.DisplayFor(modelItem => item.FechaNacimiento)
                            </td>
                            <td>
                                @Html.DisplayFor(modelItem => item.TipoDocumentoDTO.Descripcion)
                            </td>
                            <td>
                                @Html.DisplayFor(modelItem => item.NumeroDocumento)
                            </td>
                            <td>
                                @Html.ActionLink(" Editar", "Edit", new { id = item.Id }, new { @class = "btn glyphicon glyphicon-edit modal-link" })
                            </td>
                            <td>
                                @Html.ActionLink(" Eliminar", "Delete", new { id = item.Id }, new { @class = "btn glyphicon glyphicon-trash delete-link" })
                            </td>
                        </tr>
                    }
                </tbody>
            </table>
        </div>
    </div>
</div>

<div id="modal-container" class="modal fade" tabindex="-1" role="dialog">
</div>

<script type="text/javascript">
    $(function () {
        var container = $('#modal-container');

        container.on('show.bs.modal', function () {
            configuracion();
        });

        var dialog = $('.modal-dialog');

        $('.modal-link').click(function (e) {
            e.preventDefault();
            var url = $(this).attr('href');
            var request = $.get(url);
            request.success(function (result) {
                container.html(result);
                // backdrop: modo de operación del dialogo cuando se da click fuera de sus limites para evitar/permitir cerrar.
                // keyboard: modo de operación del dialogo cuando se presiona ESC para evitar/permitir cerrar.
                container.modal({ backdrop: 'static', keyboard: false });
                dialog.draggable({ handle: '.modal-header' });
            });
            request.fail(function (jqXHR, textStatus, errorThrown) {
                bootbox.alert(textStatus + ' : ' + errorThrown);
            });

            return false;
        });

        $('.delete-link').click(function (e) {
            e.preventDefault();
            var link = $(this);
            bootbox.confirm('Está seguro?', function (result) {
                if (result === true) {
                    var url = link.attr('href');
                    $.ajax({
                        url: url,
                        type: 'DELETE',
                        success: function () {
                            window.location.href = '/Home/Index';
                        },
                        error: function (jqXHR, textStatus, errorThrown) {
                            bootbox.alert(textStatus + ' : ' + errorThrown);
                        }
                    });
                }
            });
        });
    });
</script>

Create.cshtml是:

@using Datos.Entities
@model Empleado

<div class="modal-dialog modal-md">
    <div class="modal-content">
        <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
            <h4 class="modal-title">Empleado</h4>
        </div>
        <div class="modal-body">
            @using (Ajax.BeginForm("Create", "Home", new AjaxOptions() { HttpMethod = "post", OnSuccess = "configuracion" }, new { @class = "form-horizontal", id = "frmData", role = "form" }))
            {
                @Html.AntiForgeryToken()
                @Html.HiddenFor(model => model.Id)
                <div class="form-group">
                    @Html.LabelFor(model => model.Nombres, new { @class = "col-md-4 control-label" })
                    <div class="col-md-8">
                        @Html.EditorFor(model => model.Nombres)
                        @Html.ValidationMessageFor(model => model.Nombres)
                    </div>
                </div>

                <div class="form-group">
                    @Html.LabelFor(model => model.Apellidos, new { @class = "col-md-4 control-label" })
                    <div class="col-md-8">
                        @Html.EditorFor(model => model.Apellidos)
                        @Html.ValidationMessageFor(model => model.Apellidos)
                    </div>
                </div>

                <div class="form-group">
                    @Html.LabelFor(model => model.FechaNacimiento, new { @class = "col-md-4 control-label" })
                    <div class="col-md-8">
                        <div class="input-group date">
                            @Html.TextBoxFor(model => model.FechaNacimiento)
                            <span class="input-group-addon">
                                <span class="glyphicon glyphicon-calendar"></span>
                            </span>
                        </div>
                        @Html.ValidationMessageFor(model => model.FechaNacimiento)
                    </div>
                </div>

                <div class="form-group">
                    @Html.LabelFor(model => model.TipoDocumento, new { @class = "col-md-4 control-label" })
                    <div class="col-md-8">
                        @Html.DropDownListFor(model => model.TipoDocumento, new SelectList(ViewBag.TiposDocumento, "Id", "Descripcion", 0), "--- Seleccione ---")
                        @Html.ValidationMessageFor(model => model.TipoDocumento)
                    </div>
                </div>

                <div class="form-group">
                    @Html.LabelFor(model => model.NumeroDocumento, new { @class = "col-md-4 control-label" })
                    <div class="col-md-8">
                        @Html.EditorFor(model => model.NumeroDocumento)
                        @Html.ValidationMessageFor(model => model.NumeroDocumento)
                    </div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-default" data-dismiss="modal" aria-hidden="true">Cerrar</button>
                    <button type="submit" class="btn btn-primary" id="submit">Grabar</button>
                </div>
            }
        </div>
    </div>
</div>

最后,Model Empleado是:

public class Empleado
{
    [DefaultValue(0)]
    public virtual int Id { get; set; }

    [Display(Name="Nombres", Prompt="Nombres")]
    [Required]
    public virtual string Nombres { get; set; }

    [Display(Name="Apellidos", Prompt="Apellidos")]
    [Required]
    public virtual string Apellidos { get; set; }

    [Display(Name="Fecha Nacimiento")]
    [DisplayFormat(ApplyFormatInEditMode=true,DataFormatString="{0:dd/MM/yyyy}")]
    [Required]
    public virtual DateTime FechaNacimiento { get; set; }

    [Display(Name = "Tipo de Documento")]
    [Required]
    [Range(1, 3, ErrorMessage = "El tipo de documento no es válido")]
    [DefaultValue(0)]
    public virtual int TipoDocumento { get; set; }

    [Display(Name = "Número de Documento")]
    [Required]
    public virtual string NumeroDocumento { get; set; }

    public virtual TipoDocumento TipoDocumentoDTO { get; set; }

    public Empleado()
    {
        FechaNacimiento = DateTime.Now;
        TipoDocumentoDTO = new TipoDocumento();
    }
}

Web.config有:

<appSettings>
    <add key="ClientValidationEnabled" value="true"/> 
    <add key="UnobtrusiveJavaScriptEnabled" value="true"/> 
</appSettings>

基本上索引显示数据,当我点击Crear Empleado链接时,通过调用Controller返回一个PartialView(Create.cshtml)并显示,将一个bootstrap模式对话框加载到modal-container中。一切都运行正常,boostrap样式工作完美,但是当我点击Save(Grabar)时,执行form.submit(site.js中的脚本):

function configuracion() {
        var formData = $('form');

        formData.submit(function () {
            if ($(this).valid()) {   

DataAnnotations验证不起作用$(this).valid()始终为true,不会发生错误!我试过这样做:

<div id="modal-container" class="modal fade" tabindex="-1" role="dialog">
    Html.Partial("~\views\home\create.cshtml", new Empleado())
</div>

通过这种方式,似乎数据验证工作,但我需要使用ajax.submit。请帮忙!!!

1 个答案:

答案 0 :(得分:1)

这是因为您通过ajax调用视图。不显眼的验证在加载时解析页面,但在第一次加载内容后调用表单时不包括元素。

您需要调用验证程序对象中公开的$.validator.unobtrusive.parse方法。

当您调用局部视图完成并显示模态时,只需调用

$.validator.unobtrusive.parse("#modal-container");

之后,您的验证和数据注释将正常工作。

如果您需要解析所有表单,请执行以下操作:

$.validator.unobtrusive.parse("form");