A set of useful Angular.js extensions implementing common UX patterns to improve productivity from @JohnCulviner
JavaScript HTML CSS
Latest commit e05e7ea Nov 1, 2016 @johnculviner johnculviner v0.8.37



Build Status

A set of useful Angular extensions to improve productivity

Live demo of this exact source here

API Docs

Note all JavaScript/CSS that you might want to use is located in /src/.


bower install angular-agility -S


AngularJS is the only dependency.

For example for FormExtensions add the following dependencies to your Angular module definition. (aaNotify is baked in and allows for notifications. Disableable, see provider.js)

angular.module('myModule', ['aa.formExtensions', 'aa.notify']);

Form Extensions

Blog Posts | Source | API Docs | Live Demo

Angular.js form validation is likely one of the best takes on form validation out there. Unfortunately it can often be a little TOO flexible for many applications where you want basic error message display and generation without having to dive into the form object model and manually construct basic messages for each field.

This is where Form Extensions comes in. It works with built in Angular.js validation and TBS3 (by default) to:

  • Drastically reduce the amount of boilerplate, repetitive, error-prone HTML required to produce forms, labels and validation messages. Comes with support for Twitter Bootstrap 3 baked in but free to do whatever you want (see last bullet).
  • Automatically generate Angular.js fields for use in form validation, their error messages AND labels.
  • On blur and on invalid submit attempt showing of validation messages.
  • Form extensions programatically extends forms at myForm.$aaFormExtensions = {...}
  • Code is cleaner and easier to read. Form Extensions is DSL that distills your HTML down to only what is required.
  • Feel free to use full blown markup whenever you want complete control.
  • Mix and match the directive components of Form Extensions to get exactly what you'd like for each situation.
  • It does exactly what you want: EVERYTHING is overridable on a global and per-instance basis through a rich provider model. AKA if you don't like how it looks right now you can change it. Heck you don't even need to use Twitter Bootstrap even.
myApp.config(function(aaFormExtensionsProvider) {
    aaFormExtensionsProvider.onNavigateAwayStrategies.myCustomStrategy = function(rootFormScope, rootForm, $injector){/*...*/};
    aaFormExtensionsProvider.defaultOnNavigateAwayStrategy = 'myCustomStrategy';
    //etc, look at provider.js to see what is available

Note all of the strategies include a $injector as the last argument so that you can bring in your own Angular dependencies (or perhaps even do a $compile if you wanted inside)


In a nutshell

With Form Extensions:

<div ng-form="exampleForm" class="form-horizontal">
    <input type="number" aa-field-group="person.age" min="0" max="140" required>

Without Form Extensions:

<div ng-form="exampleForm" class="form-horizontal">
    <div class="form-group">
        <label for="age" class="col-sm-2 control-label">
            Age *
        <div class="col-sm-3">
            <input type="number" class="form-control" ng-model="person.age" 
            name="age" id="age" min="0" max="140" required />

            <div class="validation-error" ng-show="(exampleForm.age.$dirty || invalidSubmitAttempt) && exampleForm.age.$error.number">
                Age must be a number
            <div class="validation-error" ng-show="(exampleForm.age.$dirty || invalidSubmitAttempt) && exampleForm.age.$error.min">
                Age must be at least 0.
            <div class="validation-error" ng-show="(exampleForm.age.$dirty || invalidSubmitAttempt) && exampleForm.age.$error.max">
                Age must be at most 140.
            <div class="validation-error" ng-show="(exampleForm.age.$dirty || invalidSubmitAttempt) && exampleForm.age.$error.required">
                Age is required.

Or configure it from your code:

var app = angular.module('app', ['aa.formExternalConfiguration', 'aa.notify'])
    .controller('main', ['$scope', function(scope) {
       scope.user = {
       scope.formconfig = {
           validations: {
                   name: {
<div ng-controller="main">
    <div aa-configured-form validation-config="formconfig" ng-form="exampleForm">
        <input type="text" ng-model="user.name" />

Advanced Form Extensions

Blog Posts | Source | API Docs | Live Demo

Form changed tracking

Form changed tracking means deviation from the initial state of the form† indicates that a form changed. You can change a field and set it back to it's initial state and aaFormExtensions considers the field AND the form no longer changed (all values are equal to their init state†).

Angular considers the field/form dirty still since it was touched (yet changed back to initial state).

Form reset

Sets the state of the form back to it's original state†:

personForm.$aaFormExtensions.$reset(/*optional shouldNotConfirmReset*/);

†AFTER AJAX. All native Angular AJAX requests are counted by aaLoadingWatcher and a form isn't considered loaded until pending AJAX requests have completed. If you have other types of loading to account for simply use aaLoadingWatcher.increment()/.decrement() API to count them.

Reset initial form state

The current state of the form will be considered it's initial state (any changes from here are now $changed):


Loading indicators

isLoading boolean is available from aaLoadingWatcher.isLoading factory or: $rootScope.aaIsLoading = false

On-navigate away handling

Includes (by default, overridable) detection of Angular UI Router $stateChangeStart. If the root form in the view is myForm.$aaFormExtensions.$changed it will block with a JavaScript confirm. Please customize this with however you are doing modals. I would recommend Angular UI Modal. Then register your own custom strategy:

myApp.config(function(aaFormExtensionsProvider) {
    aaFormExtensionsProvider.onNavigateAwayStrategies.myCustomStrategy = function(rootFormScope, rootForm, $injector){/*...*/};
    aaFormExtensionsProvider.defaultOnNavigateAwayStrategy = 'myCustomStrategy';
    //etc, look at provider.js to see what is available

Use to ignore on a per form basis (if you registered a global default):

<div ng-form="myForm" on-navigate-away-strategy="none">...</div>


Fully customizable/configurable growl, toastr style notifications done right

Blog Posts | Source | API Docs | Live Demo

1. Put a notification directive somewhere in the DOM with the markup for how you want it positioned

<!-- probably use a CSS class IRL but you get the point-->
<div aa-notify style="position: fixed; bottom: 25px; right: 25px;"></div>


2. Call it from your code!

.controller('myController', function($scope, aaNotify, $timeout) {

    $scope.trySuccess = function() {
        //the default notifyConfig sets the life to 4 seconds
        //you can tweak this globally or add your own default notifyConfig
        aaNotify.success('Example success message!');

    $scope.tryInfo = function() {
        //configure to 1 second life here only
        aaNotify.info('Example info message!', { ttl: 1000 });

    $scope.tryWarning = function() {
        var handle = aaNotify.warning('Example warning message!', { ttl: 999999999 });

        //whoops that message is going to be around a little *too* long with that ttl.
        //use the handle to remove it after only 4 seconds
        $timeout(function() { aaNotify.remove(handle); }, 4000);


    $scope.tryDanger = function() {
        aaNotify.danger('Example danger message!<br/><br/><br/>This text after some <br/>s',
                //You can register many different templates and
                //*do whatever you want* for a notification, wherever/however you want!
                //(you aren't bound by this dev's idea of what a notification should look like)
                //if you like the sound of this keep scrolling to Advanced Mode below...

                //the default template has these options
                //*if you don't like them *easily* make your own template!*
                showClose: true,                            //close button
                iconClass: 'fa fa-exclamation-triangle',    //iconClass for a <i></i> style icon to use
                allowHtml: true,                            //allows HTML in the message to render as HTML

                //common to the framework as a whole
                ttl: 0  //time to live in ms

It does much more than this, advanced demos coming soon!

Select 2

Blog Posts | Source | API Docs | Live Demo

Headache free use of Select2 with Angular for most common tasks

First off, there are a few other Select2 directives out there. This one is different in that it is specifically designed to easily work with binding ids, objects, arrays, AJAX queries etc. for some pretty common use cases (see examples!)

It abstracts away the complicated Select2 API (lots of examples below!) and should make it pretty easy to do a lot of things without writing a bunch of hacks. You can still always call the select2 API directly if you want. See examples below!

It offers no support for the basic 'replace a list and ' functionality: This is desgined for working with JavaScript object data (as you generally do with Angular). Not what you want? Take a look at the AngularUI one. Interactive demos here: Demos