구성 요소 순환 참조 문제 및 현재 해결 방법

18524 단어 Angular

어떤 시기에 일어나는가



특정 Dialog를 표시하고 싶습니다. Dialog의 Component 중 Acomponent, Bcomponent의 표시를 동적으로 변화시키고 싶다.
@Component({
})
export class  Acomponent implements OnInit {
  constructor(private dialog: Dialog) {

  }

  open() {
    /// AcomponentでDialogを経由してBcomponentを呼び出す
    this.dialog.open(Bcomponent)
  }
}


@Component({
})
export class  Bcomponent implements OnInit {
  constructor(private dialog: Dialog) {

  }

  open() {
    /// BcomponentでDialogを経由してAcomponentを呼び出す
    this.dialog.open(Acomponent)
  }
}

이 때 compile시에 다음과 같은 warning이 표시된다.
WARNING in Circular dependency detected: 
a-component.ts -> dialog.ts -> b-component.ts

WARNING in Circular dependency detected: 
b-component.ts -> dialog.ts -> a-component.ts

해결 방법


  • ParentDialog에 Acomponent와 Bcomponent를 넣는다.
  • 자식 Component의 전환은 Output을 통해 이루어진다.
  • Dialog 자체의 close는 서비스를 통해 이루어집니다.
  • enum 의 ParentDialogComponent tsFile 로부터 분리하는 것으로 순환 참조를 막는다.

  • 도해하면


    DialogNameEnum


    export enum DialogName {
      A,
      B
    }
    

    ParentComponent


    <app-a-component *ngIf="componentList[0].active" (toDialog)="openDialog($event)"></app-a-component>
    <app-b-component *ngIf="componentList[1].active" (toDialog)="openDialog($event)"></app-b-component>
    
    interface DialogList {
      name: DialogName;
      component: any;
      active: boolean;
    }
    
    export interface ParentDialogData {
      dialogName: DialogName;
    }
    
    @Component({
      selector: 'app--parent-dialog',
      templateUrl: './parent-dialog.component.html',
      styleUrls: ['./parent-dialog.component.scss'],
    })
    export class ParentDialogComponent implements OnInit, OnDestroy {
      loading$: Observable<boolean>;
      private dialogCloseSubscription: Subscription;
      componentList: Array<DialogList> = [
        {
          active: true,
          name: DialogName.A,
          component: AComponent,
        },
        {
          active: false,
          name: DialogName.B,
          component: BComponent,
        },
      ];
      constructor(
        @Inject(MAT_DIALOG_DATA) public data: ParentDialogData,
        public readonly matDialogRef: MatDialogRef<ParentDialogComponent>,
        private dialogService: DialogCloseService,
      ) {
        this.query.selectLoading();
      }
    
      ngOnInit() {
        this.openDialog(this.data.dialogName);
        this.dialogCloseSubscription = this.dialogService.sharedDataSource$.subscribe((msg) => {
          if (msg === 'close') {
            this.matDialogRef.close();
          }
        });
      }
    
      openDialog(name: DialogName) {
        this.componentList.forEach((c) => (c.active = false));
        this.componentList.find((c) => c.name === name).active = true;
      }
    
      close() {
        this.matDialogRef.close();
      }
    
      ngOnDestroy(): void {
        this.dialogCloseSubscription.unsubscribe();
      }
    }
    
    

    Acomponent


    @Component({
      selector: 'app-a',
      templateUrl: './a.component.html',
      styleUrls: ['./a.component.scss'],
      changeDetection: ChangeDetectionStrategy.Default,
    })
    export class AComponent implements OnInit {
      @Output() toDialog: EventEmitter<DialogName> = new EventEmitter();
    
      constructor(
        private readonly dialog: DialogCloseService,
      ) {}
    
      ngOnInit() {
      }
    
      toBDialog() {
        this.toDialog.emit(DialogName.B);
      }
    
      closeDialog() {
        this.dialog.close();
      }
    

    Bcomponent


    @Component({
      selector: 'app-b',
      templateUrl: './b.component.html',
      styleUrls: ['./b.component.scss'],
      changeDetection: ChangeDetectionStrategy.Default,
    })
    export class BComponent implements OnInit {
      @Output() toDialog: EventEmitter<DialogName> = new EventEmitter();
    
      constructor(
        private readonly dialog: DialogCloseService,
      ) {}
    
      ngOnInit() {
      }
    
      toADialog() {
        this.toDialog.emit(DialogName.A);
      }
    
      closeDialog() {
        this.dialog.close();
      }
    

    CloseService



    Subject를 통해 close 감지
    import { Injectable } from '@angular/core';
    import { Subject } from 'rxjs';
    
    @Injectable({
      providedIn: 'root',
    })
    export class DialogCloseService {
      constructor() {}
      private sharedDataSource = new Subject<string>();
      public sharedDataSource$ = this.sharedDataSource.asObservable();
    
      close() {
        this.sharedDataSource.next('close');
      }
    }
    

    요약



    단점으로서는 전환하는 component가 늘어날 때마다 ParentComponent를 수정하지 않으면 안되는 곳.
    ngComponentOutlet등을 구사해 스마트하게 하기 위한 안 기다리고 있습니다.

    좋은 웹페이지 즐겨찾기