简体   繁体   中英

Add class to next element in Angular2 Directive

I have a dropdown menu and I wanna use angular2 directive to handling open/close of this dropdown. How can I add open class to the latest-notification div . by knowing that my directive applied to the button tag!

Here is my html code:

<div class="header-notification" (clickOutside)="showPopupNotification = false">
      <button appDropdown>
        <span [class.has-notification]="hasNotification"></span><icon name="ico_notification"></icon>
      </button>
      <div class="latest-notification">
        <span class="top-pointer"><icon name="arrow-popup"></icon></span>
        <div class="wrap">
          <ul>
            <li *ngFor="let notify of notifications" [class.seen]="notify.seen">
              <a>
                <avatar src="{{notify.userProfileUrl}}" size="35"></avatar>
                <time>{{notify.createAt}}</time>
                <h5>{{notify.name}}</h5>
                <p>{{notify.message}}</p>
              </a>
            </li>
          </ul>
        </div>
      </div>
    </div>

And here's my directive:

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

@Directive({
  selector: '[appDropdown]'
})
export class DropdownDirective {
  private isOpen = false;

  @HostBinding('class.open') get opened() {
    return this.isOpen;
  }

  @HostListener('click') open() {
    this.isOpen = !this.isOpen;
  }
  constructor() { }

}

Here is a solution I found. I don't this is a right way or best way. but at least it's working.

Now by added toggle directive to the button directive will be activated and by click on it a class with name of open will be added to the next element latest-notification . and also when click outside of the button the open class will be removed. let me know what your guys thinking .

HTML side:

<div class="header-notification">
  <button toggle>
    ...
  </button>
  <div class="latest-notification">
    ...
  </div>
</div>

and here is directive :

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

@Directive({
  selector: '[toggle]'
})
export class DropdownDirective {
  isOpen = false;

  constructor(private el: ElementRef, private renderer: Renderer) {}

  @HostListener('click') open() {
    let nextElement = this.el.nativeElement.nextElementSibling;
    this.isOpen = !this.isOpen;

    if (this.isOpen === true) {
      this.renderer.setElementClass(nextElement, "open", true);
    } else {
      this.renderer.setElementClass(nextElement, "open", false);
    }
  }

  // close dropdown if clicked outside
  public clickOutside = new EventEmitter<MouseEvent>();

  @HostListener('document:click', ['$event', '$event.target'])
  public onClick(event: MouseEvent, targetElement: HTMLElement): void {
    if (!targetElement) {
      return;
    }

    const clickedInside = this.el.nativeElement.contains(targetElement);

    if (!clickedInside) {
      this.clickOutside.emit(event);
      this.isOpen = false;
      let dropdown = this.el.nativeElement.nextElementSibling;
      this.renderer.setElementClass(dropdown,"open", false);
    }
  }


}

Sorry for late respond.

You could use exportAs meta property in directive to achieve what you want.

(I'll show you relevant lines only where you need to make changes)


/* #temp is a local template variable */
/* mydir is defined in dropdowndirective as exportAs meta property */

<button #temp=mydir appDropdown>

/* using vc which is defined in dropdown component */
<div class="latest-notification" [class.open]="vc.isOpen" [class.close]="!vc.isOpen">

import {DropdownDirective} from 'path';

export class DropDownComponent{
  @Viewchild('temp') vc:DropdownDirective;  // vc can hold isOpen variable directly defined in Dropdowndirective.
}

@Directive({
  selector: '[appDropdown]'
  exportAs:'myDir'                          // addded this line
})

Demo : http://plnkr.co/edit/AE8n4McCez7ioxiTSExL?p=preview

Hello i made an improved update to @Sajad answer regarding:

  • angular11
  • bootstrap 4
  • Renderer2
  • addClass(), removeClass()

HTML

<div class="btn-group">

    <button type="button" class="btn btn-primary dropdown-toggle"
      appDropdown
    >
      Manage Recipe
    </button>

    <div class="dropdown-menu">
      <a class="dropdown-item" href="#">To Shop List</a>
      <a class="dropdown-item" href="#">Edit Recipe</a>
      <a class="dropdown-item" href="#">Delete Recipe</a>
    </div>

  </div>

Directive

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

@Directive({
  selector: '[appDropdown]'
})
export class DropdownDirective {

  isOpen:boolean = false;

  constructor( private elRef: ElementRef, private renderer: Renderer2 ) { }

  // TOGGLE dropdownMenu
  @HostListener('click') toggleOpen(): void {

    const dropdownMenu: HTMLElement = this.elRef.nativeElement.nextSibling;

    this.isOpen = !this.isOpen;

    if( this.isOpen ) {
      this.renderer.addClass(dropdownMenu, 'show');
    }
    else{
      this.renderer.removeClass(dropdownMenu, 'show');
    }
    console.log(this.isOpen);
  }

  // CLOSE dropdown if outside click
  clickOutside = new EventEmitter<Event>();

  @HostListener('document:click', ['$event', '$event.target'])
  onClick( event: Event, targetElement: HTMLElement ): void {

    if (!targetElement || this.isOpen === false ) return;

    const dropdownMenu: HTMLElement = this.elRef.nativeElement.nextSibling;
    let clickedInside = this.elRef.nativeElement.contains(targetElement);

    if (!clickedInside) {
      this.clickOutside.emit(event);
      this.renderer.removeClass(dropdownMenu,'show');
      this.isOpen = false;
    }
  }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM