Custom ValidationAttribute does not launch IsValid function call in view mode

I created my own custom ValidationAttribute :

 public class UrlValidationAttribute : ValidationAttribute { public UrlValidationAttribute() {} public override bool IsValid(object value) { if (value == null) return true; var text = value as string; Uri uri; return (!string.IsNullOrWhiteSpace(text) && Uri.TryCreate(text, UriKind.Absolute, out uri)); } } 

I use this on one of my models and it works great. However, now I'm trying to use it in a view model:

 public class DeviceAttribute { public DeviceAttribute(int id, attributeDefinition, String url) { ID = id; Url = url; } public int ID { get; set; } [UrlValidation] public String Url { get; set; } } 

The view model is used in a partial view as follows:

 @model List<ICMDB.Models.DeviceAttribute> <table class="editor-table"> @foreach (var attribute in Model) { <tr> @Html.HiddenFor(a => attribute.ID) <td class="editor-label"> @Html.LabelFor(a => attribute.Url) </td> <td class="editor-field"> @Html.TextBoxFor(a => attribute.Url) @Html.ValidationMessageFor(a => attribute.Url) </td> </tr> } </table> 

For some unknown reason, when the constructor for UrlValidationAttribute fires, the IsValid function does not fire. Any ideas?

Edit: With further research, this seems to be happening because the DeviceAttribute view model is actually a view model for partial. The full page passes another view model containing a list of DeviceAttribute view DeviceAttribute . Therefore, when the controller action is called, a full pageview model is created and the values ​​are populated, but the DeviceAttribute view models are not built, so why the check is not performed.

+6
source share
1 answer

I would recommend that you use editor templates instead of writing foreach loops. I suggest that your main view model looks something like this:

 public class MyViewModel { public List<DeviceAttribute> Devices { get; set; } ... } 

Now in your main view:

 @model MyViewModel @using (Html.BeginForm()) { <table class="editor-table"> @Html.EditorFor(x => x.Devices) </table> <input type="submit" value="OK" /> } 

and in the corresponding editor template ( ~/Views/Shared/EditorTemplates/DeviceAttribute.cshtml ):

 @model DeviceAttribute <tr> @Html.HiddenFor(x => x.ID) <td class="editor-label"> @Html.LabelFor(x => x.Url) </td> <td class="editor-field"> @Html.TextBoxFor(x => x.Url) @Html.ValidationMessageFor(x => x.Url) </td> </tr> 

And your POST action returns a view model:

 [HttpPost] public ActionResult Index(MyViewModel model) { ... } 

Now, the default binder will successfully bind all values ​​in the view model and check for bump.

Here's a nice blog post about templates.

-1
source

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


All Articles