Как исправить проблему с закрытием раскрывающегося списка, когда я нажимаю за его пределами

Я разрабатываю проект angular 5. Моя домашняя страница состоит из многих компонентов. В navbarComponent у меня есть выпадающий список. Когда раскрывающийся список открыт, при нажатии за его пределами я бы хотел, чтобы он закрывался автоматически.

Это мой код:

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>
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2
0
136
3

Ответы 3

Добавьте TemplateRef-Id в ваше меню:

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

                ....
</ul>

Получите этот TemplateRef в коде:

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

Затем вам нужно зарегистрировать глобальное (на уровне документа) событие клика:

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

Если щелчок был за пределами раскрывающегося списка, вы устанавливаете showMenu=false, и ваше меню закрывается.

Но почему бы не использовать компонент для раскрывающегося списка? нг-выбрать делает все это автоматически.

Вот как я реализовал это в своем проекте. Сначала нам нужно привязать событие клика к окну в хуке ngOnInit.

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

Теперь всякий раз, когда происходит щелчок по окну, будет вызываться наш this.handleWindowClick, давайте добавим реализацию этого метода.

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;
}

Эта функция будет искать родителя цели события, пока не найдет grouped-control, который нам нужно закрыть, когда есть щелчок по окну, исключая этот элемент. Поэтому, если мы нашли этот элемент, мы больше ничего не делаем, мы закрываем его, используя флаг hasOptions.

Наконец, нам нужно отвязать это событие на ngDestroy.

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

Теперь, конечно, вам нужно определить свойство this.windowClickSubscription в вашем компоненте и привязать ссылку компонента к функции handleWindowClick в вашем конструкторе.

Редактировать

Чтобы связать ссылку на конструктор, добавьте следующую строку в свой конструктор

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

Это позволит вам передать эту функцию в качестве обработчика обратного вызова, и она будет выполняться со ссылкой на ваш компонент.

Поскольку мы можем показать скрытый html с помощью *ngIf, я переключаю свой элемент управления, который мне нужно скрыть, используя флаг this. hasOptions.

Что я делаю именно в конструкторе для handleWindowClick? И что я делаю в html-части?

Mat 11.02.2019 11:51

Когда вы открываете раскрывающийся список, добавляется любой класс, например «открытый», с классом «раскрывающийся список», а когда вы закрываете раскрывающийся список, этот класс удаляется. Если щелкнуть за пределами области раскрывающегося списка, он закроется.

Я добился, используя приведенный ниже код

<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>

Код для файла component.ts:

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');
  }
}

Другие вопросы по теме