Angular 1 infinite ng-click

Never thought I would be back here talking about angular. But here we are nonetheless. For the last year I have been in charge of a project that has been the literal definition of a disaster. Besides the long story about why, I just wanted to take a moment to talk about a particular issue that randomly started affecting uploading images and was really hard to find. I wanted to reveal why this occurs just in case anyone is still trapped in angular 1 land and has ran across this hard to search for issue. So let's get started.
Given the following HTML
<body>
<div ng-click="doUpload()">
<input id="fileupload" type="file" hidden multiple />
<em class="fa fa-file-image-o fa-2x"></em>
</div>
</body>
And given this javascript, and assuming this code is in a controller of some kind:
const input = document.querySelector('input#fileupload');
input.onchange(()=>{
//save the data
});
$scope.doUpload = () => {
$timeout(() =>{
input.click();
});
}
What's wrong with this?
First lets walk through what will happen. When clicking the div
that includes the ng-click
the doUpload
function will fire. This will then trigger a click action on the input field that is currently hidden.
Seems straightforward enough...
You might assume that when the div
is clicked, there is a chance that the ng-click
is triggered and maybe a chance of the input
's click
event to also trigger. Resulting in 2 events occuring instead of one.
That, while a valid assumption, would be false.
Instead you get an infinite loop of ng-click
triggers. Originally I thought that maybe the controller was being loaded on every digest. But that wasn't the case. Googling ng-click infinite trigger
doesn't bring up any results for this issue. You do run across various accounts of people having issues with ng-click
firing twice. I only found the answer in a comment to a solution of one of those 2 triggering solutions which was a lucky find.
The easiest way to fix this issue is to move the input field outside of the clickable div.
<body>
<input id="fileupload" type="file" hidden multiple />
<div ng-click="doUpload()">
<em class="fa fa-file-image-o fa-2x"></em>
</div>
</body>
It doesn't need to be nested inside - since it is hidden - and could technically be placed anywhere on the page.
I hope this helps others that may have a disaster project that needs maintaining like I do. Above all, hopefully you don't have this issue in such project at all. Although if you are here, I am sorry for your loss.