programing

각도 구성 요소 외부의 클릭 감지

cafebook 2023. 3. 14. 21:54
반응형

각도 구성 요소 외부의 클릭 감지

Angular에서 컴포넌트 외부의 클릭을 검출하려면 어떻게 해야 합니까?

import { Component, ElementRef, HostListener, Input } from '@angular/core';

@Component({
  selector: 'selector',
  template: `
    <div>
      {{text}}
    </div>
  `
})
export class AnotherComponent {
  public text: String;

  @HostListener('document:click', ['$event'])
  clickout(event) {
    if(this.eRef.nativeElement.contains(event.target)) {
      this.text = "clicked inside";
    } else {
      this.text = "clicked outside";
    }
  }

  constructor(private eRef: ElementRef) {
    this.text = 'no clicks yet';
  }
}

작업 예 - 여기를 클릭하십시오.

AMagyar의 답변에 대한 대안입니다.이 버전은 ngIf를 사용하여 DOM에서 제거된 요소를 클릭하면 작동합니다.

http://plnkr.co/edit/4mrn4GjM95uvSbQtxrAS?p=preview

  private wasInside = false;

  @HostListener('click')
  clickInside() {
    this.text = "clicked inside";
    this.wasInside = true;
  }

  @HostListener('document:click')
  clickout() {
    if (!this.wasInside) {
      this.text = "clicked outside";
    }
    this.wasInside = false;
  }

@Hostlistener를 통해 클릭하는 문서에 바인딩하는 것은 비용이 많이 듭니다.예를 들어 커스텀드롭다운컴포넌트를 빌드하고 폼에 여러 인스턴스를 작성하는 경우 등 과도하게 사용하면 퍼포먼스에 눈에 띄게 영향을 줄 수 있습니다.

문서 클릭 이벤트에 @Hostlistener()를 추가하는 것은 메인 앱 컴포넌트 내에서 한 번만 권장합니다.이벤트는 글로벌 유틸리티 서비스에 저장된 공용 제목 내에 클릭된 대상 요소의 값을 푸시해야 합니다.

@Component({
  selector: 'app-root',
  template: '<router-outlet></router-outlet>'
})
export class AppComponent {

  constructor(private utilitiesService: UtilitiesService) {}

  @HostListener('document:click', ['$event'])
  documentClick(event: any): void {
    this.utilitiesService.documentClickedTarget.next(event.target)
  }
}

@Injectable({ providedIn: 'root' })
export class UtilitiesService {
   documentClickedTarget: Subject<HTMLElement> = new Subject<HTMLElement>()
}

클릭된 타겟 요소에 관심이 있는 사람은 누구나 유틸리티 서비스의 퍼블릭 서브젝트에 가입하고 컴포넌트가 파괴되면 가입을 취소해야 합니다.

export class AnotherComponent implements OnInit {

  @ViewChild('somePopup', { read: ElementRef, static: false }) somePopup: ElementRef

  constructor(private utilitiesService: UtilitiesService) { }

  ngOnInit() {
      this.utilitiesService.documentClickedTarget
           .subscribe(target => this.documentClickListener(target))
  }

  documentClickListener(target: any): void {
     if (this.somePopup.nativeElement.contains(target))
        // Clicked inside
     else
        // Clicked outside
  }

J. 프랑켄슈타인의 답변 개선:

  @HostListener('click')
  clickInside($event) {
    this.text = "clicked inside";
    $event.stopPropagation();
  }

  @HostListener('document:click')
  clickOutside() {
      this.text = "clicked outside";
  }

앞의 답변은 맞지만, 관련 컴포넌트에서 포커스를 잃은 후 무거운 프로세스를 수행하는 경우에는 어떻게 해야 합니까?그 때문에, 2개의 플래그가 있는 솔루션을 생각해 냈습니다.이것에 의해, 포커스 아웃이벤트 프로세스는 관련 컴포넌트로부터 포커스를 잃었을 때만 행해집니다.

isFocusInsideComponent = false;
isComponentClicked = false;

@HostListener('click')
clickInside() {
    this.isFocusInsideComponent = true;
    this.isComponentClicked = true;
}

@HostListener('document:click')
clickout() {
    if (!this.isFocusInsideComponent && this.isComponentClicked) {
        // Do the heavy processing

        this.isComponentClicked = false;
    }
    this.isFocusInsideComponent = false;
}

ginalx의 답변은 기본 imo로 설정해야 합니다.이 메서드는 많은 최적화를 가능하게 합니다.

문제

예를 들어, 항목 목록이 있고 모든 항목에 전환해야 하는 메뉴가 포함되어 있다고 가정합니다.이 버튼에는 수신 대기 버튼의 토글 기능이 포함되어 있습니다.click저절로 일어나는 사건(click)="toggle()"사용자가 메뉴 밖에서 클릭할 때마다 메뉴를 전환합니다.아이템 리스트가 증가하면@HostListener('document:click')모든 메뉴에서 항목 내에 로드된 모든 메뉴는 메뉴가 꺼진 상태에서도 전체 문서에서 클릭 소리를 듣기 시작합니다.명백한 성능 문제를 제외하고, 이것은 불필요합니다.

예를 들어, 클릭으로 팝업이 전환될 때마다 구독하고 그 때만 "외부 클릭"을 듣기 시작할 수 있습니다.


isActive: boolean = false;

// to prevent memory leaks and improve efficiency, the menu
// gets loaded only when the toggle gets clicked
private _toggleMenuSubject$: BehaviorSubject<boolean>;
private _toggleMenu$: Observable<boolean>;

private _toggleMenuSub: Subscription;
private _clickSub: Subscription = null;


constructor(
 ...
 private _utilitiesService: UtilitiesService,
 private _elementRef: ElementRef,
){
 ...
 this._toggleMenuSubject$ = new BehaviorSubject(false);
 this._toggleMenu$ = this._toggleMenuSubject$.asObservable();

}

ngOnInit() {
 this._toggleMenuSub = this._toggleMenu$.pipe(
      tap(isActive => {
        logger.debug('Label Menu is active', isActive)
        this.isActive = isActive;

        // subscribe to the click event only if the menu is Active
        // otherwise unsubscribe and save memory
        if(isActive === true){
          this._clickSub = this._utilitiesService.documentClickedTarget
           .subscribe(target => this._documentClickListener(target));
        }else if(isActive === false && this._clickSub !== null){
          this._clickSub.unsubscribe();
        }

      }),
      // other observable logic
      ...
      ).subscribe();
}

toggle() {
    this._toggleMenuSubject$.next(!this.isActive);
}

private _documentClickListener(targetElement: HTMLElement): void {
    const clickedInside = this._elementRef.nativeElement.contains(targetElement);
    if (!clickedInside) {
      this._toggleMenuSubject$.next(false);
    }    
 }

ngOnDestroy(){
 this._toggleMenuSub.unsubscribe();
}

그리고...*.component.html:


<button (click)="toggle()">Toggle the menu</button>

MVP 대신 이벤트만 시청하면 됩니다.

@HostListener('focusout', ['$event'])
  protected onFocusOut(event: FocusEvent): void {
    console.log(
      'click away from component? :',
      event.currentTarget && event.relatedTarget
    );
  }

솔루션

부모를 모시다

var paths       = event['path'] as Array<any>;

상위 항목이 구성 요소인지 확인합니다.

var inComponent = false;    
paths.forEach(path => {
    if (path.tagName != undefined) {
        var tagName = path.tagName.toString().toLowerCase();
        if (tagName == 'app-component')
            inComponent = true;
    }
});

구성 요소가 상위 구성 요소인 경우 구성 요소 내부를 누르십시오.

if (inComponent) {
    console.log('clicked inside');
}else{
    console.log('clicked outside');
}

완전한 방법

@HostListener('document:click', ['$event'])
clickout(event: PointerEvent) {
    
    var paths       = event['path'] as Array<any>;
    
    var inComponent = false;    
    paths.forEach(path => {
        if (path.tagName != undefined) {
            var tagName = path.tagName.toString().toLowerCase();
            if (tagName == 'app-component')
                inComponent = true;
        }
    });
    
    if (inComponent) {
        console.log('clicked inside');
    }else{
        console.log('clicked outside');
    }
    
}

.clickOutside()ng-click-timeout 패키지의 메서드. "요소 외부의 클릭 이벤트 처리" 명령을 제공합니다.

NB: 이 패키지는 현재 사용되지 않습니다.상세한 것에 대하여는, https://github.com/arkon/ng-sidebar/issues/229 를 참조해 주세요.

event.stopPropagation()사용한 다른 솔루션:

  1. 상위 상위 컴포넌트에 클릭 리스너를 정의하여 클릭 리스너 변수를 클리어합니다.
  2. 먼저 이벤트를 호출한 다음 클릭 내부 변수를 설정하는 하위 구성 요소에 클릭 청취자를 정의합니다.stopPropagation()을 정의합니다.

(focusout) 또는 (blur)와 같은 이벤트 함수를 호출하여 코드를 입력할 수 있습니다.

<div tabindex=0 (blur)="outsideClick()">raw data </div>

outsideClick() {
  alert('put your condition here');
}

언급URL : https://stackoverflow.com/questions/40107008/detect-click-outside-angular-component

반응형