사용자가 해당 드롭 다운 외부의 아무 곳이나 클릭하면 로그인 메뉴 드롭 다운을 닫고 싶습니다. Angular2와 Angular2 “접근법”으로 수행하고 싶습니다 …
솔루션을 구현했지만 실제로는 자신감이 없습니다. 나는 같은 결과를 얻는 가장 쉬운 방법이 있어야한다고 생각하므로 아이디어가 있다면 … : 토론합시다!
내 구현은 다음과 같습니다.
드롭 다운 구성 요소 :
이것은 내 드롭 다운의 구성 요소입니다.
- 이 컴포넌트가 표시되도록 설정할 때마다 (예 : 사용자가 버튼을 클릭하여 표시 할 때) SubjectsService 내에 저장된 “global”rxjs subject userMenu를 구독합니다 .
- 그리고 숨길 때마다이 주제를 구독 취소합니다.
- 이 구성 요소의 템플릿 내 어디에서나 클릭 할 때마다 onClick () 메서드가 트리거됩니다.이 메서드는 이벤트 버블 링을 맨 위 (및 응용 프로그램 구성 요소)로 중지합니다.
여기 코드가 있습니다
export class UserMenuComponent {
_isVisible: boolean = false;
_subscriptions: Subscription<any> = null;
constructor(public subjects: SubjectsService) {
}
onClick(event) {
event.stopPropagation();
}
set isVisible(v) {
if( v ){
setTimeout( () => {
this._subscriptions = this.subjects.userMenu.subscribe((e) => {
this.isVisible = false;
})
}, 0);
} else {
this._subscriptions.unsubscribe();
}
this._isVisible = v;
}
get isVisible() {
return this._isVisible;
}
}
응용 프로그램 구성 요소 :
반면에 응용 프로그램 구성 요소 (드롭 다운 구성 요소의 부모)가 있습니다.
- 이 구성 요소는 모든 클릭 이벤트를 포착하고 동일한 rxjs 주제 ( userMenu )에서 생성합니다.
코드는 다음과 같습니다.
export class AppComponent {
constructor( public subjects: SubjectsService) {
document.addEventListener('click', () => this.onClick());
}
onClick( ) {
this.subjects.userMenu.next({});
}
}
나를 귀찮게하는 것 :
- 나는 그 구성 요소들 사이의 커넥터 역할을하는 글로벌 주제를 가지고 있다는 생각에 정말로 편안하지 않습니다.
- 에서는 setTimeout : 여기에 사용자가 버튼을 클릭 드롭 다운을 표시하는 경우, 그렇지 않으면 일이 무엇 때문이 필요합니다 :
- 드롭 다운 구성 요소의 일부가 아닌 단추를 클릭하면 드롭 다운이 표시됩니다.
- 드롭 다운이 표시되고 즉시 userMenu 주제를 구독합니다 .
- 클릭 이벤트가 앱 구성 요소에 버블 링되어 붙잡음
- 응용 프로그램 구성 요소가 userMenu 주제 에서 이벤트를 생성합니다.
- 드롭 다운 컴포넌트는 userMenu 에서이 조치를 포착 하고 드롭 다운을 숨 깁니다.
- 마지막에 드롭 다운이 표시되지 않습니다.
이 설정 시간 초과는 현재 JavaScript 코드 턴의 끝까지 구독을 지연시켜 문제를 해결하지만 매우 우아하게 생각합니다.
더 깨끗하고, 더 똑똑하고, 더 빠르거나 강한 솔루션을 알고 있다면 알려주세요 :)!
답변
당신은 (document:click)
이벤트 를 사용할 수 있습니다 :
@Component({
host: {
'(document:click)': 'onClick($event)',
},
})
class SomeComponent() {
constructor(private _eref: ElementRef) { }
onClick(event) {
if (!this._eref.nativeElement.contains(event.target)) // or some similar check
doSomething();
}
}
또 다른 방법은 사용자 지정 이벤트를 지시문으로 만드는 것입니다. Ben Nadel의 다음 게시물을 확인하십시오.
답변
우아한 방법
https://github.com/chliebel/angular2-click-outsideclickOut
지시어를
찾았습니다 . 확인하고 제대로 작동합니다 ( 프로젝트 에만 복사 ). U는 다음과 같이 사용할 수 있습니다.clickOutside.directive.ts
<div (clickOutside)="close($event)"></div>
close
사용자가 div 외부를 클릭하면 호출 될 함수는 어디에 있습니까 ? 문제가 설명 된 문제를 처리하는 매우 우아한 방법입니다.
위의 지시문을 사용하여 팝업 창을 닫는 경우 먼저 event.stopPropagation()
팝업을 여는 단추 클릭 이벤트 핸들러 에 추가 해야합니다.
보너스:
아래에서 파일에서 oryginal 지시어 코드를 복사합니다 clickOutside.directive.ts
(향후 링크가 작동하지 않을 경우). 저자는 Christian Liebel입니다 .
답변
나는 이렇게했다.
문서 click
와 해당 핸들러 에 이벤트 리스너를 추가하여 내 container
포함 여부를 확인했습니다 event.target
. 드롭 다운을 숨 깁니다.
이렇게 보일 것입니다.
@Component({})
class SomeComponent {
@ViewChild('container') container;
@ViewChild('dropdown') dropdown;
constructor() {
document.addEventListener('click', this.offClickHandler.bind(this)); // bind on doc
}
offClickHandler(event:any) {
if (!this.container.nativeElement.contains(event.target)) { // check click origin
this.dropdown.nativeElement.style.display = "none";
}
}
}
답변
나는 Sasxa가 대부분의 사람들에게 대답을 받아 들였다고 생각합니다. 그러나 오프 클릭 이벤트를 수신 해야하는 Element의 내용이 동적으로 변경되는 상황이있었습니다. 따라서 Elements nativeElement는 동적으로 작성된 event.target을 포함하지 않았습니다. 다음 지시문으로 해결할 수 있습니다
@Directive({
selector: '[myOffClick]'
})
export class MyOffClickDirective {
@Output() offClick = new EventEmitter();
constructor(private _elementRef: ElementRef) {
}
@HostListener('document:click', ['$event.path'])
public onGlobalClick(targetElementPath: Array<any>) {
let elementRefInPath = targetElementPath.find(e => e === this._elementRef.nativeElement);
if (!elementRefInPath) {
this.offClick.emit(null);
}
}
}
elementRef에 event.target이 포함되어 있는지 확인하는 대신 elementRef가 이벤트의 경로 (DOM 경로)에 있는지 확인합니다. 이렇게하면 동적으로 생성 된 요소를 처리 할 수 있습니다.
답변
iOS에서이 작업을 수행하는 경우 touchstart
이벤트도 .
Angular 4부터는 HostListener
꾸미기가 선호되는 방법입니다.
import { Component, OnInit, HostListener, ElementRef } from '@angular/core';
...
@Component({...})
export class MyComponent implement OnInit {
constructor(private eRef: ElementRef){}
@HostListener('document:click', ['$event'])
@HostListener('document:touchstart', ['$event'])
handleOutsideClick(event) {
// Some kind of logic to exclude clicks in Component.
// This example is borrowed Kamil's answer
if (!this.eRef.nativeElement.contains(event.target) {
doSomethingCool();
}
}
}
답변
우리는 오늘 직장에서 비슷한 문제를 해결하기 위해 드롭 다운 div를 클릭하면 사라지는 방법을 알아 내려고 노력했습니다. 우리는 다른 구성 요소 또는 지시문 을 클릭하지 않고 특정 div 외부 에서 클릭하기를 원하지 않기 때문에 초기 포스터의 질문과 약간 다릅니다 .
우리는 (window : mouseup) 이벤트 핸들러를 사용하여 문제를 해결했습니다.
단계 :
1.) 전체 드롭 다운 메뉴 div에 고유 한 클래스 이름을 지정했습니다.
2.) 내부 드롭 다운 메뉴 자체 (메뉴를 닫지 않기 위해 클릭하려는 유일한 부분)에 (window : mouseup) 이벤트 핸들러를 추가하고 $ event에 전달했습니다.
참고 : 일반적인 “클릭”처리기는 부모 클릭 처리기와 충돌하기 때문에 수행 할 수 없습니다.
3.) 컨트롤러에서, 우리는 클릭 아웃 이벤트에서 호출되기를 원하는 메소드를 만들었고 , 클릭 된 스팟이 타겟 클래스 div 내에 있는지 알아 내기 위해 event.closest ( docs here )를 사용합니다 .
autoCloseForDropdownCars(event) {
var target = event.target;
if (!target.closest(".DropdownCars")) {
// do whatever you want here
}
}
<div class="DropdownCars">
<span (click)="toggleDropdown(dropdownTypes.Cars)" class="searchBarPlaceholder">Cars</span>
<div class="criteriaDropdown" (window:mouseup)="autoCloseForDropdownCars($event)" *ngIf="isDropdownShown(dropdownTypes.Cars)">
</div>
</div>
답변
드롭 다운에 형제 요소를 만들어 보이지 않는 전체 화면을 덮고 클릭 이벤트를 캡처 할 수 있습니다. 그런 다음 해당 요소의 클릭을 감지하고 클릭하면 드롭 다운을 닫을 수 있습니다. 요소가 실크 스크린 클래스라고 가정 해 보겠습니다. 여기에 스타일이 있습니다.
.silkscreen {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1;
}
z- 색인은 드롭 다운을 제외한 모든 것 위에 배치하기에 충분히 높아야합니다. 이 경우 내 드롭 다운은 z-index 2입니다.
다른 답변은 경우에 따라 효과가 있었지만 때로는 요소 내의 요소와 상호 작용할 때 내 드롭 다운이 닫히고 싶지 않았기 때문에 제외했습니다. 예상대로 이벤트 대상에 따라 구성 요소에 포함되지 않은 요소를 동적으로 추가했습니다. 그 엉망을 분류하는 대신 실크 스크린 방식으로 시도해 보았습니다.