95

I am trying to fire click event (or any other event) on element programatically , In other word I want to know the similar features as offered by jQuery .trigger() method in angular2.

Is there any built in method to do this? ..... if not please suggest how can i do this

Consider the following code fragment

<form [ngFormModel]="imgUploadFrm"
          (ngSubmit)="onSubmit(imgUploadFrm)">
        <br>
        <div class="input-field">
            <input type="file" id="imgFile" (click)="onChange($event)" >
        </div>
        <button id="btnAdd" type="submit" (click)="showImageBrowseDlg()" )>Add Picture</button>
 </form>

Here when user click the btnAdd it should fire the click event on imgFile

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
Jorin
  • 1,316
  • 1
  • 12
  • 16
  • You only need `imgFile.click()` instead of `showImageBrowseDlg()` if you follow the below answer by @akshay-khale https://stackoverflow.com/a/41675017/344029 (after adding the variable ` – DJDaveMark Apr 06 '18 at 09:11

6 Answers6

200

Angular4

Instead of

    this.renderer.invokeElementMethod(
        this.fileInput.nativeElement, 'dispatchEvent', [event]);

use

    this.fileInput.nativeElement.dispatchEvent(event);

because invokeElementMethod won't be part of the renderer anymore.

Angular2

Use ViewChild with a template variable to get a reference to the file input, then use the Renderer to invoke dispatchEvent to fire the event:

import { Component, Renderer, ElementRef, ViewChild } from '@angular/core';
@Component({
  ...
  template: `
...
<input #fileInput type="file" id="imgFile" (click)="onChange($event)" >
...`
})
class MyComponent {
  @ViewChild('fileInput') fileInput:ElementRef;

  constructor(private renderer:Renderer) {}

  showImageBrowseDlg() {
    // from http://stackoverflow.com/a/32010791/217408
    let event = new MouseEvent('click', {bubbles: true});
    this.renderer.invokeElementMethod(
        this.fileInput.nativeElement, 'dispatchEvent', [event]);
  }
}

Update

Since direct DOM access isn't discouraged anymore by the Angular team this simpler code can be used as well

this.fileInput.nativeElement.click()

See also https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent

Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • You don't need to declare MouseEvent: this.renderer.invokeElementMethod(this.postcardtoggle.nativeElement, 'click', []); – Ben Oct 05 '16 at 16:47
  • 10
    `this.fileInput.nativeElement.click()` also works it seems. – lbrahim Dec 22 '16 at 08:59
  • 3
    @lbrahim that's correct. At that time it was a strong suggestion to avoid accessing properties or methods of `nativeElement` and use `Renderer` instead. I think this changed quite a bit recently and direct DOM access is fine now but it won't work with server side rendering and WebWorkers. – Günter Zöchbauer Dec 22 '16 at 09:01
  • How can I do this but if the #fileInput is inside a *ngFor? – Aral Roca Feb 01 '17 at 12:52
  • 1
    @AralRoca then use `@ViewChildren('fileInput') QueryList;` See also http://stackoverflow.com/questions/32693061/angular-2-typescript-get-hold-of-an-element-in-the-template/35209681#35209681 – Günter Zöchbauer Feb 01 '17 at 13:38
  • @GünterZöchbauer, will Angular2 propagate the event fired by `nativeElement.click()`? Simply, will it be "seen" by the other Angular2 components? – Alex Klaus Mar 03 '17 at 06:53
  • `nativeElement.click()` is a simple DOM event, not related to Angular in any way. `(click)="..."` will listen to such an event. – Günter Zöchbauer Mar 03 '17 at 06:55
  • @GünterZöchbauer can you provide any source for your update statement "DOM access isn't discouraged anymore"? Why this change? I'm just curious :). – Nightking May 12 '17 at 09:21
  • 1
    I'm not entirely sure about that and I haven't seen official docs that mention it, but as far as I remember it was mentioned in some GitHub issues. I also think to remember that some doc examples were updated to remove renderer and use direct DOM access. I got the impression if you don't intend to use server-side-rendering or web-workers, then it's just a cumbersome limitation and quite a lot developers don't care about that and that this is way they removed of making that a strong rule. – Günter Zöchbauer May 12 '17 at 09:25
  • For angular 4, use: `@ViewChild('fileInput', {read:ElementRef}) fileInput:ElementRef;` then, call the `this.fileInput.nativeElement.dispatchEvent(event);` – Leo Caseiro Jul 11 '17 at 00:53
  • Not working for me from ngAfterViewInit https://plnkr.co/edit/p4jsdPR9varhV9ihUhO5?p=preview – Wilhelmina Lohan Sep 08 '17 at 22:22
  • @WilliamLohan thanks for the Plunker. I'm pretty sure this is caused by a Chrome security feature. The code is called, but AFAIK Chrome limits this action to be called only from within an event handler of a user action (click, keypress, ...) – Günter Zöchbauer Sep 10 '17 at 14:59
  • Renderer is now deprecated in angular5. Do you know how to use this with Renderer2 ? – Kevin Vincent Dec 28 '17 at 13:35
  • @KevinVincent why not just `this.fileInput.nativeElement.dispatchEvent(event);`? – Günter Zöchbauer Dec 28 '17 at 14:12
  • Why not then use just Jquery if calling methods on native element is preferred now?? – Manu Chadha Oct 08 '19 at 05:38
  • 1
    @ManuChadha why using jquery at all? It might lead to issues when Angular and jquery think they both are in full conttol of the FOM. jquery has become much less relevant recently because the differences between browsers are much less and support for old browsers were dropped completely by most. – Günter Zöchbauer Oct 08 '19 at 14:14
  • One more question - the idea behind not using nativeElement was to keep the application portable to non-browser platforms. So how does one go about doing that if using nativeElement is ok now? – Manu Chadha Oct 09 '19 at 06:37
  • I think that server-side rendering, webworker, and other platforms like Ionic are just not relevant for a majority of Angular projects and therefore the Angular team stopped pushing this approach and leaves it to the project teams to decide. So if this platform independency is important to you do not use nativeElement directly. – Günter Zöchbauer Oct 09 '19 at 08:36
27

I also wanted similar functionality where I have a File Input Control with display:none and a Button control where I wanted to trigger click event of File Input Control when I click on the button, below is the code to do so

<input type="button" (click)="fileInput.click()" class="btn btn-primary" value="Add From File">
<input type="file" style="display:none;" #fileInput/>

as simple as that and it's working flawlessly...

Akshay Khale
  • 8,151
  • 8
  • 50
  • 58
4

This worked for me:

<button #loginButton ...

and inside the controller:

@ViewChild('loginButton') loginButton;
...
this.loginButton.getNativeElement().click();
Marco C.
  • 1,282
  • 2
  • 15
  • 19
4

To get the native reference to something like an ion-input, ry using this

@ViewChild('fileInput', { read: ElementRef }) fileInput: ElementRef;

and then

this.fileInput.nativeElement.querySelector('input').click()
davejoem
  • 4,902
  • 4
  • 22
  • 31
2

Günter Zöchbauer's answer is the right one. Just consider adding the following line:

showImageBrowseDlg() {
    // from http://stackoverflow.com/a/32010791/217408
    let event = new MouseEvent('click', {bubbles: true});
    event.stopPropagation();
    this.renderer.invokeElementMethod(
        this.fileInput.nativeElement, 'dispatchEvent', [event]);
  }

In my case I would get a "caught RangeError: Maximum call stack size exceeded" error if not. (I have a div card firing on click and the input file inside)

isherwood
  • 58,414
  • 16
  • 114
  • 157
reneval
  • 59
  • 4
2

If you want to imitate click on the DOM element like this:

<a (click)="showLogin($event)">login</a>

and have something like this on the page:

<li ngbDropdown>
    <a ngbDropdownToggle id="login-menu">
        ...
    </a>
 </li>

your function in component.ts should be like this:

showLogin(event) {
   event.stopPropagation();
   document.getElementById('login-menu').click();
}
Rammgarot
  • 1,467
  • 14
  • 9