【问题标题】:Angular 2 Children components detect changes in parentAngular 2 Children组件检测父组件的变化
【发布时间】:2016-02-08 15:52:43
【问题描述】:

我有一个显示“项目”列表的组件,这些项目是使用选择器创建的组件。当单击以更新所有子组件的“状态”时,我有一个我想要的复选框。

我真的很难找到正确的解决方案。 请参阅Plunkr 了解更多信息。

//our root app component
import {Component, EventEmitter} from 'angular2/core'

class Item {
  name: boolean;

  constructor(name: string) {
    this.name = name;
  }
}

@Component({
  selector: 'my-item',
  template: `
    <div>
      <label><input type="checkbox" [(ngModel)]="state"/> {{state}}</label>
    </div>
  `
})
export class MyItemComponent {
  state: boolean = false;
}

@Component({
  selector: 'my-app',
  template: `
    <div style="border: 1px solid red;">
      <label><input type="checkbox" [(ngModel)]="state"/> {{state}}</label>
    </div>
    <div *ngFor="#item of items">
      <my-item></my-item>
    </div>
  `,
  directives: [MyItemComponent]
})
export class App {
  state: boolean = true;
  items: Item[] = [];

  constructor() {
    this.items.push(new Item("hello"));
    this.items.push(new Item("test"));
  }
}

【问题讨论】:

    标签: typescript angular


    【解决方案1】:

    更新

    @Component({
      selector: 'my-item',
      inputs: ['state']; // added
      template: `
        <div>
          <label><input type="checkbox" [(ngModel)]="state"/> {{state}}</label>
        </div>
      `
    })
    export class MyItemComponent {
      state: boolean = false;
    }
    

    然后像这样使用它

    <my-item [state]="state"></my-item>
    

    原创

    角度变化检测不会检测数组中的变化。
    这应该使它工作:

      constructor() {
        this.items.push(new Item("hello"));
        this.items.push(new Item("test"));
        this.items = this.items.slice();
      }
    

    这样一个新数组(一个副本)被分配给this.items,因此 Angular 会将其识别为更改。

    MyItem 中,您需要一个input

    @Component({
      selector: 'my-item',
      inputs: ['items']; // added
      template: `
        <div>
          <label><input type="checkbox" [(ngModel)]="state"/> {{state}}</label>
        </div>
      `
    })
    export class MyItemComponent {
      state: boolean = false;
      items: Item[]; // added
    }
    

    然后你建立连接

    <my-item [items]="items"></my-item>
    

    要在items 更改实现ngOnChanges() 时在MyItemComponent 中调用代码,另请参阅https://angular.io/docs/ts/latest/api/core/OnChanges-interface.html

    export class MyItemComponent {
      state: boolean = false;
      items: Item[]; // added
      ngOnChanges(changes: {[propName: string]: SimpleChange}) {
        console.log('ngOnChanges - myProp = ' + changes['items'].currentValue);
      }
    }
    

    【讨论】:

    • 我不需要检测数组的变化。我只需要子组件能够检测到父“状态”布尔值的变化。
    • 那么原理是一样的,但是不需要切片和ngOnChanges()
    • 好的,感谢您的意见。我现在使用组件的“属性”对其进行了测试。
    • propertiesinputs 的已弃用前身。
    【解决方案2】:

    @Günter 所说的,完全正确!

    也就是说,我发现你的 plunkr 有一些错误:

    @Component({
      selector: 'my-item',
      template: `
        <div>Hello</div>
      `
    });  // <------- Remove the ;
    export class MyItemComponent {
    
    }
    

    而你错过了directives 属性中的组件:

    @Component({
      selector: 'my-app',
      template: `
        <div>
          <label><input type="checkbox" [(ngModel)]="state"/> {{state}}</label>
        </div>
        <div *ngFor="#item of items">
          <my-item></my-item>
        </div>
      `,
      directives: [] // <-------- Add the MyItemComponent component
    })
    export class App {
      (...)
    }
    

    编辑

    您可以利用 @ViewChildren 装饰器直接引用子级。

    @Component({
      selector: 'my-app',
      template: `
        (...)
      `,
      directives: [MyItemComponent]
    })
    export class App {
      (...)
      @ViewChildren(MyItemComponent)
      children:MyItemComponent[];
      (...)
    }
    

    然后您可以在复选框中添加一个控件,以检测更改并相应地更新子组件的状态:

    @Component({
      selector: 'my-app',
      template: `
        <div style="border: 1px solid red;">
          <label><input type="checkbox" [(ngModel)]="state"  
                [ngFormControl]="stateCtrl"/> {{state}}</label>
        </div>
        <div *ngFor="#item of items">
          <my-item></my-item>
        </div>
      `,
      directives: [MyItemComponent]
    })
    export class App {
      (...)
      constructor() {
        this.items.push(new Item("hello"));
        this.items.push(new Item("test"));
    
        this.stateCtrl = new Control();
        this.stateCtrl.valueChanges.subscribe(
          data => {
            this.children._results.forEach(child => {
              child.state = data;
            });
          });
      }
    }
    

    我用这种方法更新了你的 plunkr:https://plnkr.co/edit/nAA2VxZmWy0d4lljvPpU?p=preview

    查看此链接了解更多详情:Why is that i can't inject the child component values into parent component?

    【讨论】:

    • 我想我在粘贴链接之前忘记将最终更改保存到 Plunkr。请立即查看更新。我不确定解决方案是我试图实现的目标。当我勾选/取消勾选红色内的复选框时,我希望两个/所有孩子也更新他们的状态。
    • 哦!我使用基于 @ViewChildren 装饰器和表单控件的方法分叉并更新了您的 plunkr。我用它的描述更新了我的答案......
    • 有趣的方法,但它要求 App 组件的逻辑与子组件的模板保持同步——即,App 逻辑需要知道子组件的模板正在使用 state 属性。这是非常紧密的耦合,因此相当脆弱。最好使用输入属性,它充当子组件的公共 API。另一种减少耦合的方法(使用您的方法)是向子组件添加一个方法,该方法设置内部 state 属性。然后App组件可以调用那个公共API方法,而不是直接修改state
    猜你喜欢
    • 2017-06-04
    • 2017-06-07
    • 2018-07-10
    • 1970-01-01
    • 2018-05-14
    • 2018-09-06
    • 2021-05-29
    • 2018-06-14
    • 2018-02-13
    相关资源
    最近更新 更多