MeanGreen
MeanGreen

Reputation: 3305

Use AdditionalFields to compare to field in a different class

Introduction

In MVC Core I have a base ViewModel and two ViewModels included in the base model as properties, like so:

public class BaseViewModel
{
    public FirstViewModel First { get; set; }    
    public SecondViewModel Second { get; set; }
}

In FirstViewModel I added a custom validation attribute on one of the properties, inheriting from RemoteAttribute. My goal is to use this attribute comparing the value to a property in SecondViewModel. I've set this up using the AdditionalFields property of the RemoteAttribute.

I think my problem lies in the way the HTML attributes are added to the control in the razor view:

data-val-remote-additionalfields="*.PropOfModelFirst,*.PropOfModelSecond"

When the clientside validation is calling the controller action, the *. is replaced by the framework by First., which is wrong, because the second value is not part of the First-class.

I tried prepending the classname to the second property, resulting in

data-val-remote-additionalfields="*.PropOfModelFirst,*.Second.PropOfModelSecond"

but as can be expected this is changed to First.Second.PropOfModelSecond.

Question

Can the AdditionalFields property be used to compare against values from another ViewModel?

Upvotes: 0

Views: 357

Answers (1)

user3559349
user3559349

Reputation:

You cannot use AdditionalFields to compare against values from another ViewModel. The reason is that the rules are added to jquery.validate.js by the jquery.validate.unobtrusive.js plugin (which reads the data-val-* attributes generated by the HtmlHelper methods). Specifically it is the adapters.add("remote", ["url", "type", "additionalfields"], function (options) { method that is pre-pending First to the property names.

One option would be to use a single 'flat' view model containing all properties.

If that is not desirable, then you can just write your own ajax code to call your server method that performs the validation. This actually has some added performance benefits as well. By default, after initial validation triggered by the .blur() event, validation is performed on every .keyup() event, meaning that you are potentially making a lot of ajax and database calls if the user initially entered an invalid value.

Remove the [Remote] attribute, and add the following script (I'll assume the properties are First.ABC and Second.XYZ)

$('#First_ABC').change(function() {
    var url = '@Url.Action(...)'; // add your action name
    var input = $(this);
    var message = $('[data-valmsg-for="First.ABC"]'); // or give the element and id attribute
    $.post(url, { abc: input.val(), xyz: $('#Second_XYZ').val() }, function(response) {
        var isValid = response === true || response === "true";
        if (isValid) {
            input.addClass('valid').removeClass('input-validation-error');
            message.empty().addClass('field-validation-valid').removeClass('field-validation-error');
        } else {
            input.addClass('input-validation-error').removeClass('valid');
            message.text(response).addClass('field-validation-error').removeClass('field-validation-valid');
        }
    })
});

where the controller method would be

[HttpPost]
public ActionResult Validate(string abc, string xyz)
{
    bool isValid = .... // code to validate
    if (isValid)
    {
        return Json(true, JsonRequestBehaviour.AllowGet);
    }
    else
    {
        return Json("your error message", JsonRequestBehaviour.AllowGet)
    }
}

Upvotes: 1

Related Questions