여러 구성 요소간에 양방향 바인딩하는 것은 프리미티브가 아닌 객체로 만드는 것이 좋습니다.

문제



angular 버전

부모-자식 관계에 있는 child-conponent와 parent-component가 있고, parent-component와 부모-자식 관계에 있는 grand-parent가 있다고 가정합니다.
  • grand-parent-component
  • parent-component
  • child-component

  • 이 3개 컴포넌트간에 프리미티브형의 데이터를 양방향 바인딩하는 경우, child 컴포넌트의 데이터의 변화가 grand-parent 컴포넌트까지 전해지지 않는다는 현상이 발생했습니다.

    child-component.ts
    import { Component, EventEmitter, Input, Output } from '@angular/core';
    
    @Component({
      selector: 'app-child',
      template: `<input type="checkbox" [(ngModel)]="flag"> <p>child: {{flag}}</p>`,
    })
    export class ChildComponent {
      @Input() flag: boolean;
      @Output() flagChange: EventEmitter<boolean> = new EventEmitter();
    }
    

    parent-component.ts
    import { Component, EventEmitter, Input, Output } from '@angular/core';
    
    @Component({
      selector: 'app-parent',
      template: `<app-child [(flag)]="flag"></app-child> <p>parent: {{flag}}</p>`,
    })
    export class ParentComponent {
      @Input() flag: boolean;
      @Output() flagChange: EventEmitter<boolean> = new EventEmitter();
    }
    

    grand-parent-component.ts
    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-grand-parent',
      template: `<app-parent [(flag)]="flag"></app-parent> <p>grandParent: {{flag}}</p>`,
    })
    export class GrandParentComponent {
      flag: boolean = false;
    }
    



    다음과 같이 flagChange를 emit하면 parent 구성 요소까지 전달됩니다.

    child-component.ts
    import { Component, EventEmitter, Input, Output } from '@angular/core';
    
    @Component({
      selector: 'app-child',
      template: `<input type="checkbox" [(ngModel)]="flag" (click)="onFlagChange()"> <p>child: {{flag}}</p>`,
    })
    export class ChildComponent {
      @Input() flag: boolean;
      @Output() flagChange: EventEmitter<boolean> = new EventEmitter();
    
      onFlagChange() {
        this.flag = !this.flag;
        this.flagChange.emit(this.flag);
      }
    }
    

    parent-component.ts
    import { Component, EventEmitter, Input, Output } from '@angular/core';
    
    @Component({
      selector: 'app-parent',
      template: `<app-child [(flag)]="flag"></app-child> <p>parent: {{flag}}</p>`,
    })
    export class ParentComponent {
      @Input() flag: boolean;
      @Output() flagChange: EventEmitter<boolean> = new EventEmitter();
    }
    

    grand-parent-component.ts
    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-grand-parent',
      template: `<app-parent [(flag)]="flag"></app-parent> <p>grandParent: {{flag}}</p>`,
    })
    export class GrandParentComponent {
      flag: boolean = false;
    }
    



    그러나 이 방법에서는 grand-parent-component까지는 데이터가 전해지지 않습니다.

    해결책



    프리미티브형이 아닌 오브젝트의 프로퍼티로서 프리미티브형을 건네준다.

    즉 이렇게 한다.

    child-component.ts
    import { Component, EventEmitter, Input, Output } from '@angular/core';
    import { Flag } from 'src/app/flag';
    
    @Component({
      selector: 'app-child',
      template: `<input type="checkbox" [(ngModel)]="flag.isSelected"> <p>child: {{flag.isSelected}}</p>`,
    })
    export class ChildComponent {
      @Input() flag: Flag;
      @Output() flagChange: EventEmitter<boolean> = new EventEmitter();
    }
    

    parent-component.ts
    import { Component, EventEmitter, Input, Output } from '@angular/core';
    import { Flag } from 'src/app/flag';
    
    @Component({
      selector: 'app-parent',
      template: `<app-child [(flag)]="flag"></app-child> <p>parent: {{flag.isSelected}}</p>`,
    })
    export class ParentComponent {
      @Input() flag: Flag;
      @Output() flagChange: EventEmitter<boolean> = new EventEmitter();
    }
    

    grand-parent-component.ts
    import { Component } from '@angular/core';
    import { Flag } from 'src/app/flag';
    
    @Component({
      selector: 'app-grand-parent',
      template: `<app-parent [(flag)]="flag"></app-parent> <p>grandParent: {{flag.isSelected}}</p>`,
    })
    export class GrandParentComponent {
      flag: Flag = { isSelected: false };
    }
    



    왜 이런 현상이 일어나는가



    여기 의 기사에서는 angularjs입니다만 같은 문제를 해설하고 있어, javascript 의 평가 전략(어떤 식을 어떠한 순서로 평가하는지의 규칙)이 관계하고 있다고 말하고 있습니다.

    javascript의 평가 전략에서는 premitive형의 변수는 참조원으로부터 건네받는 것이 아니라, 같은 변수를 다시 만들어 버립니다. 개체만 참조 소스에서 전달됩니다.

    여기 의 기사에서도 ngModel 는 primitive 형의 데이터는 아니고 오브젝트에 바인드 하는 것을 추천 하고 있습니다.

    좋은 웹페이지 즐겨찾기