Angular Agility was written by John Culviner and is used in AngularJS websites to reduce the amount of HTML that developers need to write.  It also gives nice validation options – like specifying the form validation in the controller instead of on the input tag.

You might be wondering about the title of this blog post…what are “Field Group Strategies”?  A Field Group Strategy is an Angular Agility term that gives developers like me an extension point to create new field groups (i.e. a div with a label, input field and validation messages).  You can see a demo of the default field group strategy here.  It includes input tags with different types, like number, radio, text, etc.

These are the 6 new field group strategies I created in the aaCustomFGS module:

  • checkBox – easy one line checkbox
  • currency – $ in front of input text field
  • datePicker – easy one line date picker
  • percent – % at end of input text field
  • radio – The Angular Agility version is still too much HTML
  • select2 – Select2 made easy (dropdown with type ahead and ajax support)

They look like this:

6FieldGroupStrategies2

Code for the new strategies (use js and css in dist for your project): https://github.com/IntertechInc/aa-fieldgroup-strategies

Plunker demo:  http://plnkr.co/edit/rLe4N3IIsvYQupFPHR24?p=preview

PREREQUISITES

The first five strategies are dependent on using Angular UI Bootstrap…which implies the use of Bootstrap 3.  Select2 requires that you include the full jQuery Select2 library.

VIEW

This is how you would use these new strategies in the HTML:

<div aa-configured-form validation-config="formconfig" class="form-horizontal" ng-form="aatest">
    <input aa-field-group="model.BirthDate" aa-field-group-strategy="datePicker" />
    <input aa-field-group="model.IsActive" aa-field-group-strategy="checkBox" />
    <input aa-field-group="model.Salary" aa-field-group-strategy="currency" />
    <input aa-field-group="model.Percentage" aa-field-group-strategy="percent" />
    <input aa-field-group="model.State" aa-field-group-strategy="select2" config="stateSingleConfig" />
    <input aa-field-group="model.Gender" aa-field-group-strategy="radio" options="radioList" />
    <button aa-submit-form="submit()" type="submit" class="btn btn-primary">Submit</button>
</div>

Those of you new to AngularJS may not be able to appreciate the win that one line of HTML really is.  With that one line, you get a div that wraps a label, an input field and validation (that is set up in the controller, more about that later.)  Normally, that would take at least 10 lines of HTML – most of which are repetitive.

The first line of the HTML defines the Angular Agility aware form.  ‘aa-configured-form’ tells Angular Agility to look for validation in the controller code.  ‘validation-config’ is where to look in the $scope of the controller.  In other words, set the validation JSON object to ‘$scope.formconfig’. See here for the validation format that Angular Agility expects.

positioning label and input

You will notice in the above HTML that I did no Bootstrap cols to format their position.  That is because Angular Agility defaults that for you to the following:

  • Label – col-sm-2
  • Input – col-sm-3

Of course you can override the label and input columns by using attributes on the input tag.

  • Label
    • aa-lbl-col – specify Bootstrap col class (i.e. col-sm-2)
    • aa-lbl-class – specify css class, defaulted to form-label if nothing specified at all

The LESS for form-label (included as CSS in aaCustomFGS.css):

.form-label {
    @media (min-width: @screen-sm-min) {
        .make-sm-column(5);
    }
    @media (min-width: @screen-md-min) {
        .make-md-column(4);
    }
}
  •  Input
    • aa-col – specify Bootstrap col class (i.e. col-sm-3)
    • aa-class – specify css class, defaulted to form-input if nothing specified at all

The LESS for form-input (included as CSS in aaCustomFGS.css):

.form-input {
    @media (min-width: @screen-sm-min) {
        .make-sm-column(7);
    }
    @media (min-width: @screen-md-min) {
        .make-md-column(8);
    }
}

overriding default label

Angular Agility defaults the label text to the name of the model field.  For example, model.Name or model.name gets a label of ‘Name’.  Also, if the field is camel or Pascal cased (i.e. BirthDate), a space is inserted, giving ‘Birth Date’.  Note that if the field is required, an asterisk is automatically added to the label.

If you wish to override this default behavior, simply specify the aa-label attribute.  For example, aa-label=”Date of Birth”.

APP MODULE DEFINITION

Before any of this code works, the ‘app’ needs to know the necessary module dependencies.

angular.module('app', [
    'ui.bootstrap',
    'aa.formExtensions',
    'aa.formExternalConfiguration',
    'aaCustomFGS',
    'aa.notify',
    'aa.select2'
]);

The dependencies:

  • ui.bootstrap – Angular UI Bootstrap
  • aa.formExtensions – Angular Agility form extensions module
  • aa.formExternalConfiguration – Angular Agility module that allows us to define validations in the controller code
  • aaCustomFGS – The 6 custom field group strategies that are the focus of this article
  • aa.notify – Angular Agility module for notifications to the user
  • aa.select2 – Angular Agility module for select2 support
  • All of the “aa.” modules are included in the Angular Agility js file

CONTROLLER

Notice the config for the select2 strategy and the options for the radio strategy in the HTML above. Those must be set in the controller code.  Also, the controller code must set the validation up as a JSON object.

(function () {
    'use strict';

    angular.module('app').controller('angularAgilityTestController', angularAgilityTestController);

    angularAgilityTestController.$inject = ['$scope', 'aaSelectService', 'aaNotify'];

    function angularAgilityTestController($scope, aaSelectService, aaNotify) {
        $scope.model = { };
        $scope.states = [{ 'name': 'Minnesota', 'id': 'MN' }, { 'name': 'Wisconsin', 'id': 'WI' }];
        $scope.radioList = [{ name: 'Male', id: 0 }, { name: 'Female', id: 1 }];

        // Gives us our validations for the form.
        $scope.formconfig = {
            validations: {
                model: {
                    , BirthDate: {
                        required: true
                    }
                    , Salary: {
                        required: true,
                        'ng-pattern': '/^(\\d+\\.?\\d*|\\.\\d+)$/',
                        'ng-pattern-msg': 'Salary must be a decimal number'
                    }
                    , Gender: {
                        required: true
                    }
                    , State: {
                        required: true
                    }
                    , Percentage: {
                        required: true,
                        'ng-pattern': '/^[0-9]*$/',
                        'ng-pattern-msg': 'Percentage must be an integer'
                    }
                }
            }
        };

        // Use a json array constant to get options.
        $scope.stateSingleConfig = aaSelectService.getConfigSingle({
            options: $scope.states,
            placeholder: 'Select a state...'
        });

        $scope.submit = function () {
            aaNotify.success('Form successfully submitted');
        }
    }
})();

Note the following about the preceding code:

  1. Initialize the $scope.model for the two-way data binding
  2. Set $scope.states to set up the select2 search, this doesn’t initialize select2 but is a parameter to the getConfigSingle function of the aaSelectService
  3. Set $scope.radioList to set up the radio button group, this is what ‘options’ is set to in the HTML for the radio strategy
  4. Set $scope.formconfig to set up the validation for the fields that have validations, note that ‘model’ must be the name of the object that the form is binding to
  5. All messages come with defaults, you can override them all by appending ‘-msg’ to the end of the validation, for example, you could say ‘required-msg’: ‘Another required message’ to override the default
  6. Set $scope.stateSingleConfig using the aaSelectService to configure the select2 strategy

SERVICE

The aaSelectService was created to simplify the setup of the config JSON object for the select2 strategy.  Find the docs for Angular Agility’s Select2 here.  If you look at those Select2 docs, you’ll see there are some properties we can hide in the service.

This is the interface for the aaSelectService:

var service = {
    getConfigSingle: getConfigSingle,
    getConfigAjaxSingle: getConfigAjaxSingle,
    getConfigAjaxMultiple: getConfigAjaxMultiple
};

The parameter to all of these methods is ‘config’ and it allows overrides of defaults.

  • getConfigSingle sets the following properties:
    • mode = ‘id’
    • id = ‘id’ or config.id
    • text = ‘name’ or config.name
    • options = [] or config.options
    • select2 = { allowClear: true, minimumInputLength: 0, maximumInputLength: undefined, placeholder: ‘Select…’ } or override each one by passing in config.allowClear = false, etc.
  • getConfigAjaxSingle sets the following properties:
    • mode = ‘id’
    • id = ‘id’ or config.id
    • text = ‘name’ or config.name
    • select2 = { allowClear: true, minimumInputLength: 0, maximumInputLength: undefined, placeholder: ‘Select…’ } or override each one by passing in config.allowClear = false, etc.
    • options = function that calls $http get on ‘api/Lookup/’ + config.apiSearchMethod
    • textLookup = function that calls $http get on ‘api/Lookup/’ + config.apiGetMethod
  • getConfigAjaxMultiple is the same as getConfigAjaxSingle except that mode = ‘tags-id’

CONCLUSION

Angular Agility is a powerful tool in the AngularJS developers tool belt.  The 6 new field group strategies introduced in this article make it even more powerful.  In my next article, I plan to show how we can take advantage of specifying the model validation in javascript by showing how to get that validation from WEB API and System.ComponentModel.DataAnnotations attributes specified in classes.