Programming

동적으로 이벤트 리스너 추가

procodes 2020. 6. 27. 15:11
반응형

동적으로 이벤트 리스너 추가


방금 Angular 2를 엉망으로 만들기 시작했으며 누군가 요소에서 이벤트 리스너를 동적으로 추가하고 제거하는 가장 좋은 방법을 말해 줄 수 있는지 궁금합니다.

구성 요소를 설정했습니다. 템플릿의 특정 요소를 클릭하면 mousemove동일한 템플릿의 다른 요소에 리스너를 추가하고 싶습니다 . 그런 다음 세 번째 요소를 클릭하면이 리스너를 제거하고 싶습니다.

나는 보통 Javascript를 사용하여 요소를 잡고 표준을 호출하는 방식 으로이 작업을 수행 했지만addEventListener() 더 많은 " Angular2.0 "방식 으로이 작업을 수행 해야하는지 궁금해 했습니다.


렌더러는 Angular 4.0.0-rc.1에서 더 이상 사용되지 않습니다. 아래 업데이트를 읽으십시오

angular2 방법은 사용하는 것 listen또는 listenGlobal에서 렌더러

예를 들어 클릭 이벤트를 구성 요소에 추가하려면 렌더러 및 ElementRef를 사용해야합니다 (이렇게하면 ViewChild 또는을 검색하는 모든 옵션을 사용할 수 있음 nativeElement)

constructor(elementRef: ElementRef, renderer: Renderer) {

    // Listen to click events in the component
    renderer.listen(elementRef.nativeElement, 'click', (event) => {
      // Do something with 'event'
    })
);

당신은 사용할 수 있습니다 listenGlobal당신에게 액세스 권한을 부여 할 것을 document, body

renderer.listenGlobal('document', 'click', (event) => {
  // Do something with 'event'
});

beta.2 이후 모두 참고 listen하고 listenGlobal(참조 리스너를 제거하는 기능을 반환 변경 깨는 beta.2에 대한 변경 로그에서 절). 이는 대규모 응용 프로그램에서 메모리 누수를 방지하기위한 것입니다 ( # 6686 참조 ).

따라서 동적으로 추가 한 리스너를 제거하려면 반환 된 함수를 보유 할 변수 listen또는 listenGlobal변수를 할당 한 다음 실행해야합니다.

// listenFunc will hold the function returned by "renderer.listen"
listenFunc: Function;

// globalListenFunc will hold the function returned by "renderer.listenGlobal"
globalListenFunc: Function;

constructor(elementRef: ElementRef, renderer: Renderer) {

    // We cache the function "listen" returns
    this.listenFunc = renderer.listen(elementRef.nativeElement, 'click', (event) => {
        // Do something with 'event'
    });

    // We cache the function "listenGlobal" returns
    this.globalListenFunc = renderer.listenGlobal('document', 'click', (event) => {
        // Do something with 'event'
    });
}

ngOnDestroy() {
    // We execute both functions to remove the respectives listeners

    // Removes "listen" listener
    this.listenFunc();

    // Removs "listenGlobal" listener
    this.globalListenFunc();
}

다음 은 예제가 작동 하는 plnkr 입니다. 이 예는의 사용을 포함 listen하고 listenGlobal.

Angular 4.0.0-rc.1 +와 함께 RendererV2 사용 (4.0.0-rc.3 이후의 Renderer2)

  • 25/02/2017 : Renderer더 이상 사용되지 않으므로 이제 사용해야합니다 RendererV2(아래 줄 참조). 커밋을 참조하십시오 .

  • 10/03/2017 : RendererV2로 이름이 변경되었습니다 Renderer2. 주요 변경 사항을 참조하십시오 .

RendererV2listenGlobal글로벌 이벤트 (문서, 본문, 창) 에는 더 이상 기능 이 없습니다 . 그것은 listen두 기능을 모두 달성 하는 기능 만을 가지고 있습니다.

For reference, I'm copy & pasting the source code of the DOM Renderer implementation since it may change (yes, it's angular!).

listen(target: 'window'|'document'|'body'|any, event: string, callback: (event: any) => boolean):
      () => void {
    if (typeof target === 'string') {
      return <() => void>this.eventManager.addGlobalEventListener(
          target, event, decoratePreventDefault(callback));
    }
    return <() => void>this.eventManager.addEventListener(
               target, event, decoratePreventDefault(callback)) as() => void;
  }

As you can see, now it verifies if we're passing a string (document, body or window), in which case it will use an internal addGlobalEventListener function. In any other case, when we pass an element (nativeElement) it will use a simple addEventListener

To remove the listener it's the same as it was with Renderer in angular 2.x. listen returns a function, then call that function.

Example

// Add listeners
let global = this.renderer.listen('document', 'click', (evt) => {
  console.log('Clicking the document', evt);
})

let simple = this.renderer.listen(this.myButton.nativeElement, 'click', (evt) => {
  console.log('Clicking the button', evt);
});

// Remove listeners
global();
simple();

plnkr with Angular 4.0.0-rc.1 using RendererV2

plnkr with Angular 4.0.0-rc.3 using Renderer2


I aso find this extremely confusing. as @EricMartinez points out Renderer2 listen() returns the function to remove the listener:

ƒ () { return element.removeEventListener(eventName, /** @type {?} */ (handler), false); }

If i´m adding a listener

this.listenToClick = this.renderer.listen('document', 'click', (evt) => {
    alert('Clicking the document');
})

I´d expect my function to execute what i intended, not the total opposite which is remove the listener.

// I´d expect an alert('Clicking the document'); 
this.listenToClick();
// what you actually get is removing the listener, so nothing...

In the given scenario, It´d actually make to more sense to name it like:

// Add listeners
let unlistenGlobal = this.renderer.listen('document', 'click', (evt) => {
    console.log('Clicking the document', evt);
})

let removeSimple = this.renderer.listen(this.myButton.nativeElement, 'click', (evt) => {
    console.log('Clicking the button', evt);
});

There must be a good reason for this but in my opinion it´s very misleading and not intuitive.


Here's my workaround:

I created a library with Angular 6. I added a common component commonlib-header which is used like this in an external application.

Note the serviceReference which is the class (injected in the component constructor(public serviceReference: MyService) that uses the commonlib-header) that holds the stringFunctionName method:

<commonlib-header
    [logo]="{ src: 'assets/img/logo.svg', alt: 'Logo', href: '#' }"
    [buttons]="[{ index: 0, innerHtml: 'Button', class: 'btn btn-primary', onClick: [serviceReference, 'stringFunctionName', ['arg1','arg2','arg3']] }]">
    </common-header>

The library component is programmed like this. The dynamic event is added in the onClick(fn: any) method:

export class HeaderComponent implements OnInit {

 _buttons: Array<NavItem> = []

 @Input()
  set buttons(buttons: Array<any>) {
    buttons.forEach(navItem => {
      let _navItem = new NavItem(navItem.href, navItem.innerHtml)

      _navItem.class = navItem.class

      _navItem.onClick = navItem.onClick // this is the array from the component @Input properties above

      this._buttons[navItem.index] = _navItem
    })
  }

  constructor() {}

  ngOnInit() {}

  onClick(fn: any){
    let ref = fn[0]
    let fnName = fn[1]
    let args = fn[2]

    ref[fnName].apply(ref, args)
  }

The reusable header.component.html:

<div class="topbar-right">
  <button *ngFor="let btn of _buttons"
    class="{{ btn.class }}"
    (click)="onClick(btn.onClick)"
    [innerHTML]="btn.innerHtml | keepHtml"></button>
</div>

참고URL : https://stackoverflow.com/questions/35080387/dynamically-add-event-listener

반응형