Sunday, 26 October 2014

Use input[type="checkbox"]

AngularJs overrides a few standard HTML elements as element based directives. Most of these are HTML5 input types, which come with a few extra attributes that are really clever and powerful. I was completely unaware of these until I started trawling through the documentation. Once I started looking into them I couldn't believe how useful these are, and I'm quite annoyed that I haven't made better use of them in my commercial products before now. There's too much to talk about in one blog post, so I'll make the next few posts about these. Form has some extra gubbins that deserve talking about separately, and I think that having some understanding of the input types will help with understanding them better. I'll start with the checkbox.

HTML to Write

<input type="checkbox"
name="checkbox"
ng-model="checkboxValue"
ng-true-value="'Checkbox Checked'"
ng-false-value="'Checkbox Unchecked'"
ng-change="toggleCheckbox()"/>


AngularJs uses the type attribute to trigger the specific directive. The name attribute is optional, but if you want to be able to programatically query anything about the input you need to have it. You may not want to query anything about a checkbox thanks to the directive's other attributes, but I think that it's very handy for other input types. ngModel declares the $scope variable that the value of the input should be assigned to.

I think the next two attributes are just fabulous, and as all the best things in AngularJs do, they remove the need for me to write a set of repetitive code that I've written many times before. ngTrueValue and ngFalseValue contain the values that should be assigned to the ngModal variable depending on the state of the checkbox. If checked then assign the value of ngTrueValue to ngModal, if unchecked then assign the ngFalseValue value. The uninitiated state of the checkbox is false, but the ngModal variable will not have the ngFalseValue assigned to it, it will be an empty string. Due to this it is best that you assign a default value in the controller to initialise it. The checkbox will see whether this value corresponds to either it's ngTrueValue or ngFalseValue, and set the checkbox to the corresponding state on load. Watch out when writing these values into the attribute's value; it has the single quotes in double quotes gotcha. You can either put the string in directly as shown above, or you can use $scope variables in the controller, which would look like this in the HTML:

ng-false-value="'{{checkboxFalseValue}}'"

The last attribute is ngChange. This should contain an expression to be executed every time the user changes the value of the input. In the case of checkbox this is when the user checks or unchecks the box. In the example above I have a function call to a $scope function. Again, I think that this is going to be more useful on the other input types, but it's nice to have the option of doing something when the user changes the input.

Generated HTML

<input type="checkbox"
name="checkbox"
ng-model="checkboxValue"
ng-true-value="'Checkbox checked'"
ng-false-value="'Checkbox unchecked'"
ng-change="toggleCheckbox()"
class="ng-pristine ng-untouched ng-valid">


This is the HTML that is generated in the browser once the page loads. As you can see most of it is as written. The classes are where I think this starts to get interesting.

CSS Classes

AngularJs assigns different classes to the input depending on the state of the input and how the user has interacted with it. It pretty much has all the bases covered, which is great for us interface developers. Thanks to these automatically assigned classes we can concentrate on providing an intuitive user experience without having to worry about building all the code to check and handle the user's interaction with it. I know I could have produced many better forms if I'd had AngularJs to work with at the time thanks to these shortcuts, but when there's a deadline and budget constraints it's the nice-to-haves like this that get cut. These classes come from ngModel.NgModelController.

class="ng-pristine ng-untouched ng-valid"

ngPristine means that the user hasn't yet interacted with the input.

ngUntouched means the input hasn't lost focus yet. This could be a bit of a gotcha as it could mean that the input hasn't had focus to lose, or that the input has focus currently and for the first time and therefore hasn't lost it yet.

ngValid means that the state of the input is valid. In a checkbox it's hard to have an invalid state, but there are plenty of input types that need validation on the client side.

If the user tabs straight over your input, then these are the classes you get:

class="ng-pristine ng-valid ng-touched"

ngPristine remains because the user didn't change the state of it.

ngTouched means that the input has lost focus, and of course it has. The user gave it focus when they tabbed into it, and it lost focus when they tabbed out.

If your user clicks the input, then we see these classes:

class="ng-valid ng-dirty ng-valid-parse ng-touched"

ngDirty means that the user changed the state of the input, in this case by clicking to check or uncheck the box. I can see some usefulness in this state for data analytics. It would be interesting to see if a user pondered opting in/out and then changed their mind and reverted the checkbox back to it's original state.

ngValidParse means, well, I'm not sure. I've looked everywhere and I can't find any documentation for this and no one else is talking about it. I'm assuming that it means that all the developer defined validators (the parsers) have returned that the input value is valid.

Note: Normally I would prefix all the ng-* attributes with data- in order to make the HTML valid. I omitted this time for the sake of clarity.

1 comment: