根据标题,我的项目有一个主页索引,显示从数据层(使用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">×</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。请帮忙!!!
答案 0 :(得分:1)
这是因为您通过ajax调用视图。不显眼的验证在加载时解析页面,但在第一次加载内容后调用表单时不包括元素。
您需要调用验证程序对象中公开的$.validator.unobtrusive.parse
方法。
当您调用局部视图完成并显示模态时,只需调用
$.validator.unobtrusive.parse("#modal-container");
之后,您的验证和数据注释将正常工作。
如果您需要解析所有表单,请执行以下操作:
$.validator.unobtrusive.parse("form");