Помощники тега проверки пользовательской модели на стороне сервера не отображаются через unobtrusiveJS в ViewComponent

У меня есть собственный атрибут проверки, который я сделал для группы флажков, расположенных внутри ViewComponent. До того, как я переместил его в ViewComponent, ненавязчивое сообщение проверки отлично работало через asp-validation-for, но теперь сообщение там не появляется.

В то же время я собираю ошибки проверки на стороне сервера и передаю их обратно в представление, и это работает так, как ожидалось: поэтому я получаю сообщение об ошибке, которое я сделал через пользовательский атрибут, он просто не показывает сообщение в .

Вот код (я упрощаю его, где могу, дайте мне знать, если вам нужна дополнительная информация. Я извиняюсь, если что-то упустил.):

Контроллер:

public IActionResult Enrollment(EnrollmentViewModel enrollmentVM)
        {
            return View("~/Enrollment.cshtml", enrollmentVM);
        }


public EnrollmentViewModel SetModelListItems(EnrollmentViewModel enrollmentVM)
        {
            if (enrollmentVM.Preferences == null)
            {
                var preferencesList = preferences.GetAll().ToList();
                enrollmentVM.Preferences = preferences.Select(x => new SelectListItem
                {
                    Text = x.PreferencesName,
                    Value = x.PreferenceseId.ToString()
                }).ToList();
    return enrollmentVM;
    }

public async Task<IViewComponentResult> InvokeAsync(EnrollmentViewModel enrollmentVM)
        {
 
            enrollmentVM = SetModelListItems(enrollmentVM);
                return new ViewViewComponentResult()
            {
                ViewData = new ViewDataDictionary<EnrollmentViewModel>(ViewData, enrollmentVM)
            };

Основной вид:

@model EnrollmentViewModel;

@{
    Layout = "_Layout.cshtml";
}
<button type="submit" id="EnrollForm" form="EnrollForm">
<span>Save</span>
</button>
<div>
    @await Component.InvokeAsync("EnrollClient", new { enrollmentVM = @Model });                              
</div>

@section Scripts
{
    @if (Model.HasErrors)
    {

        @foreach (string errorMessage in Model.SubmitErrors)
        {
            <script type="text/javascript">
                toastr["error"]("@errorMessage");
            </script>
        }
}

ВидКомпонент:

@model EnrollmentViewModel

@using (Html.BeginForm("EnrollClient", "Enrollment", FormMethod.Post, new { enctype = "multipart/form-data", id = "EnrollForm" }))
{
    <fieldset>
     <legend>Preferences (select all that apply)</legend>
     @for (var i = 0; i < Model.Preferences.Count(); i++)
     {
    <div>
        <input type="checkbox" asp-for="@Model.Preferences[i].Selected">
        <label asp-for="@Model.Preferences[i].Selected">@Model.Races[i].Text</label>
        <input type="hidden" asp-for="@Model.Preferences[i].Value" />
        <input type="hidden" asp-for="@Model.Preferences[i].Text" />
  </div>
}
  <span asp-validation-for="Preferences" class="text-danger"></span>
</fieldset>

Почтовый метод:

public IActionResult EnrollClient(EnrollmentViewModel viewModel)
        {
            try
            {
                if (!ModelState.IsValid)
                {
                    viewModel = SetModelListItems(viewModel);
                    viewModel.HasErrors = true;
                    viewModel.SubmitErrors = ModelState.Values
                        .SelectMany(state => state.Errors)
                        .Select(error => error.ErrorMessage);
                    return View("~/Enrollment.cshtml", viewModel);
                }
                SaveForm();
            }

//....
        }

ViewModel с атрибутом:

public class EnrollmentViewModel
    {
    
        [ListItemSelected(ErrorMessage = "Please select at least one Preference option.")]
        public List<SelectListItem> Preferences { get; set; }
    }
    
public class ListItemSelectedAttribute : ValidationAttribute
    {
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            if (value != null)
            {
                if ((value as List<SelectListItem>).Count(x => x.Selected) == 0)
                    return new ValidationResult(ErrorMessage);
            }

            return ValidationResult.Success;
        }
    }

Я чувствую, что это много кода, но, надеюсь, большая его часть была необходима, чтобы объяснить мою проблему... Я сильно сократил ее. Скрипты jquery, проверки и ненавязчивые сценарии находятся в файле _Layout.cshtml. Большое спасибо!

ИЗМЕНИТЬ:

Я поставил точку останова в ViewComponent и в файлах Main View, и в обоих случаях на вкладке Locals ViewContext показывает, что ModelState недействителен, а ValidationMessageElement = span! Я думаю, это не то, что должно быть?? Я нашел сообщение, рекомендующее добавить ViewContext.FormContext = new FormContext(); вверху, но с ним или без него элементы FormContext одинаковы.


comment
чтобы быть уверенным, вы пытались поставить точку останова в файле представления ViewComponent, чтобы проверить его ModelState? Там должны быть ошибки проверки, иначе хелпер тега не работает для asp-validation-for. Кстати, я немного сомневаюсь, что это asp-for="@Model.Preferences[i].Selected" отобразит соответствующее имя Selected. Не могли бы вы проверить свою веб-страницу (используя инструмент инспектора браузера), чтобы увидеть, каковы фактические имена, сгенерированные для ваших входных данных? Однако asp-validation-for выглядит правильно.   -  person King King    schedule 12.03.2021
comment
@KingKing Я не знал, как это сделать (спасибо). Я сделал это, и ModelState недействителен, а ValidationMessageElement = span! Я думаю, это не то, что должно быть?? Я погуглил и нашел сообщение, рекомендующее добавить ViewContext.FormContext = new FormContext(); вверху, но с ним или без него элементы FormContext на вкладке Locals одинаковы.   -  person Trevor    schedule 12.03.2021
comment
вы можете проверить ModelState глубоко в списке ошибок для каждого ModelStateEntry, ModelState здесь - это IReadOnlyDictionary<string,ModelStateEntry>.   -  person King King    schedule 12.03.2021


Ответы (1)


Я проверил ваш код, но кнопка отправки не срабатывает. Кроме того, я не видел действия Enroll, когда вы установили его в Html.BeginForm.

@using (Html.BeginForm("Enroll", "Enrollment", FormMethod.Post, new { enctype = "multipart/form-data", id = "EnrollForm" }))

Я сделал пример на основе ваших кодов, который работает так, как вы ожидали.

Основное представление (Enrollment.cshtml)

@model EnrollmentViewModel

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h1>Enrollment</h1>

<div>
    @await Component.InvokeAsync("EnrollClient", new { enrollmentVM = @Model })
</div>

Компонент представления (по умолчанию.cshtml):

@model EnrollmentViewModel
@{ 
    var x = Model;
}

@using (Html.BeginForm("Enroll", "Enrollment", FormMethod.Post, new { enctype = "multipart/form-data", id = "EnrollForm" }))
{
<fieldset>
    <legend>Preferences (select all that apply)</legend>
    @for (var i = 0; i < Model.Preferences.Count(); i++)
    {
        <div>
            <input type="checkbox" asp-for="@Model.Preferences[i].Selected">
            <label>@Model.Preferences[i].Text</label>
            <input type="hidden" asp-for="@Model.Preferences[i].Value" />
            <input type="hidden" asp-for="@Model.Preferences[i].Text" />
        </div>
    }
    <button type="submit" id="EnrollForm" form="EnrollForm">
        <span>Save</span>
    </button>
    <span asp-validation-for="Preferences" class="text-danger"></span>
</fieldset>
}

EnrollClientViewComponent.cs

public class EnrollClientViewComponent : ViewComponent
{
    public async Task<IViewComponentResult> InvokeAsync(EnrollmentViewModel enrollmentVM)
    {
        enrollmentVM = SetModelListItems(enrollmentVM);
        return new ViewViewComponentResult()
        {
            ViewData = new ViewDataDictionary<EnrollmentViewModel>(ViewData, enrollmentVM)
        };

    }

    public EnrollmentViewModel SetModelListItems(EnrollmentViewModel enrollmentVM)
    {
        enrollmentVM.Preferences = new List<SelectListItem>
        {
            new SelectListItem{ Text = "A", Value = "1"},
            new SelectListItem{ Text = "B", Value = "2"},
            new SelectListItem{ Text = "C", Value = "3"},
        };
        return enrollmentVM;
    }
}

Контроллер:

public class EnrollmentController : Controller
{
    public IActionResult Enrollment(EnrollmentViewModel enrollmentVM)
    {
        return View("Enrollment", enrollmentVM);
    }

    [HttpPost]
    public IActionResult Enroll(EnrollmentViewModel enrollmentVM)
    {
        if (!ModelState.IsValid)
        {
            return View("Enrollment", enrollmentVM);
        }
        else
        {
            //SaveForm
            return View("Enrollment", enrollmentVM);
        }
    }
}

Структура проекта:

введите здесь описание изображения

Результат:

введите здесь описание изображения

person mj1313    schedule 12.03.2021
comment
Привет! Я исправил ошибку в своем вопросе. Я думаю, что основное отличие заключается в том, что, поскольку я использую пользовательский атрибут в виртуальной машине, это только проверка на стороне сервера, и каким-то образом сообщение проверки не возвращается, хотя ModelState. (Я обновил свой вопрос дополнительной информацией). Большое спасибо! - person Trevor; 12.03.2021