Avoiding silent Angular failures

Prevent silent module overrides, misspelled directives, undefined expressions.

We like AngularJs, but it has one terrible flaw that causes a lot of waisted time during development - it fails silently in several cases. The silent errors are hidden from the developer, leading to long debug cycles. After spending several hours a couple of times trying to locate the problem, we have written the following 3 tools to help prevent silent AngularJs errors.

Silent module overrides

If several modules use the same name, the last one usually wins. If several directives use the same name, the winning one depends on the surrounding controller! To prevent silent overrides, just include the stop-angular-overrides script before loading your code. Any name clashes in modules, controllers, filters will generate an exception

<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/stop-angular-overrides/stop-angular-overrides.js"></script>
<script src="A1.js"></script> // angular.module('A', []) ...
<script src="A2.js"></script> // angular.module('A', []) ... !!!
// results in
Uncaught Error: Angular module A already exists stop-module-override.js:17
  angular.module stop-module-override.js:17
  A2 A2.js:3
  (anonymous function)

Silent missing directives

If you misspell a directive's name, or forget to include module dependency, a custom HTML tag will just sit there. Include find-missing-directives script on your page and run it after page loads. It will find all empty non-standard elements, helping you locate any missing element directives.

<body>
    <p>
        Below is angular directive without actual module
        <my-tag></my-tag>
    </p>
// your angular stuff
<script src="find-missing-directives.js"></script>
<script>
    var empty = findMissingDirectives();
    if (empty.length) {
        throw new Error('Found missing directives ' + empty.join(','));
    }
    // throws error 'Found missing directives my-tag'
</script>
</body

Silent undefined expressions

Angular is very forgiving when evaluating expressions - if there is an error, the result is just an undefined value. This is especially deadly when you modify scope properties in JavaScript, but forget to update bound expressions inside html templates.

The last utility is too small to place it in a separate repo. It is a filter that throws an exception if an undefined value is passed through it.

1
2
3
4
5
6
7
8
angular.module('utils').filter('isDefined', function () {
return function (value, msg) {
if (value === undefined) {
throw new Error('isDefined filter got undefined value ' + msg);
}
return value;
};
});

You can use it after any expression, it just passes any other value through.

1
<div ng-if="foo.bar === 'baz' | isDefined:'foo.bar value' "></div>

If for some reason $scope.foo is undefined, this will generate an error.

Conclusions

Taken together these 3 utilities prevented a lot of problems, speeding up our development cycles.