Xamarin MVC Project Separation & Fluent Validation – Part II

Continuing from Part I:

Fluent Validation 

So now we’ve got a project setup that shared our models and services, it’d be great to same the same validation for inputs or fields with minimum effort across multi-platforms.  Meaning if a “FirstName” is required, it would be great to not code that three separate times (Web, Android, and iOS).  With Fluent Validation we can achieve this with a little bit of setup on the mobile side.  On an .NET MVC site integrating FluentValidation is as easy as installing the correct NuGet package for your version of MVC.  It will integrate with un-obtrusive jQuery Validation automatically.  With Xamarin.Forms currently there is no solution to integrate so nicely.  However, I’ve created a framework that allows you to do something similar and have it work across the three platforms.

So one of the things you need in order to show validation is a Label, which will serve as our “Html.ValidationMessageFor” control in the web world and that label needs to be associated with some type of input.  Within the .NET MVC Platform this is achieved by a naming convention, however, we can’t necessarily achieve the same thing in Xamarin.Forms when we create our views via code, not XAML; but we can create our own naming convention!

Before we dive into the code, you should know that under normal circumstances you don’t new up a “Validator” (term for class that extends AbstractValidator) but you can! When you do there is a method you’d call called “Validate()”.  This method returns a “ValidationResult” with two properties: “IsValid” and “Errors” and “Errors” is a collection of “ValidationFailure”, which contains the Name and ErrorMessage for a particular property.  Knowing that we can create our own “unobtrusive” validation setup.  Lets begin!

One of the first things we need to setup is some type of control that we can know needs to be validated. Currently, Xamarin offers something called “Behaviors” which can validate a single input and you associate the “Behavior” to the type of validation you need.  If you needed validation for an e-mail address, you could create a Behavior that does this as the user types.  The only caveat is that it is now limited to only e-mail addresses, well we want something more flexible.  So let’s dive into the code:

IValidatableControl

public interface IValidatableControl
{
	string Identifier { get; set; }

    Entry Entry { get; set; }

    Label Message { get; set; }

    void SetMessage(string message);

    void SetMessageColor(Color color);

    void Clear();
	
	bool HasError { get; set; }
}

We create an interface that defines what a “Valdiatable” control needs to entail.  We have an “Identifier” which is what we will use as our naming convention, an “Entry” which will serve as our input.  The “Entry” could be replaced as the base type “View” but for this we want an actual text box to work with.  Next we have a Label that will serve as our error message label or “Html.ValidationMessageFor()”.  Then we have a couple of methods to set the message, error color, and the ability to clear the error.

Next, we need to implement the IValidationControl, so we lets do so:

Validatable Control Implementation

	public class ValidatableControl : ContentView, IValidatableControl
    {
        public string Identifier { get; set; }

        public Entry Entry { get; set; }

        public Label Message { get; set; }

        public bool HasError { get; set; }

        public ValidatableControl(string propertyName)
        {

            Identifier = propertyName;

            Entry = new Entry();

            Message = new Label { IsVisible = false };


            var stackLayout = new StackLayout()
            {
                Orientation = StackOrientation.Vertical,
                Children =
                {
                    Entry,
                    Message
                }
            };


            this.Content = stackLayout;
        }

        public void SetEntryTextBinding(string propertyName)
        {
            Entry.SetBinding(Entry.TextProperty, propertyName);
        }

        public void SetMessage(string message)
        {
            Message.Text = message;

            HasError = true;

            Message.IsVisible = true;
        }

        public void SetMessageColor(Color color)
        {
            Message.TextColor = color;
        }

        public void Clear()
        {
            HasError = false;

            Message.Text = "";

            Message.TextColor = Color.Default;
        }
    }

Here we have it! Our validatable control, which is actual a “View” in Xamarin.Forms lingo. The reasoning behind this is so we can keep a clear association of Entry and Label together.  Now the setup is pretty straight forward, we use a StackLayout to simply have the label below the input; however, we set the visibility property to false so our label doesn’t take up space. So now we need that backbone of our validation system, since we don’t have anything that does this for us out of the box.

 

Validation Page

So let me preface this by saying this does couple your Xamarin Page tightly with Fluent Validation, but that isn’t necessarily a bad thing.  After pondering how I’d like to use validation and coming up with some compromises I ended up settling on creating a base page that I will extend when ever I need a page with validation.   Lets dive into the code piece by piece:

Class Decleration:

	public abstract class ValidatorPage<T, TValidator> : ContentPage where TValidator : IValidator, new() where T : class, new()
	{ }

This is the key and is extremely important to understand.  First, we declare the class as an abstract class, since we’re providing some default functionality and we don’t ever want to initialize this class on its own.  Next, we create two generic declarations; the first declaration is of “T” where “T” is a model from our “*.Models” project and the second declaration is our Validator class and we use the constraint that it must be of type “IValidator”.  This way we ensure that it is a validator type of Fluent Validation.

Properties:

 

	public IBaseType<T> Model { get; set; }

	private readonly HashSet<IValidatableControl> ValidatableElements = new HashSet<IValidatableControl>();

These two properties are everything we need.  An IBaseType<T> is what all of our ViewModels will implement from and following MVVM patterns this blends nicely. Next we have a HashSet or List of IValidatableControls, so this will be our list of controls that we may validate against.  Now if you have done any Xamarin.Forms development you’ll probably say: “I’m not going to remember to add my control to this list!” and you’re absolutely right! You won’t!  However, I’ve made a helper method that accomplishes two things: 1.) adds the control to the list of elements and 2.) adds the control to your layout.  Lets look:

 

	public void AddValidatableElement(Layout layout, View control)
        {
            layout.Children.Add(control);

            var item = control as IValidatableControl;

            if (item != null)
            {
                ValidatableElements.Add(item);
            }
        }

Here I’m allowing you to use any control and any layout you’d like but I check if the control being added is an IValidatableControl and if it is I add it to the list for you.  Next we’ll look at the actual method that does the validation:

Validate Method:

	public ValidationResult Validate()
        {
            var validator = new TValidator();

            var result = validator.Validate(Model.BaseType);

            
                var controlsHavingValidation = ValidatableElements.Where(a =&gt; a.HasError);

                foreach (var control in controlsHavingValidation)
                {
                    control.Clear();
                }
            if (!result.IsValid) {
                foreach (var error in result.Errors)
                {
                    var control = ValidatableElements.FirstOrDefault(a =&gt; a.Identifier == error.PropertyName);

                    if (control != null)
                    {
                        control.SetMessageColor(Color.Red);

                        control.SetMessage(error.ErrorMessage);
                    }
                }
            }

           return result;
        }

So here’s where everything comes together.  First, we initialize our TValidator then we pass in our ViewModel.BaseType (we’ll review this later) to the Validate method.  This returns that ValidationResult I mentioned earlier and now we can get a handle on the set of controls by looking for the Identifier, which is the property name we set when initializing the control.  Then we can set the message and color of the text from this method.   This method is essentially all we need to validate that our model is in fact valid.

 

Usage

Now that we have our infrastructure in place we need to actually use it!  To demonstrate this in practical use, I’m going to implement a “UserProfile” page in our mobile application.  For simplicity this page will consist of a input field and a button to validate the input against one of our validation rules.  Lets look at the code:

	public class UserProfilePage : ValidatorPage&lt;UserProfile, UserProfileValidator&gt;
    {
        
        public ValidatableControl FirstNameEntry = new ValidatableControl("FirstName");

        public UserProfilePage()
        {
            Model = new UserProfileViewModel();

            this.BindingContext = Model;

            FirstNameEntry.SetEntryTextBinding("FirstName");

            var button = new Button()
            {
                Text = "Click Here"
            };

            button.Clicked += (sender, args) =&gt; Validate();

            var layout = new StackLayout()
            {
                Orientation = StackOrientation.Vertical
            };

            AddValidatableElement(layout, FirstNameEntry);

            AddValidatableElement(layout, button);

            this.Content = layout;
        }
    }

Simple as that, now normally you’d have more of a “submit” type functionality for the button click event but this at lease shows you what you can do with it.

Continue to Part III

Leave a Reply

Your email address will not be published. Required fields are marked *