Как использовать <ValidationMessage> внутри компонента

Обратите внимание, что хотя это тесно связано с этим вопросом, ответ там специфичен для одного известного свойства модели, тогда как я хочу, чтобы это работало для любого свойства, поэтому компонент можно использовать повторно.

Я хочу использовать тег Blazor <ValidationMessage> внутри компонента. Однако, когда я это делаю, сообщение проверки не отображается.

Например, следующий компонент (FormRowText.razor) создает строку (в смысле сетки Bootstrap), содержащую <input type = "text /> для именованного свойства модели...

<div class = "form-group row">
  <label for = "@PropertyName" class = "col-lg-2 col-form-label">@(Caption ?? PropertyName)</label>
  <div class = "col-lg-10 input-group">
    <div class = "input-group-prepend">
      <span class = "input-group-text">
        <i class = "far fa-@Icon"></i>
      </span>
    </div>
    <input class = "form-control" Value = "@Value" type = "text"
           id = "@PropertyName" name = "@PropertyName" @onchange = "@OnChanged" />
  </div>
</div>

@code {

  [Parameter]
  public string PropertyName { get; set; }

  [Parameter]
  public string Value { get; set; }

  [Parameter]
  public EventCallback<string> ValueChanged { get; set; }

  [Parameter]
  public string Caption { get; set; }

  [Parameter]
  public string Icon { get; set; }

  private async Task OnChanged(ChangeEventArgs cea) =>
    await ValueChanged.InvokeAsync(cea.Value?.ToString() ?? "");

}

Используется так...

<EditForm ...>
  <FormRowText PropertyName = "@nameof(Customer.Email)" @bind-Value = "_customer.Email" Icon = "at" />
</EditForm>

Если я добавлю <ValidationMessage> после этой разметки, она будет работать нормально, но если я добавлю ее внутри компонента (именно там, где я хочу, чтобы она была), это не сработает.

Я видел этот ответ от @enet, в котором показано, как это сделать для конкретного известного свойства модели. Однако мой компонент предназначен для работы с любым свойством модели, и я не знаю, как его изменить.

Я попытался добавить параметр для модели (с @typeparam T вверху файла)...

[Parameter]
public T Model { get; set; }

...и добавив в HTML следующее...

<ValidationMessage For = "() => PropertyName" />

Однако это не работает.

Есть идеи?

Для развертывания Сайтов с использованием Blazor, Angular и React с репозиторием на GitHub на Cloudflare
Для развертывания Сайтов с использованием Blazor, Angular и React с репозиторием на GitHub на Cloudflare
Как развернуть сайты с помощью Blazor, Angular и React с репозиторием на GitHub на Cloudflare.
3
0
770
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете каскадировать EditContext в свой компонент и использовать его для уведомления об изменении текущего поля.

FormRowText.razor.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;    

public partial class FormRowText
    {
        [CascadingParameter] EditContext CascadedEditContext { get; set; }

        [Parameter]
        public string PropertyName { get; set; }

        [Parameter]
        public string Value { get; set; }

        [Parameter]
        public EventCallback<string> ValueChanged { get; set; }

        [Parameter]
        public string Caption { get; set; }

        [Parameter]
        public string Icon { get; set; }

        [Parameter] public Expression<Func<string>> ValueExpression { get; set; }
        private async Task OnChanged(ChangeEventArgs cea)
        {
            await ValueChanged.InvokeAsync(cea.Value?.ToString() ?? "");
            var identifier = CascadedEditContext.Field(PropertyName);
            CascadedEditContext.NotifyFieldChanged(identifier);
        }
         
    }

FormRowText.razor

<div class = "form-group row">
    <label for = "@PropertyName" class = "col-lg-2 col-form-label">@(Caption ?? PropertyName)</label>
    <div class = "col-lg-10 input-group">
        <div class = "input-group-prepend">
            <span class = "input-group-text">
                <i class = "far fa-@Icon"></i>
            </span>
        </div>
        <input class = "form-control" value = "@Value" type = "text" id = "@PropertyName" name = "@PropertyName" 
               @onchange = "@OnChanged" />
        <ValidationMessage For = "@ValueExpression" />

    </div>
</div>

Вам также понадобится

public class Customer
    {
        [Required]
        public string Name { get; set; }
        public Country Country { get; set; } = Country.Israel;
        [Required]
        [DataType(DataType.EmailAddress)]
        [EmailAddress]
        public string Email { get; set; }
    } 

Применение

@page "/"


    @using System.ComponentModel.DataAnnotations;
    @using Microsoft.AspNetCore.Components.Forms;


    <EditForm  EditContext = "EditContext" OnValidSubmit = "HandleValidSubmit">
        <DataAnnotationsValidator />

        <div class = "form-group">
            <label for = "name">Name: </label>
             <InputText @bind-Value = "@customer.Name"></InputText>
            <ValidationMessage For = "@(() => customer.Name)" />

        </div>
        <div class = "form-group">
            <FormRowDropdownEnum @bind-Value = "customer.Country" Caption = "Country" T = "Country"></FormRowDropdownEnum>
            <ValidationMessage For = "@(() => customer.Country)" />
            <FormRowText @bind-Value = "customer.Email" Caption = "Email" 
                 PropertyName = "@nameof(Customer.Email)" Icon = "at"></FormRowText>


        </div>
        <p>
            <button type = "submit" class = "btn btn-primary">Save</button>

        </p>
    </EditForm>


  
    @code{
        private Customer customer = new Customer();
        private EditContext EditContext;

        protected override void OnInitialized()
        {
            EditContext = new EditContext(customer);

        }

        public async Task HandleValidSubmit()
        {
            await Task.Delay(1);

            Console.WriteLine("Saving...");
           
        }
   }

Другая версия

Поскольку вы используете компонент EditForm, было бы неплохо использовать компоненты Forms, такие как компонент InputText и тому подобное.

В следующем фрагменте кода описывается, как реализовать компонент FormRowText путем создания подклассов компонентов InputText и добавления новых свойств. Обратите внимание, что PropertyName не требуется для работы компонента.

FormRowText2.razor

@inherits InputText

<div class = "form-group row">
    <label for = "@PropertyName" class = "col-lg-2 col-form-label">@(Caption ?? PropertyName)</label>
    <div class = "col-lg-10 input-group">
        <div class = "input-group-prepend">
            <span class = "input-group-text">
                <i class = "far fa-@Icon"></i>
            </span>
        </div>
        <input class = "form-control" @bind = "CurrentValue" type = "text" id = "@PropertyName" name = "@PropertyName" />
        <ValidationMessage For = "@ValueExpression" />
     </div>
</div>

@code{
    [Parameter]
    public string Caption { get; set; }

    [Parameter]
    public string Icon { get; set; }

    [Parameter]
    public string PropertyName { get; set; }
    
}

Спасибо, это именно то, что мне было нужно! У вас есть блог? Если нет - надо! Вы мне очень помогли несколько раз, я хотел бы увидеть больше ваших мыслей и идей.

Avrohom Yisroel 10.12.2020 20:49

Нет, у меня нет блога. На самом деле я вообще не программист. Я делаю это для удовольствия. Сейчас я работаю волонтером в благотворительной ассоциации, которая раздает горячее питание бедным. В моем профиле есть ссылка для пожертвований ;) По этой ссылке можно перейти ко всем моим ответам, связанным с Blazor: stackoverflow.com/tags/blazor/topusers

enet 10.12.2020 21:24

Вы не программист? Я зарабатываю этим на жизнь и знаю не так много, как ты! Снимаю шляпу перед вами за вашу צדקה работу. Хорошо, что есть такие люди, как ты. כל הכבוד вам, обязательно подарю!

Avrohom Yisroel 11.12.2020 00:39

Другие вопросы по теме