ng-okevin's Angular ch.5 - Forms
3 Apr 2014
The form, input, select, and textarea elements are augmented (as directives) in Angular for added sauce. Angular forms have a built-in awareness of their state, such as whether data has been inputted or whether the forms are valid. Angular forms provide
- two-way data binding with the model.
- form state.
We have covered the two-way data binding in ch.1 AngularJS. Form state, however, can be used to conditionally
- enable or disable form controls (e.g. buttons).
- style the form.
With directives though, we can also achieve
- custom form validation.
- custom form controls.
Form State
A bit about the backend. Angular forms and inputs are instances of
FormController. As instances of FormController, form and input
directives contain a handful of useful properties that represent form state.
$pristine: True if the user has not yet interacted with the form$dirty: opposite of$pristine$valid: True if all of containing forms and controls are valid$invalid: opposite of$valid$error: a JS object referencing invalid forms or a mapping of error names to booleans
$error is a bit confusing as it has two faces. If getting the $error
attribute from a form’s FormController, it will be a reference to invalid
form controls.
{"required": [{}], "max": false}
Else if getting the $error attribute from an input’s
FormController, it will be a mapping of error names to booleans (with
“true” indicating an error).
{"required": false, "max": true}
To reference all these properties though, the form and input directives need to
be given a name attribute to register it into scope. We can then use
these properties to drive form interaction and behavior.
Example
In Muffin Button, we will create a button that dispenses a number of muffins to demonstrate form states.
<form name="muffinForm" ng-submit="muffins = muffinNum">
<input id="muffinNum" type="number" name="muffinNumInput"
ng-model="muffinNum" min="1" max="255">
<button ng-disabled="muffinForm.muffinNumInput.$invalid">
Dispense Muffins
</button>
<span ng-show="muffinForm.muffinNumInput.$error.max">
Muffin Overload!
</span>
<img src="muffin.png" ng-repeat="i in [] | range: muffins">
</form>
Muffin Button contains a number input field. Given a number, it dispenses or displays a respective number of muffins when pressing the muffin button. We use form states to disable the muffin button on invalid and non-number inputs, and we also use it to display a specific error message in the case that too many muffins are requested.
We disabled the button with the ng-disabled directive when our
HTML5 number input field is invalid. We give the input field the name
muffinNumInput. That name can then be referenced from the
muffinForm object in the scope as muffinForm.muffinNumInput. Our
number field dictates whether the form is invalid. If the input is not a
number, is under the min, or over the max,
muffinForm.muffinNumInput.$invalid will be switched on.
However, $invalid is general catch-all flag. If we want make use of more
detailed form states, we can make use of the $error attribute to see
specific error flags. In the example, we display an error message with the
ng-show directive when the number input field contains too large of a
number. In that case, the max flag will be set and can be referenced by
### Styling the Form Based on Form State
We can style elements differently based on form states. It is nothing new, we
simply use form states in conjunction with ```ng-class```. With that, we can
give the number input element a red error border when the input is invalid and
green when valid.
As a side thriller, we use ```$pristine``` to not apply these classes when the
form has not been touched. The border color would then initially default to
gray.
```html
Up Next
Scopes, controllers, and templates lay the foundation of the cake. Now we can start decorating with the frostiest part of Angular, Directives, where I demonstrate hands-on how to build a mobile slider.