We want robust validation for our AngularJS models. This will require a bit of forethought, and this lesson will examine the goals that we have for the validation piece of our model library.
The next bit of functionality we're going to add to our base class is validations. This is a bit of a different approach from the way that Angular implements validations out of the box, as you may know.
Angular implements validations as directives. For example, if I have a post model and the "title" attribute is always a required field, I have to add this required directive to every single input where the ng model would be "post.title."
The reason I don't like Angular's approach -- actually, there are a number of reasons I don't like this approach. The first is that it requires us to duplicate this code over and over again.
Instead of saying in one location that the post title is always required, on every single input we have to remember to add this required validation. There's a lot of room for us to screw that up, or if we have to make changes, to forget which places we have to make changes to.
Another reason that I would prefer for our models to know this information is because the models actually need to know this information. The model has to be able to know that it is valid or invalid. If it's invalid it shouldn't save, or attempt to save, this input.
Then also, our inputs can just react to model changes. So, if we want to say on ng-change, or on ng-blur, for instance, "validate this field on this model," the input is able to react without needing to know what it means to be valid. It just needs to know whether the field is valid or invalid.
Then, further below, we can list out errors. We can say "post.errors.title" and loop through each of the errors that may or may not be within there. We'll update the errors hash whenever we update or validate this model input, so it will remove or include errors, depending on the value of the input.
That's the 10,000-foot view as to why we're creating this validations library and including it in the base class. Now let's move into a bit lower-level detail as to what exactly we're planning on creating.
The first piece that we're planning on creating in this validatable library, similar to the cacheable library that will be mixed into our base class, is a validator.
I've created a bit of a readme here, as to what I'm intending to create. Validators are going to be registerable, configurable constructors that return validation functions.
What that means is that in our application we'll be able to create something like a "min" validator. This will simply be a function where we can say that the value provided has a length that's greater than or equal to whatever the "min" configuration happens to be for that validation.
If I have a post class and I say that it validates its title with a min length of five, then this function will be configured to use the value "5" for this.min.
At an even lower level, what that means is that min validator is going to have this configure function. We're able to pass it either just the value "5," or some more details where we can override the default message.
We're also going to allow our users to define arbitrary configuration options.
If, for example, someone created a numericality validator, and that had an "ignore" option with a regular expression that was ignoring dollar signs at the start of words -- perhaps to create a money validation, in this case -- the numericality function would get passed in this.ignore to check, and replace that, and then check whether or not the value is a number.
Moving further down, this is a bit of how we're defining our DSL, or Domain-Specific Language. For instance, we might have a collection of validators that are under the same header, so I might say that the title has a length with a minimum validation of five and a maximum validation of 10.
This means that this length validator is going to have several child validators -- min and max -- and these are going to be infinitely configurable.
We could also have "is" -- we could say it's exactly seven -- or we could say "in: _.range(1, 20)" something like that. There would be an infinite number of options that we could add or remove here, and plug and play with. That's the functionality that we'd ultimately like to expose.
The way I see this working is that you'll have a factory for each of the children, so you'll create your min validator and your max validator.
Then we'll have a factory for our length validator. That will simply define options. Those options will include the min and max validator, so we'll use the dependency injection system to grab hold of those and simply expose them as options.
In each case, we're including this validator and exposing it to the client, so that users of our library can define new validators as they like to.
Furthermore, we want to just take a look at how this is going to interact as a whole with the validatable module.
We have this validator idea and we're also going to have the idea of a validation, which is going to marry a field with a validator, and the validator is going to be configured at this point.
After the configuration phase, each field will have its collection of validators that is described by each of these validates. For instance, "title" will have an array of two validators, a min and a max validator. That's how this is ultimately going to interact with the rest of our library.
I know that was a lot of talk and not a lot of code for one episode, but I think it's important for us to have a good idea of where we're going, before we go there. That way, we're not moving around blindly or building pieces that we don't really see how they contribute to the whole.
To speak at an even higher level of how the validatable library contributes to the whole, you are probably noticing at this point that we're building up each of the component pieces of our base class, so that we're going to allow them to integrate.
We haven't even gotten into the new, the update, the save functionality -- a lot of the things that we probably think of as the core of a library on modeling -- but each of these component pieces is going to be necessary in order for us to accurately define the bigger pieces of the library.
That's it for this episode -- I'll see you in a bit.