首页 > 解决方案 > asp.net core 2.1 和 Razor 页面中的模态表单验证

问题描述

我有一个 Details.cshtml 页面,显示客户的详细信息以及 customer.sites 的链接列表。在页面上,我创建了一个按钮来添加一个新站点,该站点打开一个引导模式,其中包含用于进入新站点的表单。

我遇到的问题是验证。如果表单无效,则页面重新加载,但模式再次隐藏,这意味着除非用户再次单击添加站点按钮,否则看不到 asp-validation-for 文本。有什么办法可以使 ModelState.IsValid 无效时模态自动保持打开状态?

details.cshtml 页面

@page "{id:int?}"
@model ServiceManager.Pages.Customers.DetailsModel

@{
    ViewData["Title"] = "Details";
}
<h2>@Html.DisplayFor(model => model.Customer.Name)</h2>
<hr />
<!-- Modal -->
<div class="modal fade" tabindex="-1" role="dialog" aria-hidden="true" id="createSiteModal">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">Add New Customer Site</h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            <div class="modal-body">
                <form method="post">
                    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
                    <div class="form-group">
                        <label asp-for="Site.Description" class="control-label"></label>
                        <input asp-for="Site.Description" class="form-control" />
                        <span asp-validation-for="Site.Description" class="text-danger"></span>
                    </div>
                    <div class="form-group">
                        <label asp-for="Site.AddressLine" class="control-label"></label>
                        <textarea asp-for="Site.AddressLine" class="form-control"></textarea>
                        <span asp-validation-for="Site.AddressLine" class="text-danger"></span>
                    </div>
                    <div class="form-group">
                        <label asp-for="Site.City" class="control-label"></label>
                        <input asp-for="Site.City" class="form-control" />
                        <span asp-validation-for="Site.City" class="text-danger"></span>
                    </div>
                    <div class="form-group">
                        <label asp-for="Site.County" class="control-label"></label>
                        <input asp-for="Site.County" class="form-control" />
                        <span asp-validation-for="Site.County" class="text-danger"></span>
                    </div>
                    <div class="form-group">
                        <label asp-for="Site.Postcode" class="control-label"></label>
                        <input asp-for="Site.Postcode" class="form-control" />
                        <span asp-validation-for="Site.Postcode" class="text-danger"></span>
                    </div>
                    <div class="form-group">
                        <label asp-for="Site.Country" class="control-label"></label>
                        <input asp-for="Site.Country" class="form-control" />
                        <span asp-validation-for="Site.Country" class="text-danger"></span>
                    </div>
                    <div class="form-group">
                        <label asp-for="Site.Telephone" class="control-label"></label>
                        <input asp-for="Site.Telephone" class="form-control" />
                        <span asp-validation-for="Site.Telephone" class="text-danger"></span>
                    </div>
                    <div class="form-group">
                        <label asp-for="Site.Fax" class="control-label"></label>
                        <input asp-for="Site.Fax" class="form-control" />
                        <span asp-validation-for="Site.Fax" class="text-danger"></span>
                    </div>
                    <div class="form-group">
                        <input asp-for="Site.CommercialID" class="form-control" value="3" />
                    </div>
                    <div class="form-group">
                        <input type="button" value="Cancel" class="btn btn-danger" data-dismiss="modal" />
                        <input type="submit" value="Create" class="btn btn-success" />
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
<div class="btn-group" role="group">
    <a class="btn btn-light" data-toggle="tooltip" data-placement="top" data-delay="200" title="Back to Customer List" asp-page="./Index">
        <span class="fas fa-arrow-left fa-1x" style="color:#007bff"></span>
    </a>
    @if (Model.Customer.CustomerType == "Domestic")
    {
        <a class="btn btn-light" asp-page="./EditDom" asp-route-id="@Model.Customer.ID" data-toggle="tooltip" data-placement="top" data-delay="200" title="Edit Customer">
            <span class="fas fa-user-edit fa-1x" style="color:#0c0cbe"></span>
        </a>
    }
    else
    {
        <a class="btn btn-light" asp-page="./Edit" asp-route-id="@Model.Customer.ID" data-toggle="tooltip" data-placement="top" data-delay="200" title="Edit Customer">
            <span class="fas fa-user-edit fa-1x" style="color:#0c0cbe"></span>
        </a>
    }
        <a class="btn btn-light" asp-page="./Delete" asp-route-id="@Model.Customer.ID" data-toggle="tooltip" data-placement="top" data-delay="200" title="Delete Customer">
            <span class="fas fa-user-times fa-1x" style="color:#b01111"></span>
        </a>
</div>
<div class="row">
    <div class="col-lg-4">
        <div class="card border-info mb-3">
            <h5 class="card-header bg-info text-white">Customer Details</h5>
            <div class="card-body">
                <dl class="row">
                    <dt class="col-5">
                        @Html.DisplayNameFor(model => model.Customer.AccountRef)
                    </dt>
                    <dd class="col-7">
                        @Html.DisplayFor(model => model.Customer.AccountRef)
                    </dd>
                    <dt class="col-5">
                        @Html.DisplayNameFor(model => model.Customer.ContactDate)
                    </dt>
                    <dd class="col-7">
                        @Html.DisplayFor(model => model.Customer.ContactDate)
                    </dd>
                    <dt class="col-5">
                        @Html.DisplayNameFor(model => model.Customer.Active)
                    </dt>
                    <dd class="col-7">
                        @Html.DisplayFor(model => model.Customer.Active)
                    </dd>
                </dl>
            </div>
        </div>
    </div>
    <div class="col-lg-4">
        <div class="card border-info mb-3">
            <h5 class="card-header bg-info text-white">Customer Sites</h5>
            <table class="table table-hover fixed-table">
                <tr>
                    <th>Description</th>
                    <th>Location</th>
                    <th>Telephone</th>
                </tr>
                @foreach (var item in Model.Customer.Sites)
                {
                    <tr>
                        <td>
                            @Html.DisplayFor(modelItem => item.Description)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.City)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.Telephone)
                        </td>
                        <td>
                            <a class="rowlink" asp-page="/Sites/Details" asp-route-id="@item.ID"><i class="fa fa-chevron-right"></i></a>
                        </td>
                    </tr>
                }
            </table>
            <div class="card-footer">
                <!-- Button trigger modal -->
                <button class="btn btn-link float-right" data-toggle="modal" data-target="#createSiteModal">
                    <span class="fa-stack fa-lg">
                        <i class="fas fa-circle fa-stack-2x" style="color:tomato"></i>
                        <i class="fas fa-stack-1x fa-inverse">+</i>
                    </span>
                </button>
                <div class="clearfix"></div>
            </div>
        </div>
    </div>
</div>

详细信息.cshtml.cs 页面

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using ServiceManager.Models;

namespace ServiceManager.Pages.Customers
{
    public class DetailsModel : PageModel
    {
        private readonly CompanyContext _context;

        public DetailsModel(CompanyContext context)
        {
            _context = context;
        }

        public Commercial Customer { get; set; }


        public async Task<IActionResult> OnGetAsync(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            Customer = await _context.Commercial
                .Include(c => c.Sites)
                .Include(c => c.Contacts)
                .FirstOrDefaultAsync(m => m.ID == id);

            if (Customer == null)
            {
                return NotFound();
            }
            return Page();
        }

        [BindProperty]
        public Address Site { get; set; }

        public async Task<IActionResult> OnPostAsync(int? id)
        {

            if (!ModelState.IsValid)
            {
                Customer = await _context.Commercial
                .Include(c => c.Sites)
                .Include(c => c.Contacts)
                .FirstOrDefaultAsync(m => m.ID == id);

                return Page();
            }
            _context.Address.Add(Site);
            await _context.SaveChangesAsync();
            return RedirectToPage("./Index");  
        }
    }
}

标签: c#jqueryasp.net-coreasp.net-core-mvc

解决方案


One way to address this issue is to perform client side validation using jQuery before you submit the form. That way the modal stays open and the user gets instant feedback on what the errors are.

Another alternative and perhaps less invasive in this case, is to pass a flag from the server back to the client with the status of the model validation and then open the modal based on the value of such flag e.g.

Controller:

   public async Task<IActionResult> OnPostAsync(int? id)
    {

        if (!ModelState.IsValid)
        {
            Customer = await _context.Commercial
            .Include(c => c.Sites)
            .Include(c => c.Contacts)
            .FirstOrDefaultAsync(m => m.ID == id);
            ViewData["ModelIsValid"] = false;
            return Page();
        }
        ViewData["ModelIsValid"] = true;
        _context.Address.Add(Site);
        await _context.SaveChangesAsync();
        return RedirectToPage("./Index");  
    }

View:

<script>
    $(document).ready(function () {
        @if(!(bool)ViewData["ModelIsValid"])
        {
            $("#createSiteModal").modal('show');
        }
    });
</script>

The main drawback of the second approach is that the modal will popup after the page has rendered instead of staying open.


推荐阅读