Tag Helper asp-validation-for всегда показывает - PullRequest
1 голос
/ 01 июня 2019

Я пытался использовать Asp.Net Core TagHelper, но, похоже, он не работает.Однако при использовании HtmlHelpers он работает как положено.Моя проблема заключается в том, что он всегда отображает сообщение об ошибке, хотя ModelState является действительным.Я делаю что-то неправильно или кто-то может воспроизвести эту ошибку?

<label class="control-label" asp-for="Firstname">Firstname</label>
<input type="text" class="form-control" asp-for="Firstname">
<span class="form-control-feedback" asp-validation-for="Firstname"> This field has an error. </span>

Свойство Firstname имеет атрибут Required в ViewModel.

Это работает так:

<label class="control-label" asp-for="Firstname">Firstname</label>
<input type="text" class="form-control" asp-for="Firstname">
@Html.ValidationMessageFor(x => x.Firstname)

Редактировать:

Кажется, это работает, если я не добавляю пользовательское сообщение об ошибке в элемент Html, а вместо этого в ViewModel DataAnnotation, например:

<label class="control-label" asp-for="Firstname">Firstname</label>
<input type="text" class="form-control" asp-for="Firstname">
<span class="form-control-feedback" asp-validation-for="Firstname"></span>

Модель:

[Required(ErrorMessage = "This field has an error.")]
public string Firstname { get; set; }

1 Ответ

1 голос
/ 06 июня 2019

TL; DR:

Подумайте о том, чтобы поместить текст в помощники тегов в тех случаях, когда вам действительно что-то нужно отличается от сгенерированного значения.

Полный ответ

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

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

var tagHelperContent = await output.GetChildContentAsync();

// We check for whitespace to detect scenarios such as:
// <span validation-for="Name">
// </span>
if (!tagHelperContent.IsEmptyOrWhiteSpace)
{
    message = tagHelperContent.GetContent();
}

Он получает содержимое тега и затем заполняет переменную message, если содержимое имеет значение null, пусто или пропущено. Переменная message затем используется для генерации сообщения проверки:

var tagBuilder = Generator.GenerateValidationMessage(
    ViewContext,
    For.ModelExplorer,
    For.Name,
    message: message,
    tag: null,
    htmlAttributes: htmlAttributes);

Если message равен null или пуст, тогда генератор выдаст ошибку модели (см. строка 858 из DefaultHtmlGenerator);

if (!string.IsNullOrEmpty(message))
{
    tagBuilder.InnerHtml.SetContent(message);
}
else if (modelError != null)
{
    modelExplorer = modelExplorer ?? ExpressionMetadataProvider.FromStringExpression(
        expression,
        viewContext.ViewData,
        _metadataProvider);
    tagBuilder.InnerHtml.SetContent(
        ValidationHelpers.GetModelErrorMessageOrDefault(modelError, entry, modelExplorer));
}

GetModelErrorMessageOrDefault() из ValidationHelpers:

public static string GetModelErrorMessageOrDefault(
    ModelError modelError,
    ModelStateEntry containingEntry,
    ModelExplorer modelExplorer)
{
    Debug.Assert(modelError != null);
    Debug.Assert(containingEntry != null);
    Debug.Assert(modelExplorer != null);

    if (!string.IsNullOrEmpty(modelError.ErrorMessage))
    {
        return modelError.ErrorMessage;
    }

    // Default in the ValidationMessage case is a fallback error message.
    var attemptedValue = containingEntry.AttemptedValue ?? "null";
    return modelExplorer.Metadata.ModelBindingMessageProvider.ValueIsInvalidAccessor(attemptedValue);
}

Так что да, если вы поместите какой-либо текст в тег проверки <span>, помощник тега выберет ваш текст вместо ошибки проверки из состояния модели. Подобное поведение происходит, если вы помещаете текст внутри тега <label>, как вы это сделали:

<label class="control-label" asp-for="Firstname">Firstname</label>

Помощник по тегам не будет перезаписывать значение Firstname, которое вы поместили внутри тега. Это может показаться плохим поведением, но если вы хотите использовать отображаемое имя для свойства Firstname:

[Display(Name = "Fancy first name")]
public string Firstname { get; set; }

Вы не увидели бы, как это работает! Поскольку помощник по тегам снова выберет текст, который вы поместите между тегами <label>, вместо отображаемого имени для Firstname.

Что вам нужно сделать, так это оставить все как можно проще:

<label class="control-label" asp-for="Firstname"></label>

Подумайте о том, чтобы поместить текст в помощники тегов в тех случаях, когда вам действительно что-то нужно отличается от сгенерированного значения.

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

<select asp-for="LevelId" asp-items="@Model.Levels" class="custom-select">
    <option selected disabled>Select option</option>
</select>

приведет к:

<select class="custom-select" data-val="true" data-val-required="&#x27;Level Id&#x27; must not be empty." id="LevelId" name="LevelId">
    <option selected disabled>Select parking level</option>
    <option value="9">-2</option>
    <option value="8">-1</option>
    <option value="7">0</option>
</select>
...