FluentValidation AddToModelState

HOORAY FOR THIS!

I was working on some admin functionality, and I added a validator to a model. The problem is the forms are contained within Telerik MVC Extensions tabs, so the validators for one tab are applied even if you are on a different tab. No dice! I really wanted to offer some nice error messages though, so I refused to quit! 🙂

I thought, “why can’t I just new up my validator manually”. I did this, and at first I wasn’t seeing what I needed. I eventually found that I had to get the result of validator.Validate(model) and then check result.IsValid. I was originally assuming I’d just be checking validator.IsValid. Anyway, here is what I had to that point:

var validator = 
    new Nop.Admin.Validators.Catalog
        .AddProductRelationshipAttributeValidator(_localizationService);
var result = validator.Validate(model);
if (result.IsValid == false) {
    // Do some error stuff here
}

The only thing now was how to get the result errors into the model state? I found a great thread on the FluentValidation discussion board that was asking the same thing – do I have to iterate the errors and manually add them to the ModelState? It turns out the answer is “yes”, but there is the handy extension method mentioned in the subject! TADA! So now, I have this nice bit of code:

var validator = 
    new Nop.Admin.Validators.Catalog
        .AddProductRelationshipAttributeValidator(_localizationService);
var result = validator.Validate(model);
if (result.IsValid == false) {
    Response.StatusCode = 400;
    result.AddToModelState(ModelState, "");
    return ModelState.JsonValidation(JsonRequestBehavior.AllowGet);
}

The ModelState.JsonValidation bit is another extension method I have to put the ModelState into a standardized JSON result that I then pass through a couple of js functions. Those functions make sure the validation passes, and if the validation does not pass it puts the errors in the validation summary manually. It is all pretty handy stuff.

If anyone has any better ideas, I am all ears!

Recent gists

I have saved a couple of code snippets lately and just wanted to add them to the blog too. This one enables “focus out” unobtrusive validation. I use this in a couple of forms on the site. Part of this gist also has a little code where I was messing around with “remote” validation to get it to work with the focus out validation – of course, I wasn’t thinking, just geeking out. The remote stuff is already baked in to jquery validation and I already had code to handle the focus out portion, so it wasn’t really worth the time, but I didn’t spend much on it. Anyway, the code:

window.Em = window.Em || {};

Em = {
    setFocusOutValidation: function (form) {
        var s = $.data(form, "validator").settings;
        s.onfocusout = function (element) {
            if ($(element).val().length > 0) {
                $(element).valid();
            }
        };
        s.showErrors = function (map, list) {
            this.defaultShowErrors();
            if (list && list.length > 0) {
                for (prop in map) {
                    $("#" + prop).focus();
                }
            }
            Em.displayValidationSummaryErrors(list);
        };
        return s;
    },
    displayValidationSummaryErrors: function (list) {
        $("[data-valmsg-summary]")
            .removeClass("validation-summary-valid")
            .addClass("validation-summary-errors");
        $("[data-valmsg-summary] ul").html("");
        $.each(list, function (idx, data) {
            $("[data-valmsg-summary] ul").append($("<li></li>")
                .html(data.message));
        });
    }
}

The focus out validation will check if the field is valid (also remotely) and if not, the focus will stay on the invalid field. Sometimes it can get a little wonky, but for the most part it works well. Typically when it doesn’t seem to “work” it is because the validation is setup backwards, like comparing a “new password” to the “confirm password”, rather than the other way around.

Gist

The next gist is a Telerik MVC grid extension I started messing with to handle filtering. Turns out I was making it harder than necessary, and there was already an extension method in the NopCommerce codebase. Oh well, here it is anyway:

public static IList<T> ApplyFilter<T>(this IList<T> list, 
                                      FilterDescriptor filter) {
 
    // We wont allow filtering on anything but string type properties
    if (filter.MemberType != typeof(string)) 
        throw new ArgumentException(@"Filtering is only allowed 
            for properties with a type of 'string'.");
 
    var value = filter.Value.ToString();
    switch (filter.Operator) {
        case FilterOperator.IsEqualTo:
            list = list.Where(x => {
                var propertyValue = x.GetType()
                    .GetProperty(filter.Member)
                    .GetValue(x, null);
                return String.Equals((string)propertyValue, value, 
                    StringComparison.InvariantCultureIgnoreCase);
            }).ToList();
            break;
 
        case FilterOperator.IsNotEqualTo:
            list = list.Where(x => {
                var propertyValue = x.GetType()
                    .GetProperty(filter.Member).GetValue(x, null);
                return String.Equals((string)propertyValue, value, 
                    StringComparison.InvariantCultureIgnoreCase) == false;
            }).ToList();
            break;
 
        case FilterOperator.StartsWith:
            list = list.Where(x => {
                var propertyValue = x.GetType()
                    .GetProperty(filter.Member).GetValue(x, null);
                return ((string)propertyValue).StartsWith(value, 
                    StringComparison.InvariantCultureIgnoreCase);
            }).ToList();
            break;
 
        case FilterOperator.Contains:
            list = list.Where(x => {
                var propertyValue = x.GetType()
                    .GetProperty(filter.Member).GetValue(x, null);
                return ((string)propertyValue).IndexOf(value, 
                    StringComparison.InvariantCultureIgnoreCase) > -1;
            }).ToList();
            break;
 
        case FilterOperator.EndsWith:
            list = list.Where(x => {
                var propertyValue = x.GetType()
                    .GetProperty(filter.Member).GetValue(x, null);
                return ((string)propertyValue).EndsWith(value, 
                    StringComparison.InvariantCultureIgnoreCase);
            }).ToList();
            break;
    }
 
    return list;
}

Gist

The last one is just a little quickie example of how to select data attributes with jQuery:

var d = $("input[data-toggle-class]");
$.each(d, function (idx, data) {
    console.log(idx, data);
});

Gist

I was originally looking for a way to select a wildcard in the data attribute, but I couldn’t get it working. Anyway, this works fine – though I did not actually end up using it. 🙂

FluentValidation NotEqual client-side validation

I wanted client-side validation for NotEqual. I started out writing my own and got 90% of the way there, then I got a little stuck. A quick Google revealed an existing Gist – however, the original implementation was only for NotEqual property comparison and I need a value compariosn. So, I forked it and made it work for my needs.

(function ($) {
    $.validator.addMethod("notequal", function (value, element, param) {
        if (param.indexOf("#") == -1) return value != param;
        return value != $(param).val();
    }, $.validator.messages.notequal);

    $.validator.unobtrusive.adapters.add("notequal", ["field"], 
    function (options) {
        options.rules["notequal"] = options.params.field;
        if (options.message) options.messages["notequal"] = options.message;
    });
})(jQuery);
FluentValidationModelValidatorProvider.Configure(provider =>
{
    provider.Add(typeof(NotEqualValidator), 
        (metadata, context, description, validator) => 
        new NotEqualClientRule(metadata, context, description, validator));
});
@model Test.Models.PersonModel
@using (Html.BeginForm())
{
    @Html.TextBoxFor(x => x.First)
    @Html.ValidationMessageFor(x => x.First)
    @Html.TextBoxFor(x => x.Last)
    @Html.ValidationMessageFor(x => x.Last)
    <button type="submit">OK</button>
}
[Validator(typeof(PersonValidator))]
public class PersonModel
{
    public string First { get; set; }
    public string Last { get; set; }
}
public class PersonValidator : AbstractValidator<PersonModel>
{
    public PersonValidator()
    {
        RuleFor(x => x.First).NotEqual(x => x.Last);
    }
}

Gist

When I look back at it, the whole hash check bit in the js just feels icky, so I think I’d go back and fix it up – probably just check to see if the jQuery selector exists, and if so compare .val() otherwise just compare the value. Whatever, it works for now even if it isn’t perfect…

A cassette gem!

Man, I cannot believe I did not know about this before. Cassette has some pretty decent documentation, and I have combed over it, but either this was never listed before or I just missed it. I am using v1.2, and I recently noticed there is a v2. In the upgrade notes, there is a mention that the diagnostics page moved. I had no idea there was a diagnostics page!

For example http://localhost:2452/_cassette#stylesheets (debugging locally, obviously) produces this nice little dashboard:

Cassette diagnostics dashboard

Cassette diagnostics dashboard

I wish I would have found this a few months ago…

Silly little CSS banner demo

A co-worker that has some light HTML/CSS experience asked about using some banners where she could rotate text. I cam up with this:

HTML

<div class="banner">
	<a id="banner-link" href="#">I am a cool banner!</a>
</div>
<br />
<div class="banner">
	<a id="banner-link-2" href="#"><span>I am also cool!</span></a>
</div>

CSS

.banner {
	background: url("/wp-content/uploads/2012/10/banner.png") 
	    left top no-repeat;
	width: 300px;
	height: 75px;
	overflow: hidden;
}
#banner-link {
	font-size: 2em;
	display: block;
	padding: 5px 0 0 150px;
	line-height: 1.1em;
	height: 75px;
	text-decoration: none;
	color: #FFF;
}
#banner-link-2 {
	font-size: 2em;
	display: block;
	line-height: 1.1em;
	height: 75px;
	text-decoration: none;
	color: #FFF;
	position: relative;
}
#banner-link-2 span {
	position: absolute;
	top: 5px;
	right: 5px;
	width: 135px;
}

Put it all together…

Uses padding

Uses positioning

Remote jQuery validation

I just spent too much time banging me head against the wall trying yo get remote validation working. Each of the examples I found just did not seem to work, until I found this post from jquery4u.com. It used an interesting syntax that I had not seen anywhere. Until I found that post, all the examples used remote as an attribute under “rules”, but I could not seem to get it to work. The jquery4u syntax worked.

When I see multiple examples that I cannot get to work, I can never seem to let it go, so I started trying to get that syntax to work. I finally realized that, even though I had seen examples where data was passed, the way to get it to work was just to specify the URL and let jQuery validation do the rest. It creates a query param using the name you specify in the validator (so essentially your input name) as well as the value of the input. I finally got that working. This post from develoq.net helped set me straight.

My final struggle was to get unobtrusive validation working as well as an error message in my validation summary. I found how you can decorate an attribute on your model, but we use localization, and that just doesn;t work. We use FluentValidation, and that allows us to use our localization strings, so I wanted to see if FV could handle remote. It turns out it isn’t out of the box, but I found this post where a user created his own RemoteValidator.

It too me a while, but I knew it would and should all work!

MultiSelect with checkboxes

More work on my nopCommerce plugin. This time, I wanted to offer more than the standard multi-select listbox. I thought it would be nice if the list had checkboxes. There are an infinite number of smarter people out there than myself, so I Googled knowing someone had some javascript goodness. I found the jQuery UI MultiSelect Widget. This is an awesome little plugin that is themable using jQuery UI. I know that nopCommerce doesn’t use jQuery UI in the admin (it is a Telerik MVC “shop”), but I still appreciate the ability since I really love the jQuery UI stuff.

MVC Templates

I am creating a plugin for our nopCommerce site, and I wanted to use a rich text editor for my admin page. I started looking at how to use the Telerik MVC editor, but then I realized I should be looking at an editor template. I found a great post by Brad Wilson about MVC templates.

This got me thinking. We opted to switch the rich editor from Telerik to CKEditor (for file upload ability) and when we did that I made the change by creating a new shared editor template. Since this plugin is ultimately used within the admin section, I thought I may be able to make use of the existing editor template. Sure enough, I could. Adding a simple template name parameter to the Html.EditorFor did the trick.

Conditional validation

Related to my recent post about cascading drop down lists, I needed to do some conditional validation based on a drop down list selection. I found a great post on Simon Ince’s blog about conditional validation in MVC 3. He even has a new project to wrap up some of the conditional functionality that he blogged about, but I decided to roll my own since I would learn more going that route.

Using the project attached to his post, I started dissecting what he was doing. After a while, I got the client validation working, but I realized I was so focused on the client/server validation piece that I did not get my conditions correct! After a few tweaks, I finally got everything working. Here is the custom attribute:

public class ConditionalMaximumWeightAttribute : ValidationAttribute, 
                                                 IClientValidatable {

    private const string ERRORMSG = "Weight must not exceed {0} lbs.";

    public string DependentProperty { get; set; }
    public string DependentValue { get; set; }
    public int MaximumWeight { get; set; }

    public ConditionalMaximumWeightAttribute(string dependentProperty, 
                                             string dependentValue, 
                                             int maximumWeight) {
        this.DependentProperty = dependentProperty;
        this.DependentValue = dependentValue;
        this.MaximumWeight = maximumWeight;
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
                         ModelMetadata metadata, ControllerContext context) {
        var rule = new ModelClientValidationRule() {
            ErrorMessage = String.Format(ERRORMSG, this.MaximumWeight),
            ValidationType = "maximumweight",
        };

        string depProp = BuildDependentPropertyId(metadata, context 
                                                            as ViewContext);

        rule.ValidationParameters.Add("dependentproperty", depProp);
        rule.ValidationParameters.Add("dependentvalue", this.DependentValue);
        rule.ValidationParameters.Add("weightvalue", this.MaximumWeight);

        yield return rule;
    }

    protected override ValidationResult IsValid(object value, 
                                        ValidationContext validationContext) {
        // get a reference to the property this validation depends upon
        var containerType = validationContext.ObjectInstance.GetType();
        var field = containerType.GetProperty(this.DependentProperty);

        if (field != null) {
            // get the value of the dependent property
            var dependentvalue = 
                field.GetValue(validationContext.ObjectInstance, null);

            var weight = 
                containerType.GetProperty(validationContext.DisplayName);
            int weightvalue = 
                (int)weight.GetValue(validationContext.ObjectInstance, null);

            // compare the value against the target value
            if (dependentvalue == this.DependentValue && 
                                  weightvalue > this.MaximumWeight) {
                // validation failed - return an error
                return new ValidationResult(String.Format(ERRORMSG, 
                                            this.MaximumWeight));
            }
        }

        return ValidationResult.Success;
    }

    private string BuildDependentPropertyId(ModelMetadata metadata, 
                                            ViewContext viewContext) {
        // build the ID of the property
        string depProp = viewContext.ViewData.TemplateInfo
                         .GetFullHtmlFieldId(this.DependentProperty);
        // unfortunately this will have the name of the current field appended 
        // to the beginning,
        // because the TemplateInfo's context has had this fieldname appended 
        // to it. Instead, we
        // want to get the context as though it was one level higher (i.e. 
        // outside the current property,
        // which is the containing object (our Person), and hence the same 
        // level as the dependent property.
        var thisField = metadata.PropertyName + "_";
        if (depProp.StartsWith(thisField))
            // strip it off again
            depProp = depProp.Substring(thisField.Length);
        return depProp;
    }
}

Gist

To put this in context, I need to restrict a maximum weight allowed for a carrier. At a certain point, we may as well go with another carrier because it is cheaper. However, I don’t want to restrict the maximum weight if the carrier that has been selected is the cheaper carrier.

The attribute is used like so, where PropertyName is the name of the related property to check, PropertyValue is the conditional value of the property, and MaximumWeight is an integer representing the maximum weight for the conditional:

public class ...

    [ConditionalMaximumWeight("PropertyName", "PropertyValue", MaximumWeight)]
    [Required(ErrorMessage = "Weight required")]
    public int? Weight { get; set; }

    ...
}

Here is the client-side implementation:

<script type="text/javascript">
    $.validator.addMethod("maximumweight",
        function (value, element, parameters) {
            var carrier = $("#" + parameters["dependentproperty"]).val();
            var carriervalue = parameters["dependentvalue"].toString();
            var weightvalue = Number(parameters["weightvalue"]);
            if (carrier == carriervalue && value > weightvalue) {
                return false;
            }
            return true;
        }
    );

    $.validator.unobtrusive.adapters.add(
    "maximumweight",
    ["weightvalue", "dependentproperty", "dependentvalue"],
    function (options) {
        options.rules["maximumweight"] = {
            weightvalue: options.params["weightvalue"],
            dependentproperty: options.params["dependentproperty"],
            dependentvalue: options.params["dependentvalue"]
        };
        options.messages["maximumweight"] = options.message;
    });
</script>

Gist

I should probably do a little null checking in on the client-side, but I may just find that out the hard way! 🙂