Royi Namir
Royi Namir

Reputation: 148574

Angular - How can I remove all other existing directives?

I'm using a tooltip directive , so when I click a <button> - I dynamically inject and display a tooltip.

It does work well and I do see the tooltips :

enter image description here

This is the code I've used to inject a tooltip when clicking a button :

@Directive({ selector: '[popover]'})
class Popover {
  private _component: ComponentRef<>;
    constructor(private _vcRef: ViewContainerRef, private _cfResolver: ComponentFactoryResolver,private elementRef: ElementRef) {
    }
    @HostListener('click')
  toggle() {
    if (!this._component) {
      const componentFactory = this._cfResolver.resolveComponentFactory(PopoverWindow);
      this._component = this._vcRef.createComponent(componentFactory);

    } else {
      this._vcRef.clear()
      this._component.destroy();
      this._component = null;
    }
  }
} 

But I don't want more than one tooltip to appear in the screen.
In other words , before I inject a tooltip - I want to remove all existing tooltips - if any.

Question:

How can I "find" all existing tooltips and remove them before I insert a new one ?

I know that I can add a class to each one and then remove them via removeNode but I want to do it in the Angular way.

Full Plunker

BTW - I'll be happy to find a general solution also for components and not only for directives. ( If possible).

Upvotes: 4

Views: 857

Answers (1)

Poul Kruijt
Poul Kruijt

Reputation: 71921

An obvious right answer would be to use a service and have your popup inject this service and register to it when they open and close, to know if there is a current popup open.

But lets look at another, less obvious solution.. Which can be frowned upon, but for tiny things like this, really seems to be the easiest and readable way. To use a static property on your Popover class:

@Directive({ selector: '[popover]'})
class Popover {
  private static currentPopover: Popover;

  private get active() {
      return this === Popover.currentPopover;
  } 

  private component: ComponentRef<any>;

  constructor(
       private vcRef: ViewContainerRef, 
       private cfResolver: ComponentFactoryResolver,
       private elementRef: ElementRef
  ) {}

  @HostListener('document:click')
  onDocClick() {
    if (this.active) {
      this.close();
    }
  }

  @HostListener('click', ['$event'])
  toggle(event: MouseEvent) {
    if (Popover.currentPopover && !this.active) {
      Popover.currentPopover.close();
    } 
    if (!this.active) {
      this.open();
      event.stopImmediatePropagation();
    }
  }

  open() {
    const componentFactory = this.cfResolver.resolveComponentFactory(PopoverWindow);
    this.component = this.vcRef.createComponent(componentFactory);
    Popover.currentPopover = this;
  }

  close() {
    this.vcRef.clear()
    this.component.destroy();
    this.component = null;
    Popover.currentPopover = undefined;
  }
} 

I've also added a document click listener, so when you click anywhere else, it closes the current popup.

plunkr

But if you are willing to use a service (untested code):

export class PopoverService {

    private activePopover: Popover;

    public setActive(popover: Popover): void {
        if (this.activePopover) {
            this.activePopover.close();
        }
        this.activePopover = popover;
    }   

    public isActive(popover: Popover): boolean {
       return popover === this.activePopover;
    }
}

And your directive will look like this:

@Directive({ selector: '[popover]'})
class Popover {

  private get active() {
      return this.popoverService.isActive(this);
  } 

  private component: ComponentRef<any>;

  constructor(
       private vcRef: ViewContainerRef, 
       private cfResolver: ComponentFactoryResolver,
       private elementRef: ElementRef,
       private popoverService: PopoverService
  ) {}

  @HostListener('document:click')
  onDocClick() {
    if (this.active) {
       this.close();
    }
  }

  @HostListener('click', ['$event'])
  toggle(event: MouseEvent) {
    if (!this.active) {
       this.open();           
       event.stopImmediatePropagation();
    }
  }

  open() {
    const componentFactory = this.cfResolver.resolveComponentFactory(PopoverWindow);
    this.component = this.vcRef.createComponent(componentFactory);
    this.popoverService.setActive(this);
  }

  close() {
    this.vcRef.clear()
    this.component.destroy();
    this.component = null;
  }
}

Upvotes: 3

Related Questions