Clientseitige Validierung mit Blazor
Ich habe eine EditForm, die eine Tierliste als Modell mit einer Liste von Tieren verwendet. Ich benutze Daten Anmerkungen zu den Tierobjekten. Am Frontend habe ich die Möglichkeit, die Liste der Tiernamen als Dropdown + Texteingabe-Kombination anzuzeigen und Objekte zu dieser Liste hinzuzufügen. Sie würden also einen Tiernamen auswählen und dem Tier einen Anzeigenamen zuweisen. dh ich wähle "Giraffe" und gebe ihm dann einen freundlichen Giraffennamen wie "Brian". Wenn Sie dann auf "Weitere hinzufügen" klicken, wird der Liste ein leerer Standard hinzugefügt, und Sie können weitermachen. Ich versuche, Datenanmerkungen zu verwenden, um eine clientseitige Validierung des Texteingabefelds in jeder Zeile in dem Sinne zu erhalten, dass ich dies tun sollte, wenn ich den Anzeigenamen leer lasse Sie erhalten eine Nachricht, dass das Feld erforderlich ist. Am besten nachdem ich in das Textfeld geklickt und nach links geklickt habe (onBlur denke ich?).
Ich bin der Meinung, dass dies Standard sein sollte, aber ich bin der Meinung, dass die Verwendung einer Liste die Standardvalidierung erschwert.
Ich erhalte eine Bestätigung, wenn ich Text eingebe, wegklicke und dann den Text lösche und wegklicke, was nicht ideal ist. Noch wichtiger ist, wenn ich auf Senden klicke, denke ich, dass das Formular validiert und dann nicht gesendet werden sollte, da es Nachrichten geben sollte, aber dies funktioniert auch nicht und sendet gut. Ich habe seitdem context.validate ausprobiert und seine Rückgabe ist wahr?
Sollte ich mir hier eine Art benutzerdefinierte Validierung ansehen oder sollte dies machbar sein?
AddAnimals.Razor Code:
<Css />
<h3>Add your animals</h3>
<EditForm Class="login-form" Model="@AnimalList" OnValidSubmit="SaveAnimals">
<DataAnnotationsValidator/>
@if (!string.IsNullOrEmpty(Error))
{
<Notification Type="@NotificationType.Error" Message="@Error" />
}
<div class="form-group">
<div class="form-row">
<div class="col-6">
<label for="Select">Select Animal</label>
</div>
<div class="col">
<label>Animal Name</label>
</div>
</div>
<ul>
@foreach (var animalSelection in AnimalList.SelectedAnimals)
{
<li>
<div class="form-row">
<div class="col-6">
<select class="form-control" @bind="animalSelection.AnimalName">
<option value="" disabled selected hidden>Select...</option>
@foreach (var name in AnimalNames)
{
<option value="@name">@name</option>
}
</select>
</div>
<div class="col">
<InputText class="form-control" @bind-Value="animalSelection.FriendlyName" />
<ValidationMessage For="@(() => animalSelection.FriendlyName)" />
</div>
</div>
</li>
}
</ul>
</div>
<div class="form-group">
<button type="button" class="btn" @onclick="AddMoreAnimals">+ Add More</button>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Continue</button>
</div>
</EditForm>
@code {
[Parameter]
public AnimalList AnimalList { get; set; } = new AnimalList();
[Parameter]
public List<string> AnimalNames { get; set; } = new List<string>();
public string Error { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
AnimalNames = AnimalsService.GetAnimals();
await GetAnimalList();
if (AnimalList.SelectedAnimals.Count < 1)
{
AddMoreAnimals();
}
StateHasChanged();
}
}
private async Task GetAnimalList()
{
AnimalList.SelectedAnimals = await AnimalsService.GetAnimals();
}
private void AddMoreAnimals()
{
AnimalList.AddDefault();
}
private async void SaveAnimals(EditContext context)
{
AnimalList.TrimList();
foreach (var animal in AnimalsList.SelectedAnimals)
{
var response = await AnimalsService.AddAnimal(animal);
if (response.IsError)
{
Error = response.Error;
StateHasChanged();
break;
}
else
{
Error = string.Empty;
}
}
}
}
AnimalsList
public class AnimalList
{
public List<Animal> SelectedAnimals { get; set; }
public AnimalList()
{
SelectedAnimals = new List<Animal>();
}
public void AddDefault()
{
SelectedAnimals.Add(new Animal());
}
public void TrimList()
{
//Trim away possible unused rows
SelectedAnimals.RemoveAll(
item => string.IsNullOrEmpty(item.FriendlyName) &&
string.IsNullOrEmpty(item.AnimalName));
}
}
Tier
public class Animal
{
[Required(ErrorMessage = "Animal selection is required")]
public string AnimalName { get; set; }
[Required(ErrorMessage = "You must enter a name for your Animal")]
[DisplayFormat(ConvertEmptyStringToNull = true)]
public string FriendlyName { get; set; }
}
Antworten
Ich habe dies in eine Liste von Formularen aufgeteilt, wie ich vorgeschlagen habe.
Hinweis: Ich habe Code hinter Seiten verwendet.
AnimalsPage.razor
@page "/animals"
<h3>Add your animals</h3>
<CascadingValue Value="this">
@foreach (var animal in this.animals)
{
<AnimalEditForm @key="animal" Animal="animal" />
}
</CascadingValue>
<button class="btn mr-1" @onclick="AddAnimalClicked">Add Animal</button>
@if (lastValidateResult)
{
<button class="btn btn-success" @onclick="SaveAllClicked">Save All</button>
}
else
{
<button class="btn btn-primary" @onclick="ContinueClicked">Continue</button>
}
AnimalsPage.razor.cs
public partial class AnimalsPage
{
private void AddAnimalClicked(MouseEventArgs mouseEventArgs)
{
this.animals.Add(new Animal());
this.lastValidateResult = false;
}
private void ContinueClicked(MouseEventArgs mouseEventArgs)
{
//Your "Trim" function
List<Animal> emptyAnimals = this.animals.Where(a => a.AnimalName == default && a.AnimalType == default).ToList();
emptyAnimals.ForEach(a =>
{
this.animals.Remove(a);
this.animalForms.Remove(a);
this.animalValidationResults.Remove(a);
});
this.animalForms.Values.ToList().ForEach(a => a.ShowValidations(true));
StateHasChanged();
}
private void SaveAllClicked()
{
//.....
}
internal void ChildChangedState(Animal animal, bool result)
{
bool prev;
if (!this.animalValidationResults.ContainsKey(animal))
{
prev = false;
this.animalValidationResults[animal] = prev;
}
else
{
prev = this.animalValidationResults[animal];
if (prev != result)
{
this.animalValidationResults[animal] = result;
this.lastValidateResult = this.animalValidationResults.Values.All(a => a);
StateHasChanged();
}
}
}
internal Dictionary<Animal, AnimalEditForm> animalForms = new Dictionary<Animal, AnimalEditForm>();
private readonly Dictionary<Animal, bool> animalValidationResults = new Dictionary<Animal, bool>();
private readonly List<Animal> animals = new List<Animal>();
private bool lastValidateResult = false;
}
AnimalEditForm.razor
<EditForm Model="Animal">
<DataAnnotationsValidator />
@{if (validatorVisible) { { Validate(context); } } }
<div class="form-row mb-1">
<div class="col">
<InputSelect class="form-control" @bind-Value="Animal.AnimalType">
<option value="" selected>Select...</option>
@foreach (var animalType in animalTypes)
{
<option value=@animalType>@animalType</option>
}
</InputSelect>
<ValidationMessage For="@(() => Animal.AnimalType)" />
</div>
<div class="col">
<InputText class="form-control" @bind-Value="Animal.AnimalName" />
<ValidationMessage For="@(() => Animal.AnimalName)" />
</div>
</div>
</EditForm>
AnimalEditForm.razor.cs
public partial class AnimalEditForm
{
[Parameter]
public Animal Animal { get; set; }
[CascadingParameter]
public AnimalsPage AnimalsPage { get; set; }
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
AnimalsPage.animalForms[Animal] = this;
}
base.OnAfterRender(firstRender);
}
private IReadOnlyList<string> animalTypes => new[]
{
"Eagle",
"Elephant",
"Giraffe",
"Hippopotamus",
"Horse",
"Koala",
"Zebra"
};
public bool LastValidateResult { get; private set; }
private bool validatorVisible = false;
public void ShowValidations(bool show) => this.validatorVisible = show;
public void Validate(EditContext editContext)
{
LastValidateResult = editContext.Validate();
AnimalsPage.ChildChangedState(Animal, LastValidateResult);
}
}
Nicht benutzen OnValidSubmitnoch OnInvalidSubmit, nurOnSubmit
<EditForm Class="login-form" Model="@AnimalList" OnSubmit="SaveAnimals">
Überprüfen Sie in SaveAnimal die Liste
private async void SaveAnimals(EditContext context)
{
bool valid = context.Validate();
AnimalList.SelectedAnimals.ForEach(x =>
{
var field=FieldIdentifier.Create((() => x.FriendlyName));
context.NotifyFieldChanged(field);
if (valid)
valid = context.GetValidationMessages(field)
.FirstOrDefault(x => !String.IsNullOrEmpty(x)) == null;
});
if (valid)
Console.WriteLine("Save Animals");
}