下拉列表的远程功能和验证不适用于弹出模式
Remote function and validation for dropdownlistfor not working in pop-up modal
当我没有为 create/edit 操作使用弹出模式时一切正常,并且在我更新我的代码并且已经使用弹出模式之后,远程功能没有触发并且 dropdownlistfor 的验证是无法正常工作。
模态显示代码:
<a class="btn btn-sm btn-success btn-flat" onclick="showModal('@Url.Action("CreateOrEditProduct", "Purchases", new { pid=Model.Id, pn=Model.PurchaseNumber },
Context.Request.Scheme)', 'Add Product', '#productForm')"><i class="fas fa-plus"></i> Add Product</a>
控制器中的代码:
public async Task<IActionResult> CreateOrEditProduct(int id = 0, int pid = 0, string pn = "")
{
PurchaseDetailViewModel vmodel = new PurchaseDetailViewModel();
if (id == 0)
{
/*create view code here*/
}
else
{
/*edit view code here*/
}
vmodel.Products = ProductList();
vmodel.Suppliers = SupplierList();
return View(vmodel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> CreateOrEditProduct(int id, PurchaseDetailViewModel purchaseDetail)
{
purchaseDetail.Products = ProductList();
purchaseDetail.Suppliers = SupplierList();
if (ModelState.IsValid)
{
var vmodel = _mapper.Map<PurchaseDetail>(purchaseDetail);
if (id == 0)
{
/*create code here*/
}
else
{
/*edit code here*/
}
return Json(new { isValid = true, html = this.RenderViewAsync("Details", purchaseDetail) });
}
return Json(new
{
isValid = false,
html = this.RenderViewAsync("CreateOrEditProduct", purchaseDetail)
});
}
表单代码:
<form id="productForm" asp-action="CreateOrEditProduct" onsubmit="return jQueryAjaxPost(this);" autocomplete="off">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
@Html.HiddenFor(Model => Model.PurchaseId)
@Html.HiddenFor(Model => Model.PurchaseNumber)
<div class="form-group">
<label asp-for="ProductId" class="control-label"></label>
@Html.DropDownListFor(x => Model.ProductId, Model.Products, "**Please select**",
htmlAttributes: new { @class = "form-control", @id = "Product" })
@Html.ValidationMessageFor(Model => Model.ProductId, "", new { @class = "text-danger" })
</div>
/*some other inputs here*/
<div class="form-group">
<button type="submit" class="btn btn-success btn-flat"><i class="far fa-check-circle"></i> Save</button>
<a class="btn btn-danger btn-flat" asp-controller="Purchases" asp-action="Details" asp-route-id="@Model.PurchaseId"><i class="far fa-times-circle"></i> Cancel</a>
</div>
</form>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
Ajax 在此处显示表格和 post 代码:
showModal = (url, title, formid) => {
$.ajax({
type: 'GET',
url: url,
success: function (res) {
//$.validator.unobtrusive.parse(formid);
$('#form-modal .modal-body').html(res);
$('#form-modal .modal-title').html(title);
$('#form-modal').modal('show');
}
})
}
jQueryAjaxPost = form => {
try {
$.ajax({
type: 'POST',
url: form.action,
data: new FormData(form),
contentType: false,
processData: false,
success: function (res) {
console.log(res);
if (res.isValid) {
//$('#view-all').html(res.html)
$('#form-modal .modal-body').html('');
$('#form-modal .modal-title').html('');
$('#form-modal').modal('hide');
}
else
$('#form-modal .modal-body').html(res.html.result);
},
error: function (err) {
console.log(err)
}
})
//to prevent default form submit event
return false;
} catch (ex) {
console.log(ex)
}
}
代码下拉列表:
private IList<SelectListItem> ProductList()
{
var list = _context.Product.Select(x => new SelectListItem { Text = x.ProductName, Value = x.Id.ToString() }).ToList();
return list;
}
控制器扩展:
public static class ControllerExtensions
{
public static async Task<string> RenderViewAsync<TModel>(this Controller controller, string viewName, TModel model, bool partial = false)
{
if (string.IsNullOrEmpty(viewName))
{
viewName = controller.ControllerContext.ActionDescriptor.ActionName;
}
controller.ViewData.Model = model;
using (var writer = new StringWriter())
{
IViewEngine viewEngine = controller.HttpContext.RequestServices.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine;
ViewEngineResult viewResult = viewEngine.FindView(controller.ControllerContext, viewName, !partial);
if (viewResult.Success == false)
{
return $"A view with the name {viewName} could not be found";
}
ViewContext viewContext = new ViewContext(
controller.ControllerContext,
viewResult.View,
controller.ViewData,
controller.TempData,
writer,
new HtmlHelperOptions()
);
await viewResult.View.RenderAsync(viewContext);
return writer.GetStringBuilder().ToString();
}
}
}
查看模型代码,并且已经检查远程功能在没有模态的情况下工作
public class PurchaseDetailViewModel
{
public int Id { get; set; }
[Display(Name ="Product"), Required]
[Remote("CheckPurchaseProduct", "Purchases", AdditionalFields = "PurchaseId", ErrorMessage = "Product already exists.")]
public int ProductId { get; set; }
public IList<SelectListItem> Products { get; set; }
/*other fields here*/
}
没有模态的示例输出:
Output without modal
模态输出示例:
Output with modal
我已经搜索了几个小时,但无法解决问题
你的 razor 视图中的模式似乎没有使用 Layout.If 所以你不能在那个 razor 视图中使用 @section Script{}
。
这是我的简单演示,您可以查看:
1.Index.cshtml:
@model PurchaseDetailViewModel
<a class="btn btn-sm btn-success btn-flat" onclick="showModal('@Url.Action("CreateOrEditProduct", "Home", new { pid=Model.Id, pn=Model.PurchaseNumber })')">open</a>
<div class="modal fade" id="form-modal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close" onclick="Close()">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body" id="details">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" onclick="Close()">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
@section Scripts {
<script>
showModal = (url, title, formid) => {
$.ajax({
type: 'GET',
url: url,
success: function (res) {
$('#form-modal .modal-body').html(res);
$('#form-modal').modal('show');
}
})
}
jQueryAjaxPost = form => {
try {
//...
})
//to prevent default form submit event
return false;
} catch (ex) {
console.log(ex)
}
}
</script>
}
2.CreateOrEditProduct.cshtml:
@model PurchaseDetailViewModel
@{
Layout = null;
}
<form id="productForm" asp-action="CreateOrEditProduct" onsubmit="return jQueryAjaxPost(this);" autocomplete="off">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
@Html.HiddenFor(Model => Model.PurchaseId)
@Html.HiddenFor(Model => Model.PurchaseNumber)
<div class="form-group">
<label asp-for="ProductId" class="control-label"></label>
@Html.DropDownListFor(x => Model.ProductId, Model.Products, "**Please select**",
htmlAttributes: new { @class = "form-control", @id = "Product" })
@Html.ValidationMessageFor(Model => Model.ProductId, "", new { @class = "text-danger" })
</div>
<div class="form-group">
<button type="submit" class="btn btn-success btn-flat"><i class="far fa-check-circle"></i> Save</button>
<a class="btn btn-danger btn-flat" asp-controller="Purchases" asp-action="Details" asp-route-id="@Model.PurchaseId"><i class="far fa-times-circle"></i> Cancel</a>
</div>
</form>
@{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
3.Controller:
public class HomeController : Controller
{
private IList<SelectListItem> ProductList()
{
var data = new List<dynamic> {
new { ProductName ="aaa", Id =1},
new { ProductName ="bbb", Id =2},
new { ProductName ="ccc", Id =3}
};
var list = data.Select(x => new SelectListItem { Text = x.ProductName, Value = x.Id.ToString() }).ToList();
return list;
}
public IActionResult Index()
{
var model = new PurchaseDetailViewModel()
{
Id = 1,
PurchaseNumber = 1001
};
return View(model);
}
public async Task<IActionResult> CreateOrEditProduct(int id = 0, int pid = 0, string pn = "")
{
PurchaseDetailViewModel vmodel = new PurchaseDetailViewModel();
if (id == 0)
{
/*create view code here*/
}
else
{
/*edit view code here*/
}
vmodel.Products = ProductList();
return View(vmodel);
}
[AcceptVerbs("GET", "POST")]
public IActionResult CheckPurchaseProduct()
{
return Json(false);
}
}
4.Result:
当我没有为 create/edit 操作使用弹出模式时一切正常,并且在我更新我的代码并且已经使用弹出模式之后,远程功能没有触发并且 dropdownlistfor 的验证是无法正常工作。
模态显示代码:
<a class="btn btn-sm btn-success btn-flat" onclick="showModal('@Url.Action("CreateOrEditProduct", "Purchases", new { pid=Model.Id, pn=Model.PurchaseNumber },
Context.Request.Scheme)', 'Add Product', '#productForm')"><i class="fas fa-plus"></i> Add Product</a>
控制器中的代码:
public async Task<IActionResult> CreateOrEditProduct(int id = 0, int pid = 0, string pn = "")
{
PurchaseDetailViewModel vmodel = new PurchaseDetailViewModel();
if (id == 0)
{
/*create view code here*/
}
else
{
/*edit view code here*/
}
vmodel.Products = ProductList();
vmodel.Suppliers = SupplierList();
return View(vmodel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> CreateOrEditProduct(int id, PurchaseDetailViewModel purchaseDetail)
{
purchaseDetail.Products = ProductList();
purchaseDetail.Suppliers = SupplierList();
if (ModelState.IsValid)
{
var vmodel = _mapper.Map<PurchaseDetail>(purchaseDetail);
if (id == 0)
{
/*create code here*/
}
else
{
/*edit code here*/
}
return Json(new { isValid = true, html = this.RenderViewAsync("Details", purchaseDetail) });
}
return Json(new
{
isValid = false,
html = this.RenderViewAsync("CreateOrEditProduct", purchaseDetail)
});
}
表单代码:
<form id="productForm" asp-action="CreateOrEditProduct" onsubmit="return jQueryAjaxPost(this);" autocomplete="off">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
@Html.HiddenFor(Model => Model.PurchaseId)
@Html.HiddenFor(Model => Model.PurchaseNumber)
<div class="form-group">
<label asp-for="ProductId" class="control-label"></label>
@Html.DropDownListFor(x => Model.ProductId, Model.Products, "**Please select**",
htmlAttributes: new { @class = "form-control", @id = "Product" })
@Html.ValidationMessageFor(Model => Model.ProductId, "", new { @class = "text-danger" })
</div>
/*some other inputs here*/
<div class="form-group">
<button type="submit" class="btn btn-success btn-flat"><i class="far fa-check-circle"></i> Save</button>
<a class="btn btn-danger btn-flat" asp-controller="Purchases" asp-action="Details" asp-route-id="@Model.PurchaseId"><i class="far fa-times-circle"></i> Cancel</a>
</div>
</form>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
Ajax 在此处显示表格和 post 代码:
showModal = (url, title, formid) => {
$.ajax({
type: 'GET',
url: url,
success: function (res) {
//$.validator.unobtrusive.parse(formid);
$('#form-modal .modal-body').html(res);
$('#form-modal .modal-title').html(title);
$('#form-modal').modal('show');
}
})
}
jQueryAjaxPost = form => {
try {
$.ajax({
type: 'POST',
url: form.action,
data: new FormData(form),
contentType: false,
processData: false,
success: function (res) {
console.log(res);
if (res.isValid) {
//$('#view-all').html(res.html)
$('#form-modal .modal-body').html('');
$('#form-modal .modal-title').html('');
$('#form-modal').modal('hide');
}
else
$('#form-modal .modal-body').html(res.html.result);
},
error: function (err) {
console.log(err)
}
})
//to prevent default form submit event
return false;
} catch (ex) {
console.log(ex)
}
}
代码下拉列表:
private IList<SelectListItem> ProductList()
{
var list = _context.Product.Select(x => new SelectListItem { Text = x.ProductName, Value = x.Id.ToString() }).ToList();
return list;
}
控制器扩展:
public static class ControllerExtensions
{
public static async Task<string> RenderViewAsync<TModel>(this Controller controller, string viewName, TModel model, bool partial = false)
{
if (string.IsNullOrEmpty(viewName))
{
viewName = controller.ControllerContext.ActionDescriptor.ActionName;
}
controller.ViewData.Model = model;
using (var writer = new StringWriter())
{
IViewEngine viewEngine = controller.HttpContext.RequestServices.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine;
ViewEngineResult viewResult = viewEngine.FindView(controller.ControllerContext, viewName, !partial);
if (viewResult.Success == false)
{
return $"A view with the name {viewName} could not be found";
}
ViewContext viewContext = new ViewContext(
controller.ControllerContext,
viewResult.View,
controller.ViewData,
controller.TempData,
writer,
new HtmlHelperOptions()
);
await viewResult.View.RenderAsync(viewContext);
return writer.GetStringBuilder().ToString();
}
}
}
查看模型代码,并且已经检查远程功能在没有模态的情况下工作
public class PurchaseDetailViewModel
{
public int Id { get; set; }
[Display(Name ="Product"), Required]
[Remote("CheckPurchaseProduct", "Purchases", AdditionalFields = "PurchaseId", ErrorMessage = "Product already exists.")]
public int ProductId { get; set; }
public IList<SelectListItem> Products { get; set; }
/*other fields here*/
}
没有模态的示例输出:
Output without modal
模态输出示例:
Output with modal
我已经搜索了几个小时,但无法解决问题
你的 razor 视图中的模式似乎没有使用 Layout.If 所以你不能在那个 razor 视图中使用 @section Script{}
。
这是我的简单演示,您可以查看:
1.Index.cshtml:
@model PurchaseDetailViewModel
<a class="btn btn-sm btn-success btn-flat" onclick="showModal('@Url.Action("CreateOrEditProduct", "Home", new { pid=Model.Id, pn=Model.PurchaseNumber })')">open</a>
<div class="modal fade" id="form-modal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close" onclick="Close()">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body" id="details">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" onclick="Close()">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
@section Scripts {
<script>
showModal = (url, title, formid) => {
$.ajax({
type: 'GET',
url: url,
success: function (res) {
$('#form-modal .modal-body').html(res);
$('#form-modal').modal('show');
}
})
}
jQueryAjaxPost = form => {
try {
//...
})
//to prevent default form submit event
return false;
} catch (ex) {
console.log(ex)
}
}
</script>
}
2.CreateOrEditProduct.cshtml:
@model PurchaseDetailViewModel
@{
Layout = null;
}
<form id="productForm" asp-action="CreateOrEditProduct" onsubmit="return jQueryAjaxPost(this);" autocomplete="off">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
@Html.HiddenFor(Model => Model.PurchaseId)
@Html.HiddenFor(Model => Model.PurchaseNumber)
<div class="form-group">
<label asp-for="ProductId" class="control-label"></label>
@Html.DropDownListFor(x => Model.ProductId, Model.Products, "**Please select**",
htmlAttributes: new { @class = "form-control", @id = "Product" })
@Html.ValidationMessageFor(Model => Model.ProductId, "", new { @class = "text-danger" })
</div>
<div class="form-group">
<button type="submit" class="btn btn-success btn-flat"><i class="far fa-check-circle"></i> Save</button>
<a class="btn btn-danger btn-flat" asp-controller="Purchases" asp-action="Details" asp-route-id="@Model.PurchaseId"><i class="far fa-times-circle"></i> Cancel</a>
</div>
</form>
@{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
3.Controller:
public class HomeController : Controller
{
private IList<SelectListItem> ProductList()
{
var data = new List<dynamic> {
new { ProductName ="aaa", Id =1},
new { ProductName ="bbb", Id =2},
new { ProductName ="ccc", Id =3}
};
var list = data.Select(x => new SelectListItem { Text = x.ProductName, Value = x.Id.ToString() }).ToList();
return list;
}
public IActionResult Index()
{
var model = new PurchaseDetailViewModel()
{
Id = 1,
PurchaseNumber = 1001
};
return View(model);
}
public async Task<IActionResult> CreateOrEditProduct(int id = 0, int pid = 0, string pn = "")
{
PurchaseDetailViewModel vmodel = new PurchaseDetailViewModel();
if (id == 0)
{
/*create view code here*/
}
else
{
/*edit view code here*/
}
vmodel.Products = ProductList();
return View(vmodel);
}
[AcceptVerbs("GET", "POST")]
public IActionResult CheckPurchaseProduct()
{
return Json(false);
}
}
4.Result: