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.
Oh, yeah, a very similar one of those bit me a week or so ago. I really can't believe they want to introduce an Object.prototype method called "watch" into JS. It's just going to cause issues, since it's such a common word or variable.
Yeah, it does seem like a common names, especially with all the reactive frameworks that revolve around "watching" values.
Hello Ben Nadel,
can you tell me which angular versions are affected by this bug? and if there it is resolved now in a new release?
I have tried angular 1.4.0 and 1.4.5, but the bug was persistent :-(