Yesterday, I was up until 11PM (which is late for me) trying to track down an AngularJS error that suddenly popped up and was only affecting Firefox users. The error seemed nonsensical and manifested as a problem in the $digest loop of the AngularJS framework. Firefox kept complaining that it was missing arguments when trying to evaluate one of the $watch() expressions.
The actual error message being reported was:
TypeError: Missing Argument 1 when calling function b.get()
It was being thrown in minified code, hence the "b.get()". After reproducing the problem in non-minified code, however, I was able to track the error down to the following expression in the AngularJS $digest loop:
value = watch.get(current)
After I put some break-points in the code and start logging out watch expressions, I came across one expression that was identifying itself as [native code]. This was extremely peculiar. After much hand-wringing and teeth-gnashing, I finally came across this page on the Mozilla Developer Network (MDN) that discussed a proprietary method in Firefox:
At that point, I had a Eureka moment! I was able to following the stack-traces to a custom AngularJS directive that was conditionally passing "attributes.watch" to a scope.$watch() binding. When the "attributes.watch" was defined, the code worked, no problem. But, when it was not defined on the given HTML element, the code broke. The problem here was that if the "attributes.watch" wasn't defined by the code, Firefox was walking up the prototype chain (of the attributes object) and finding the "watch" property - Object.prototype.watch. This prototype method was then being passed into the scope.$watch() binding, which was throwing a fit when it was invoked with the wrong number of arguments.
Once I understood the problem, it was easy to reproduce (in all versions of AngularJS):
Notice that the bnList directive takes an optional "watch" attribute. On the directive instance that defines the "watch" attribute, all is good. But, on the instance that doesn't define it, we end up binding to the Firefox-native watch() method. And, when we run this code, we get the following console output:
To fix this problem, you could add a check for .hasOwnProperty() in order to ensure that Firefox won't walk up the prototype chain to look for the reference. However, it's probably best just to avoid using attributes called "watch". Or, if you do use them, make sure that they are not optional. After all, this only breaks when you have to check to see if you should be binding to "watch".
Want to use code from this post? Check out the license.