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 | angular.module('utils').filter('isDefined', function () { |
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.