There are several utility functions exposed on the angular global, like forEach, copy, and bind, but there were some functionalities we found we needed throughout our app.

There are several builds of our app, and in our minimal build we didn’t want to include any libraries or frameworks beyond angular (i.e. Lodash, but some of the functionality we wanted was a bit tweaked).

Functions below are

  • isEmpty (line 16)
  • isScope (line 47)
  • once (line 57)
  • remove (line 84)
  • map (line 110)
  • $safeApply (line 136)
  1 //Add a few utility functions to the angular global
  2 angular.module('myApp.ngAdditions', [])
  3   .config(function() {
  4 
  5     //angular function extensions
  6     var ext = {};
  7 
  8     /**
  9      * @name angular.isEmpty
 10      * @description Determines whether object is empty.
 11      * Booleans return false.
 12      * {}, [], undefined, null, length == 0, NaN will return true
 13      * @param {*} value
 14      * @returns {Boolean}
 15      */
 16     ext.isEmpty = function isEmpty(value) {
 17       if (angular.isNumber(value)) {
 18         return value !== value;
 19       }
 20       else if (value === true || value === false) {
 21         return false;
 22       }
 23       else if (angular.isObject(value)) {
 24         if (value.length === 0) {
 25           return true;
 26         } else {
 27           for (var key in value) {
 28             if (value.hasOwnProperty(key)) {
 29               return false;
 30             }
 31           }
 32           return true;
 33         }
 34       }
 35       else if (angular.isUndefined(value) || value === null || value.length == 0) {
 36         return true;
 37       }
 38       return false;
 39     };
 40 
 41     /**
 42      * @name angular.isScope
 43      * @description Determines whether an object is an angular $scope
 44      * @param {*} obj
 45      * @returns {Boolean}
 46      */
 47     ext.isScope = function isScope(obj) {
 48       return !!obj && angular.isFunction(obj.$evalAsync) && angular.isFunction(obj.$watch);
 49     };
 50 
 51     /**
 52      * @name angular.once
 53      * @description Creates a function that is restricted to execute `func` once. Repeat calls to the function will return the value of the first call. The `func` is executed with the `this` binding of the created function.
 54      * @param {Function} func The function to restrict.
 55      * @returns {Function} Returns the new restricted function.
 56      */
 57     ext.once = function once(func) {
 58       var ran,
 59         result;
 60 
 61       if (!angular.isFunction(func)) {
 62         throw new TypeError;
 63       }
 64       return function() {
 65         if (ran) {
 66           return result;
 67         }
 68         ran = true;
 69         result = func.apply(this, arguments);
 70 
 71         func = null;
 72         return result;
 73       };
 74     };
 75 
 76     /**
 77      * Removes all elements from an array that the callback returns truey for and returns an array of removed elements.
 78      * The callback is invoked with three arguments; (value, index, array).
 79      * @param {Array} array The array to modify
 80      * @param {Function} callback The function called per iteration
 81      * @param {*} context `this`
 82      * @returns {Array} Returns a new array of removed elements.
 83      */
 84     ext.remove = function remove(array, callback, context) {
 85       var index = -1,
 86         length = array ? array.length : 0,
 87         result = [];
 88 
 89       context = context || null;
 90 
 91       while (++index < length) {
 92         var value = array[index];
 93         if (angular.isUndefined(value) || callback.call(context, value, index, array)) {
 94           angular.isDefined(value) && result.push(value);
 95           array.splice(index--, 1);
 96           length--;
 97         }
 98       }
 99       return result;
100     };
101 
102     /**
103      * @name angular.map
104      * @description Apply a function to each element of an array or values of an object, returning a new Array / Object.
105      * @param {Array|Object} obj Object to iterate over
106      * @param {Function} iterator Function to call each time -- function (value, key|index, obj) {}
107      * @param {Object} context `this` context
108      * @returns {Array|Object} New array with results of each function call, or Object with same keys and mapped values.
109      */
110     ext.map = function map(obj, iterator, context) {
111       var results = angular.isArray(obj) ? [] : {};
112       angular.forEach(obj, function(value, index, list) {
113         if (angular.isFunction(iterator)) {
114           angular.isArray(results) ?
115             results.push(iterator.call(context, value, index, list)) :
116             results[index] = iterator.call(context, value, index, list)
117         }
118       });
119       return results;
120     };
121 
122     angular.extend(angular, ext);
123   })
124   .run(function($rootScope) {
125     /**
126      @name $rootScope.$safeApply
127      @adaptedFrom: https://github.com/yearofmoo/AngularJS-Scope.SafeApply
128      @description Particularly for 3rd party apps, when need to force digest or apply safely. Alternative is $timeout, but can cause flicker.
129 
130      @usage
131      $scope.$safeApply();
132      $rootScope.$safeApply($scope);
133      $scope.$safeApply(<function>);
134      $rootScope.$safeApply($scope, <function>, <force>);
135      */
136     $rootScope.$safeApply = function() {
137       var $scope, fn, force = false;
138       if(arguments.length == 1) {
139         var arg = arguments[0];
140         if(typeof arg == 'function') {
141           fn = arg;
142         }
143         else {
144           $scope = arg;
145         }
146       }
147       else {
148         $scope = arguments[0];
149         fn = arguments[1];
150         if(arguments.length == 3) {
151           force = !!arguments[2];
152         }
153       }
154       $scope = $scope || this;
155       fn = fn || function() { };
156       if(force || !$scope.$$phase) {
157         $scope.$apply ? $scope.$apply(fn) : $scope.apply(fn);
158       }
159       else {
160         fn();
161       }
162     };
163   });