🐟

[Angular] 特定の要素以外のクリックイベント

2023/03/23に公開

特定の要素以外のクリックイベント

特定の要素以外をクリックしたときに何らかの処理を実行したい場面があると思います。
JavaScriptではaddEventListenerで比較的簡単に実装することができます。

Angularにおいてはいつくか方法がありますが、今回は@HostListenerと自作ディレクティブを使って実装してみます。

イベント処理を伴う自作ディレクティブ

自作する属性ディレクティブにイベントハンドラを設定します。

clickOutsideディレクティブの作成

src\app\shared\directive\click-outside.directive.ts
import { Directive, Output, EventEmitter, ElementRef, HostListener } from '@angular/core';

@Directive({
  selector: '[clickOutside]',
})
export class ClickOutsideDirective {

  @Output() clickOutside = new EventEmitter<any>();

  constructor(private elementRef: ElementRef) { }

  @HostListener('document:click', ['$event.target'])
  public onClick(target: any) {
    const clickedInside = this.elementRef.nativeElement.contains(target);
    if (!clickedInside) {
      // 内側
      this.clickOutside.emit(true);
    } else {
      // 外側
      this.clickOutside.emit(false);
    }
  }
}
  • @HostListenerはリッスンする DOM イベントを宣言し、そのイベントが発生したときに実行するハンドラメソッドを提供する Decoratorです。

AppModuleへ登録

src\app\app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ClickOutsideDirectiveComponent } from './click-outside-directive/click-outside-directive.component';
import { ClickOutsideDirective } from './shared/directive/click-outside.directive';

@NgModule({
  declarations: [
    AppComponent,
    ClickOutsideDirectiveComponent,
    ClickOutsideDirective
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

以上でディレクティブの定義は完了です。

使ってみる

では実際に使ってみます。

src\app\click-outside-directive\click-outside-directive.component.html
<div fxLayout="row" fxLayoutAlign="center center" class="outer">
  <div class="inner" (clickOutside)="clickInsideOrOutSide($event)" fxLayoutAlign="center center">
    <p>{{ message }}</p>
  </div>
</div>
src\app\click-outside-directive\click-outside-directive.component.ts
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-click-outside-directive',
  templateUrl: './click-outside-directive.component.html',
  styleUrls: ['./click-outside-directive.component.scss']
})
export class ClickOutsideDirectiveComponent implements OnInit {
  message = '';

  constructor() { }

  ngOnInit(): void {
  }

  clickInsideOrOutSide(isOutside: Boolean) {
    if(isOutside) {
      this.message = '外側クリックしたよ';
    } else {
      this.message = '内側クリックしたよ';
    }
  }
}

水色の部分が内側で、灰色の部分が外側です。

AppModule以外のModuleでも使いたい場合

AppModule内(app.component)で自作ディレクティブを使いたい場合は、上記の実装でも使うことができますが、SharedModuleなど別のModuleがある場合、そのModule内のComponentでは使うことができません。

解決方法としては、自作ディレクティブ専用のModuleを作成し、任意の場所でインポートするのが好ましいです。

大規模の場合こちらの方が良いと思います。

DirectivesModuleの作成

src\app\directives.module.ts
import { NgModule } from '@angular/core';
import { ClickOutsideDirective } from './shared/directive/click-outside.directive';

@NgModule({
  imports: [],
  declarations: [ClickOutsideDirective],
  exports: [ClickOutsideDirective]
})
export class DirectivesModule { }

任意のModuleでimport

import { DirectivesModule } from './directives.module';

@NgModule({
  imports: [
    DirectivesModule
  ],
  declarations: []
})
export class OtherModule { }

参考

https://www.willtaylor.blog/click-outside-directive/

https://hirakublog.com/outside-click-event-js/

Discussion