5

I have the following class which is being used as an input model for an EditForm in a Blazor server side application.

public class KundeInput
{
    [ValidateComplexType]
    public List<AnsprechpartnerInput> Ansprechpartner { get; } = new List<AnsprechpartnerInput>();
  
    public string? Kundennummer { get; }
 
    [Required]
    [MaxLength(60)]
    public string Firma { get; set; } = String.Empty;
    [MaxLength(60)]
    public string? Name2 { get; set; }
    [MaxLength(60)]
    public string? Name3 { get; set; }
}

As you can see, my model contains a list of another model called AnsprechpartnerInput. Here is this model:

public class AnsprechpartnerInput
{
    public string? Kundennummer { get; set; }
    public int Nummer { get; } = -1;
    [MaxLength(60)]
    [Required]
    public string Vorname { get; set; } = String.Empty;
    [MaxLength(60)]
    [Required]
    public string Nachname { get; set; } = String.Empty;
    [MaxLength(40)]
    [Required]
    public string? Bereich { get; set; }
    / * More properties */
}

The validation works fine. However, once I have multiple invalid AnsprechpartnerInput models in my list, the ValidationSummary becomes a mess. Because it displays e.g. 5 times field xyz is invalid.

I know I can set a custom message with the ErrorMessage property but I am not able to use other attributes from my model in this message.

What I want to achive is this:

[Required(ErrorMessage = $"Vorname of {Kundennummer} is required")]
public string Vorname { get; set; } = String.Empty;

I already tried to change the message with reflection but accoridng to Microsoft this way is not recommend or supported https://github.com/dotnet/aspnetcore/issues/25611

Is there any way to get it to work? I thought of string replacement but I am not sure how I can figure out the right model for my ValidationMessage.

Also is there any way to validate the items of the list by one and get a boolean result? Let's say I want to achive this:

@foreach (var ansprechpartner in Input.Ansprechpartner)
{

    if (Input.SelectedAnsprechpartner is null)
        Input.SelectedAnsprechpartner = ansprechpartner;

    <a @onclick="() => Input.SelectedAnsprechpartner = ansprechpartner"
       class="@GetNavListClass(Input.SelectedAnsprechpartner == ansprechpartner)"
       id="list-ansprechpartner-tab-@(ansprechpartner.Nummer)"
       data-toggle="list"
       href="#list-ansprechpartner-@(ansprechpartner.Nummer)"
       role="tab"
       aria-controls="@(ansprechpartner.Nummer)">
        @((MarkupString)(ansprechpartner.Nummer < 0  ? "<span class=\"font-weight-bold\">NEU</span>" : $"({ansprechpartner.Nummer})")) @ansprechpartner.Vorname @ansprechpartner.Nachname
    </a>
    // When the model ansprechpartner is invalid, I want to display an icon
}

Thanks for any help! PS: Blazor rocks!

2
  • 3
    Yup Blazor rocks :) Have you tried FluentValidation? and use this with Blazor Commented Sep 10, 2020 at 10:48
  • 2
    FluentValidation will save you. Commented Sep 10, 2020 at 11:06

2 Answers 2

4

You should use a custom validation attribute where you can explicitly add any error message you want

public class KundennummerValidationAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var model = (AnsprechpartnerInput)validationContext.ObjectInstance;
        if(string.IsNullOrEmpty((string)value))
        {
            return new ValidationResult($"Vorname of {model.Kundennummer} is required", new[] { "Kundennummer" });
        }


        return ValidationResult.Success;
    }
}

then use

[KundennummerValidation]
public string Vorname { get; set; } = String.Empty;

result : Validation summary:

enter image description here

Sign up to request clarification or add additional context in comments.

Comments

0
public class RequiredCollectionAttribute : ValidationAttribute
{
    private readonly string _dependentProperty;

    public RequiredCollectionAttribute(string dependentProperty)
    {
        _dependentProperty = dependentProperty;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var property = validationContext.ObjectType.GetProperty(_dependentProperty);
        if (property == null)
        {
            return new ValidationResult($"Unknown property: {_dependentProperty}");
        }

        var dependentValue = property.GetValue(validationContext.ObjectInstance, null);
        if (dependentValue == null)
        {
            return ValidationResult.Success;
        }

        IEnumerable<string> isCollection;
        try
        {
            isCollection = (IEnumerable<string>)dependentValue;
        }
        catch (InvalidCastException)
        {
            return new ValidationResult($"Property {_dependentProperty} is not of type IEnumerable<string>.");
        }

        if (isCollection == null || (isCollection != null && isCollection.Count() == 0))
        {
            return new ValidationResult(ErrorMessage ?? "This field is required.");
        }

        return ValidationResult.Success;
    }
}


[RequiredCollection("SelectedProjectManagers", ErrorMessage = "Project Manager(s) is required.")]
public IEnumerable<string>? SelectedProjectManagers { get; set; }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.