DEV Community

Cover image for How to Programmatically Trigger 'Click' Event in Angular 4 & 5
Monique Dingding
Monique Dingding

Posted on

How to Programmatically Trigger 'Click' Event in Angular 4 & 5

Resources in Angular 4 (and 5) are scarce. Really. Sometimes I get so lost in sifting through Stack Overflow that I end up translating the answers that I have found in older versions (1.x and 2.x) using the latest Angular docs.

One of the tasks in the project that I am working on involves uploading a file using either a drag-and-drop box or by clicking browse, like so:

File Upload Page

Essentially, I want to trigger the file input button hidden in the page to open the file browser when browse (which is an anchor tag) is clicked.

For newbs like me, I initially resorted to search for packages in NPM, only to know that some are barely maintained, not tested for compatibility with newer versions of Angular, or have too many features that I do not need.

Realizing my mistake, I decided to look for a simpler answer but many of them still require me to import modules that bloat the project.

Before starting, here are my stats:



Angular CLI: 1.7.0-beta.3
Node: 8.9.4
OS: linux x64
Angular: 4.4.6
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router, tsc-wrapped

@angular/cli: 1.7.0-beta.3
@angular-devkit/build-optimizer: 0.2.0
@angular-devkit/core: 0.2.0
@angular-devkit/schematics: 0.2.0
@ngtools/json-schema: 1.1.0
@ngtools/webpack: 1.10.0-beta.3
@schematics/angular: 0.2.0
@schematics/package-update: 0.2.0
typescript: 2.3.4
webpack: 3.10.0


Enter fullscreen mode Exit fullscreen mode

I particularly disliked angular.element as it is just a wrapper that still utilizes jQuery and as much as possible I like to keep my code free from imports that I can't reuse. Ultimately, I came across a more efficient solution by using HTMLElement, an interface from Web API.

So this is how I did it, to give you (and future me) a guide:

The Template

The page view is named uploadFile.component.html

Upload Image

Important stuff:

  • The class .hide-style contains {display: none} to hide the file input.
  • The id #tenantPhotoId is an identifier used to reference the file input.
  • The variable tenantIDFileName binds the file name of the file to be displayed on the page.

The Component

The TS file is named uploadFile.component.ts

Upload Image

Important stuff:

  • The method onFileChange($event) displays the file name of the file being uploaded by assigning it on the variable tenantIDFileName.
  • The method openFileBrowser($event) triggers the click event on the file browser.

Walkthrough of the Process

When browse is clicked, openFileBrowser($event) is called with $event as the argument. This is so we can use preventDefault() to prevent the normal behavior of an anchor tag of redirecting to the page it is pointed to.

Then, the value is fetched using the ID of the file input using document.getElementById() and is casted to an HTMLElement type.

HTMLElement represents any element in the document and implements Event Handler interface, so now we can call click(). Successfully, the file browser is triggered to open.

Whenever a file is selected, onFileChange($event) will be called and the tenantIDFileName variable will be assigned as the value of the filename and is displayed on the page.

Note that in the actual file upload process, you can consume an API endpoint that will store the data on your server or call a third-party service like AWS S3.

EDIT: If you've found a better solution then we can help each other out by sharing as many references as we can. :-)

There you go! Happy hacking!

Top comments (23)

Collapse
 
reegodev profile image
Matteo Rigon • Edited

Two things I'd like to point out:

  1. You should always use a button instead of a link if you just need to trigger an action
  2. Instead of the openFileBrowser method which is a more "jQuerish" approach you can do that in the angular way with template ref:
<input id="tenantPhotoID" type="file" #photoInput >
<button type="button" (click)="photoInput.click()"  > 

Enter fullscreen mode Exit fullscreen mode
Collapse
 
gloriamaris profile image
Monique Dingding

Hi, Matteo! I am not - by any means - an expert in Angular but I just want to help other developers who may be lost right now looking for solutions by sharing mine. :-) Thanks for these pointers!

Collapse
 
jamesbcn profile image
jamesbcn

This was exactly what I was looking for! Much appreciated.

Thread Thread
 
oif01 profile image
Óscar

Same over here... :)

Collapse
 
viktorkukurba profile image
Viktor

In this case you've missed $event.stopPropagation(), which could be important in some cases.
So, method still can make sense in this case if you'd like to keep your html clean.

Collapse
 
hoyvicente profile image
nate nate

Mine resulted to 'ERROR TypeError: Cannot read property 'click' of undefined'. Did I missed out something?

Collapse
 
reegodev profile image
Matteo Rigon

Hello, sadly i switched to Vue a long time ago so i may be a bit behind current Angular, but if you get that error it appears that you forgot to add to the input the reference attribute #photoInput.

Just tested it here and the code appears to be working as originally wrote:
stackblitz.com/edit/angular-playgr... ( checkout hello-framework/hello-framework.component.html )

Collapse
 
pellanokeith07 profile image
ERROR 401

Hi matteo may I ask if this will work when you use #templateRef using material UI in angular?

Collapse
 
manuchadha1979 profile image
manuchadha1979

Hi Monique, how do you unit-test the example? I have created a similar application but I am unable to unit-test it as I don't know how to trigger the change event from file selector. I have created a question in StackOverflow. Would it be possible for you to take a look at stackoverflow.com/questions/542022...

Collapse
 
leomayer profile image
leomayer

I like this sample more or less cuz the element-id could be used in a dynamical way. i.e. if I don't know the id at the codeing time but only on runtime I can still use the given approach. Which I like :-)

Collapse
 
itamarlev profile image
Itamar Lev

Instead of getElementById there's a good way of controlling the elements on the template by using the @ViewChild Directive. (Angular 8)

in the template adding a local reference to the button: (#photoInputButton)

in the component
adding the @ViewChild directive referencing the photoInputButton:

@ViewChild('photoInputButton', {static: false}) photoInputButton: ElementRef;
//{static: true} => only if it's being used in the ngOnInit phase

Now you have an ElementRef - you can trigger the click:

this.photoInputButton.nativeElement.dispatchEvent(new MouseEvent('click'));

Collapse
 
vaibhav_arora__ profile image
Vaibhav Arora

Need 1 help to understand guys.

When Angular suggests(I guess so) to use Renderer2 and not directly access your HTML DOM, doesn't that future proof law or whatever we call that, applies to this code.

Is there any other way if we had to do it reactively and not template wise, at least add some custom code in between like "event.preventDefault()"

Collapse
 
gene profile image
Gene

It's nice to see fellow Filipinos active in this community!

Collapse
 
hoyvicente profile image
nate nate

Hi!

I don't know if this is still active but I have some questions.

I've tried your approach above and also the one Matteo Rigon provided. Both of them resulted to 'ERROR TypeError: Cannot read property 'click' of undefined'. Did I missed out something before applying these approaches to trigger click event? I'm using Angular 7 by the way.

Thanks

Collapse
 
igghera profile image
Andrea Gherardi

I found a solution that is more elegant, standard, degrades gracefully and is framework-agnostic. Check it out here: tympanus.net/codrops/2015/09/15/st...

Collapse
 
gloriamaris profile image
Monique Dingding

This is great! Thanks :-)

Collapse
 
lflores profile image
Leonardo Flores

Thank you Monique, it was very useful for me!

Collapse
 
anushahmad profile image
AnushAhmad

great post it really helped me, thanku