Checking the data for each item in the list of my ViewModel

To perform a validation check with a regex, I usually do:

// In my ViewModel
[RegularExpression("MyRegex", ErrorMessageResourceName = "MyErrorMessage")]
public string MyField { get; set; }

And HTML helper

@Html.TextBoxFor(model => model.MyField)

generates markup that looks like this:

<input type="text" class="valid" name="MyField" value="" id="MyField" data-val="true" data-val-regex-pattern="MyRegex" data-val-regex="MyErrorMessage"></input>

The problem is that I want to have a dynamic number of fields and now use

// In my ViewModel
[RegularExpression("MyRegex", ErrorMessageResourceName = "MyErrorMessage")]
public IList<string> MyField { get; set; }

This time

@Html.TextBoxFor(model => model.MyField[0])

will generate (without regex html attributes)

<input id="MyField_0_" type="text" value="" name="MyField[0]"></input>

How can I guarantee that data-valhtml attributes are created when linking list items with a DataAnnotation validation attribute in my ViewModel?

+4
source share
3 answers

. , - -, :

public IList<MyField> MyFields {get;set;}

public class MyField
{
    [RegularExpression("MyRegex", ErrorMessageResourceName = "MyErrorMessage")]
    public string Value
}

:

@Html.TextBoxFor(model => model.MyFields[0].Value)
+6

DataAnnotations . , DataAnnotation .

Html.EditorFor, ModelMetadata , , ModelValidators, . ModelValidators 'data-val- *' HTML.

Html.EditorFor ( ), ModelMetadata Validators - ModelMetadata, MyField - "RegularExpression" . ModelMetadata Validators . ModelMetadata , Validators. , HTML.

, , , Validator, "MyField" .

  • ModelMetadataProvider DataAnnotationsModelMetadataProvider
  • GetValidators 'DataAnnotationsModelValidatorProvider'

1

@model System.Collections.Generic.IEnumerable<object>
@{
    ViewBag.Title = "Collection";
    var modelMetadata = this.ViewData.ModelMetadata;
    var validators = modelMetadata.GetValidators(ViewContext).ToList();
    ViewContext.HttpContext.Items["rootValidators"] = validators;
}

@foreach (var item in Model)
{
    @Html.EditorFor(m => item)
}

, , . . HttpContext.Items ModelValidatorProvider.

2 - Global.asax -

ModelValidatorProviders.Providers.Clear();
ModelValidatorProviders.Providers.Add(new DAModelValidatorProvider());

ModelMetadataProviders.Current = new CachedDataAnnotationsModelMetadataProvider();

3 - ModelValidatorProvider, GetValidators,

public class DAModelValidatorProvider : DataAnnotationsModelValidatorProvider
{
    protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
    {
        var validators = base.GetValidators(metadata, context, attributes).ToList();

        // get root validators of the collection. this was stored in the editor template - fetching it for use now.
        // fetching the rootvalidators inside this method is a bad idea because we have to call GetValidators method on the 
        // containers ModelMetadata and it will result in a non-terminal recursion
        var rootValidators = context.HttpContext.Items["rootValidators"] as IEnumerable<ModelValidator>;
        if (rootValidators != null)
        {
            foreach (var rootValidator in rootValidators)
            {
                validators.Add(rootValidator);
            }
        }

        return validators;
    }
}

3 . , Html.EditorFor Html.TextBoxFor. Html.EditorFor, , , . , . https://github.com/swazza85/Stackoverflow, , . , , , , , , .

Cheers, Swarup.

+3

@swazza85, . , - , . IEnumerable<object> IList<object> ( IList<decimal?>, IList<object> .). for, name item, .

@model System.Collections.Generic.IList<decimal?>

@{
    ViewBag.Title = "Collection";
    var modelMetadata = this.ViewData.ModelMetadata;
    var validators = modelMetadata.GetValidators(ViewContext).ToList();
    ViewContext.HttpContext.Items["rootValidators"] = validators;
}

@for (var i = 0; i < Model.Count(); i++)
{
    @Html.EditorFor(model => Model[i], new { htmlAttributes = new { @class = "form-control" } })
    @Html.ValidationMessageFor(model => Model[i], "", new { @class = "text-danger" })
}

Also, if you do not want to clear your suppliers in the file Global.asax, just return the validators to the if statement and return an empty list outside it, just note that this editor template must be the last in your views or it will encounter problems with other properties or templates . You can set ViewContext.HttpContext.Items["rootValidators"] = nullat the end of the template.

  protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
    {
      var validators = base.GetValidators(metadata, context, attributes).ToList();

      // get root validators of the collection. this was stored in the editor template - fetching it for use now.
      // fetching the rootvalidators inside this method is a bad idea because we have to call GetValidators method on the 
      // containers ModelMetadata and it will result in a non-terminal recursion
      var rootValidators = context.HttpContext.Items["rootValidators"] as IEnumerable<ModelValidator>;

      if (rootValidators != null)
      {
        foreach (var rootValidator in rootValidators)
        {
          validators.Add(rootValidator);
        }
        return validators;
      }

      return new List<ModelValidator>();
    }
+1
source

Source: https://habr.com/ru/post/1538368/


All Articles