Форма редактирования с расширенной функциональностью без написания html-части

#blazor

#blazor

Вопрос:

Я хотел бы, чтобы EditForm это уведомляло меня о наличии несохраненных изменений.

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

Это легко сделать с EditContext EditContext.OnFieldChanged помощью обработчика событий и (рабочая демонстрация):

     
 <EditForm EditContext="@editContext" OnValidSubmit="()=> isUnsavedChangePresent=false">
   <span>@(isUnsavedChangePresent?"changes are not saved!": "changes are saved")</span>
    <InputText @bind-Value="product.Name" />
    <button >Save it </button>
    <div>(state is updated after focus change)</div>
 </EditForm>

@code {
    private EditContext editContext;
    Product product= new Product();
   bool isUnsavedChangePresent;
    private async void EditContext_OnFieldChanged(object? sender,FieldChangedEventArgs e)
    {
      isUnsavedChangePresent = true;
    }
    class Product
    {
     public string Name {get;set;}
    }

     protected override  void OnParametersSet()
    {
      editContext = new EditContext(product);
      editContext.OnFieldChanged  = EditContext_OnFieldChanged;
    }
}

 

Я хотел бы иметь это EditForm как собственный компонент, поэтому я попробовал это:

 @using Microsoft.AspNetCore.Components.Forms
@inherits EditForm
@ChildContent(EditContext) @*is this correct?? It throws error about cascading parameters*@
@code
{

    protected override async Task OnParametersSetAsync()
    {
        this.EditContext = new EditContext(this.Model);
        EditContext.OnFieldChanged  = EditContext_OnFieldChanged;
    }

    private async void EditContext_OnFieldChanged(object? sender, FieldChangedEventArgs e)
    {
        Console.WriteLine("In On Filed Changed");
    }

}
 

Я хотел бы использовать HTML-часть от parent EditForm , но я не знаю, как и возможно ли это вообще..

Комментарии:

1. Я предлагаю вам написать свой собственный компонент <ChangesWatcher . Вы можете получить доступ к EditContext через каскадные параметры.

2. @daniherrera Спасибо, это отличное решение, намного чище, чем я надеялся. (можете ли вы превратить свой комментарий в ответ?)

Ответ №1:

Следующий код описывает, как вы можете определить универсальный класс, производный от компонента EditForm…

ExtendedEditForm.cs (поместите его в папку Pages)

 public partial class ExtendedEditForm <T> : EditForm
    {
        [Parameter]
        public T MyModel { get; set; }

        protected override void OnInitialized()
        {
            EditContext = new EditContext(MyModel);
            EditContext.OnFieldChanged  = EditContext_OnFieldChanged;
           
            base.OnInitialized();
        }


        private void EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
        {
            Console.WriteLine(e.FieldIdentifier.FieldName);

        }
    }
 

Использование

 @page "/"

<ExtendedEditForm MyModel="product" OnValidSubmit ="OnValidSubmit">
    <span>@(IsSaved ? "changes are saved" : "changes are not saved!" )</span>
    <InputText @bind-Value="product.Name" />
    <button type="submit">Save it </button>
    <div>(state is updated after focus change)</div>
</ExtendedEditForm>
    
@code {
       
    Product product = new Product();

    private bool IsSaved { get; set; }

    private void OnValidSubmit()
    {
        IsSaved = true;
    }

    public class Product
    {
        public string Name { get; set; }
    }
}
 

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

Вы должны создавать и инициализировать свои объекты, такие как EditContext, в OnInitialized(асинхронных) методах, а не в OnParametersSet (асинхронных) методах.

Использование EditContext.OnFieldChanged для установки значения isUnsavedChangePresent неверно, хотя в этом случае это работает…Предположим, что у вас в форме 10 полей вместо одного. В любом случае вы должны сделать это в обработчике OnValidSubmit событий, который запускается при нажатии кнопки «отправить». Вы должны поместить кнопку «отправить» вместо (или в дополнение) к элементу button.

 @ChildContent(EditContext) @*is this correct?? It throws error about cascading parameters*@
 

Нет, это неверно. Свойство ChildContent наследуется от EditForm, ваш производный класс не переопределяет метод BuildRenderTree, который отображает EditForm, и использует ChildContent в своей процедуре рендеринга. Вы просто ничего не делаете… в этом отношении нет кода. Обратите внимание, что переопределение метода BuildRenderTree может быть выполнено двумя способами: в коде, путем переопределения BuildRenderTree или путем создания части представления вашего компонента (разметки Razor и C #) (.razor)

Комментарии:

1. Это именно то, о чем я просил. Спасибо. Также спасибо за ваши заметки и пояснения. Единственное, что я не совсем понимаю, это IsSaved vs isUnsavedChangePresent . Потому что они не являются полным oposit. При инициализации IsSaved будет false, но никаких изменений нет (поэтому нет необходимости уведомлять пользователя). После изменения он должен уведомлять, но IsSaved это то же самое. Вот почему я считаю OnFieldChanged это необходимым.

2. IsSaved — это короткое имя, которое я дал для isUnsavedChangePresent . Хорошо, я понимаю, чего вы хотите достичь. Измените свой код на это: IsSaved ? «»: «изменения не сохраняются!» И определите свойство IsSaved со значением по умолчанию true. Теперь, когда ваше приложение запускается в первый раз, значение IsSaved равно true, и, следовательно, сообщение не отображается. В методе EditContext_OnFieldChanged add: IsSaved = false; что приводит к отображению сообщения «изменения не сохранены!», Когда пользователь начинает редактирование. Наконец, когда пользователь нажимает кнопку «отправить», для IsSaved устанавливается значение true, в результате чего сообщение исчезает…

Ответ №2:

В Net 6 я сделал нечто подобное, я хотел расширить EditForm, чтобы принимать событие изменения, потому что я хочу инициировать изменение, не нажимая кнопку отправки. Может быть, этот пример может кому-то помочь

 using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;

namespace HOLOblazor.Components.Form;

public partial class EditFormChange : EditForm
{
    [Parameter] public EventCallback OnPropertyChange {  get; set; }

    protected override void OnParametersSet()
    {
        base.OnParametersSet();
        EditContext.OnFieldChanged -= EditContext_OnFieldChanged; // Unsuscribe old parameters set
        EditContext.OnFieldChanged  = EditContext_OnFieldChanged;
    }

    private void EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
    {
        if (EditContext.Validate())
            OnPropertyChange.InvokeAsync();
    }
}
 

И теперь я могу сделать

 <EditFormChange Model="mymodel" OnPropertyChange="() => UpdateForm(mymodel)">
    <FluentValidationValidator />
    <InputText @bind-Value="mymodel.Field1" />
    <small class="form-text text-muted"><ValidationMessage For="() => mymodel.Field1" /></small>
</EditFormChange>