0

I would like to create a directive that asks for confirmation when a user clicks on a button. To do that, I should be able to store the original event, and call it only when the user has confirmed his choice.

I have mocked a similar behavior here :

https://stackblitz.com/edit/angular-6wnvjk?file=src%2Fapp%2Fapp.component.html

(This is with a Timeout and not a confirmation, but the issue is the same)

How would one proceed to store the original event, then call it once the timeout/confirmation ends ?

import { Directive, ElementRef, HostListener } from '@angular/core';

@Directive({
  selector: '[confirm]'
})
export class ConfirmDirective {

  constructor() { }

  @HostListener('click', ['$event'])
  onClick(event: MouseEvent) {
    console.log('handler from directive');
    // 1 - Would like to store original event
    const originalEvent = () => {};
    // Would like to call it later
    setTimeout(() => {
      originalEvent();
    })
  }

}
  • You want something like this ? https://stackblitz.com/edit/angular-confirmation-dialog – Janak Prajapati Apr 19 '19 at 11:01
  • @JanakPrajapati in the idea yes, but I don't want it to be a component, just a directive. –  Apr 19 '19 at 11:03
  • @JanakPrajapati and also, I don't want to have a button that says "open dialog", but a button that says "confirm" and does X, and when you click on it, it opens a dialog to confirm : if you confirm, it does X, if not, it does nothing. –  Apr 19 '19 at 11:04
  • Might be [this](https://stackoverflow.com/questions/34759384/custom-directive-button-with-confirm-popup) will help – Prashant Pimpale Apr 19 '19 at 11:08
  • @PrashantPimpale I am using Angular, not AngularJS, concepts aren't the same. –  Apr 19 '19 at 11:15
  • have you tried to create a stream of events using subject – Fateh Mohamed Apr 19 '19 at 11:20
  • @FatehMohamed that's too broad, could you specify ? –  Apr 19 '19 at 11:45

1 Answers1

2

You can build your directive to intercept the clicks and emit them only if the condition is passed:

@Directive({
    selector: '[confirm]',
})
export class ConfirmDirective implements OnInit, OnDestroy {
    @Output() confirmed = new EventEmitter();
    private clicks = new Subject();
    private subscription: Subscription;

    constructor() {}

    ngOnInit() {
        this.subscription = this.clicks
            .pipe(
                switchMap((event) =>
                    confirm('Do you agree?') ? of(event) : null
                )
            )
            .subscribe((e) => this.confirmed.emit(e));
    }

    ngOnDestroy() {
        if (this.subscription) this.subscription.unsubscribe();
    }

    @HostListener('click', ['$event'])
    clickEvent(event) {
        event.preventDefault();
        event.stopPropagation();
        this.clicks.next(event);
    }
}

Usage:

  <button confirm (confirmed)="doSomething()">Click</button>

Good luck!

Andrew Radulescu
  • 1,862
  • 13
  • 21
  • Indeed ! Can't believe it was that stupid. I wanted to bind on the `click` event and I was having a hard time, but I didn't think once about changing the event name ... Thank you ! (stackblitz : https://stackblitz.com/edit/angular-igiuvh?file=src%2Fapp%2Fconfirm.directive.ts) –  Apr 19 '19 at 12:01
  • When I click cancel I get an error "ERROR TypeError: You provided 'null' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable". How did you resolve this one if u got one? If I use of(null) it passes the test and event is triggered later – Mitul Panchal Nov 18 '22 at 09:13