-2

I am developing an angular 5 project. My home page is composed by many components. In navbarComponent I have a dropdown list. When the dropdown list is open, on clicking outside it, I would like it to close automatically.

This is my code:

ngOnInit() {
    this.showMenu = false;
}

toggle() {
    this.showMenu = !this.showMenu;
}

<div *ngIf="isConnect" class=" userStyle dropdown-toggle " (click)="toggle()">
    <ul class="dropdown-menu subMenu" role="menu" *ngIf="showMenu">
        <li (click)="profile()" class="subMenuItem"> PROFILE</li>
        <li (click)="administration()" class="subMenuItem subMenuItem-last">ADMINISTRATION</li>
        <li class="subMenuItem subMenuItem-last"><button class="btn blue-btn" (click)="logout()" ><mat-icon mat-list-icon fontIcon="icon-logout"></mat-icon>LOGOUT</button></li>
    </ul>
</div>
balping
  • 7,518
  • 3
  • 21
  • 35
Mat
  • 631
  • 3
  • 10
  • 21

3 Answers3

0

This is how I implemented it in my project. First we need to bind click event on window in ngOnInit hook.

ngOnInit() : void {
  this.windowClickSubscription = Observable
    .fromEvent(window, "click")
    .subscribe(this.handleWindowClick)
}

Now whenever there is a click on window our this.handleWindowClick will be called, lets add implementation of this method.

handleWindowClick(res: any) {
  let target: any = res.target;
  let threshold: number = 0;
  while(target && target.className != 'grouped-control' && threshold <= 4) {
    target = target.parentElement;
    threshold++;
  }
  if(target && target.className != 'grouped-control') this.hasOptions = false;
}

This function will search for parent of event target until it finds grouped-control which we need to close when there is a click on window excluding this element. So if we fount that element we do nothing else we close it using hasOptions flag.

Finally we need to unbind that event on ngDestroy

ngOnDestroy(): void {
  this.windowClickSubscription && this.windowClickSubscription.unsubscribe();
}

Now ofcourse you need to define this.windowClickSubscription property in your component and bind reference of component to function handleWindowClick in your constructor

Edit

To bind reference of constructor add the following line in your constructor

constructor() {
  this.handleWindowClick = this.handleWindowClick.bind(this);
}

This will allow you to pass this function as callback handler and it will be executed with reference of your component.

Since we can show hide html with help of *ngIf I am toggling my control which I need to hide using flag this. hasOptions

Umair Abid
  • 1,453
  • 2
  • 18
  • 35
  • What i do exactly in constructor for handleWindowClick? And in the html part what i do? – Mat Feb 11 '19 at 10:51
0

When you open Dropdown adds any class like 'open' with class 'dropdown-toggle' and when you close dropdown removes that class. If you click outside the dropdown area it will close the dropdown.

I have achieved using below code

<div class="drop-menu">
    <a class="dropdown-toggle" title="Filter" (click)="openDropdown()">
       <span class="fa fa-arrow"></span>
    </a>
    <ul class="dropdown-menu subMenu" role="menu" *ngIf="showMenu">
       <li (click)="profile()" class="subMenuItem"> PROFILE</li>
       <li (click)="administration()" class="subMenuItem subMenuItem-last">ADMINISTRATION</li>
       <li class="subMenuItem subMenuItem-last"><button class="btn blue-btn" (click)="logout()" ><mat-icon mat-list-icon fontIcon="icon-logout"></mat-icon>LOGOUT</button></li>
    </ul>
</div>

Code for component.ts file:

constructor(private renderer: Renderer2) { }
ngOnInit() {
  const selectDOM = document.getElementsByClassName('dropdown-toggle')[0];
  this.renderer.listen('document', 'click', (evt) => {
    const eventPath = evt.path;
    const hasClass = _.where(eventPath, { className: 'drop-menu' });
    if (hasClass.length <= 0) {
      this.renderer.removeClass(selectDOM, 'open');
    }
  });
}

openDropdown() {
  const selectDOM = document.getElementsByClassName('dropdown-toggle')[0];
  if (selectDOM.classList.contains('open')) {
    this.renderer.removeClass(selectDOM, 'open');
  } else {
    this.renderer.addClass(selectDOM, 'open');
  }
}
Zarna Borda
  • 695
  • 1
  • 6
  • 22
-1

Add a TemplateRef-Id to your menu:

<ul #menuRef class="dropdown-menu subMenu" role="menu" *ngIf="showMenu">

                ....
</ul>

Get that TemplateRef in Code:

@ViewChild('menuRef') menuRef: TemplateRef<any>;

Then you have to register a global (on document level) click event:

@HostListener('document:click', ['$event'])
hideMenu(event) {
  if (!this.menuRef.nativeElement.Contains(event.target) {
    if (this.showMenu) {
       this.showMenu = false;
    }
  }
}

If the click was outside of your dropdown, you set showMenu=false and your menu closes.

But why not use a component for your dropdown? ng-select does all of that automatically.

dannybucks
  • 2,217
  • 2
  • 18
  • 24