如何在angular2中应用@ViewChildren进行父和子之间的通信

时间:2016-10-07 03:17:51

标签: angular typescript

我想了解@ViewChildren()装饰器。我创建了一个名为Component-A的Component,它接受用户的电子邮件地址。创建了名为“component-B”的父组件,它具有双胞胎(2个组件-A)。现在,我通过使用@Input()和@Output()装饰器,通过合并事件和属性绑定,找到了有关父组件和子组件如何相互作用的在线教程。但是如何使用@ViewChildren()获得相同的结果,我们检查输入的两封电子邮件是否相同并打印出来并匹配?

@Component({
    selector: 'component-A'
    styleUrls: [],
    templateUrl: `<form>
                   <div>
                     <fieldset>
                     <legend>Component A</legend>
                       <div class="form-horizontal">
                          <div class="form-group">
                            <label for="inputEmail3" class="col-sm-2 control-label">Email</label>
                             <div class="col-sm-10">
      <input type="email" class="form-control" id="inputEmail3" placeholder="Email">
    </div>
</div>
</fieldset>
})

  export class ComponentA {

  }

现在我有一个componentB:

@Component({
    selector: 'component-B',
    styleUrls: [],
    templateUrl: `
                  <form>
                    <div>
                      <fieldset>
                        <legend>Parent Component</legend>
                          <div class="form-horizontal">
                            <div class="form-group">
                              <label for="1" class="col-sm-2 control-label">1</label>
                               <div class="col-sm-10">
                                   <component-A></component-A>
                               </div>

                              <div class="form-group">
                              <label for="2" class="col-sm-2 control-label">2</label>
                               <div class="col-sm-10">
                                   <component-A></component-A>
                               </div>
                         </div>
                     </div>
               </fieldset>
            </div>
         </form>
})

   export class Parent {
}

2 个答案:

答案 0 :(得分:4)

我想尝试一下这个答案,因为我最近已经完成了这个,并且需要进行相当多的研究才能完全理解。 Angular2中的变化检测流程非常强大,但也非常不直观。我将回答你的问题,然后再详细介绍一下。

@ViewChildrenAfterViewChecked

如前面的答案所指定,您可以使用@ViewChildren装饰器。我将在顶部抛出一些额外的代码:

@Component({
    template: `
       <my-component #component *ngFor="let component of components"></my-component>
    `
})
export class Parent implements AfterViewChecked {
  private different: boolean = false;
  private components: any[] = [];
  @ViewChildren("component") componentList: QueryList<MyComponent>;

  // Inject the change detector for later use.
  constructor(private detector: ChangeDetectorRef) {

  }

  // Use "AfterViewChecked" hook to check for differences and apply them.
  // This will run after every change detection iteration.
  ngAfterViewChecked() {
    // Figure out if the emails are different.
    var email: string;
    var different = false;
    this.componentList.forEach(component => {
      if (email == undefined) {
        email = component.email;
      }

      if (email != component.email) {
        different = true;
      }
    });

    // If something changed, save it.
    if (different != this.different) {
      this.different = different;

      // MUST NOTIFY CHANGE DETECTOR.
      // This will tell angular to run change detection in the current scope.
      //
      // Note that we only run it when changes are detected, otherwise it will
      // cause an endless loop.
      this.detector.detectChanges();
    }
  }
}

呼!这是一个简单比较的代码。基本上,它使用AfterViewChecked事件来遍历子项,检查所有电子邮件是否匹配。如果差异状态发生变化,则会告知Angular执行一轮变更检测。

Angular2真正想要的是什么?

正如您所写,Angular2框架大力鼓励@Input@Output在组件之间进行数据更改:

  

现在,我通过使用@Input()和@Output()装饰器,通过合并事件和属性绑定,找到了有关父组件和子组件如何交互的在线教程。

例如,您可以对元素使用*ngFor并使用输入和输出传递数据。然后,您根本不需要使用@ViewChildren

<my-component 
    *ngFor="let component of components"
    (emailChanged)="emailChanged(email)">
</my-component>

使用@Input@Output完全封装Angular2变化检测,以便在正常变化检测周期内检测父项和子项之间的所有更改。你不需要做任何事情。

高效变更跟踪

基本上,此行为的原因是有效的更改跟踪。如果您已经在Angular.js(版本1)中开发,您可能知道更改跟踪是一团糟。当发生更改时,没有一个好的系统可以知道。你必须相信人们正在调用可怕的scope.apply(),它会(如果在根作用域上调用)重新编译整个DOM。这通常会导致糟糕的表现。

当某些事件发生时,Angular2具有自动更改跟踪相当数量的功能。悬停,点击,HTTP请求等。然而,它们很聪明,并使其在应用程序中的区域中发生变化。这意味着更改检测发生在较小的封装单元中,而不是始终在整个应用程序中。

其他方式

如果您有无法使用@Input@Output的数据(例如动态列表或其他原因),则必须使用ViewChildren。如果根据子项的更改检测修改父状态,则必须让更改检测器知道事情已更改。为什么?因为大多数时候,孩子变化检测轮不会与父变换检测轮对齐。发生这种情况时,您将收到以下错误消息:

  

angular2.min.js:17 EXCEPTION:在HostApp @ 0:0'中表达'isLoading'在检查后发生了变化。上一个值:'false'。当前值:[isLoading in HostApp @ 0:0]

中的'true'

这基本上是说“嘿,数据在我的变化检测周期之外发生了变化”。解决方法是修复它以便在正确的周期内进行更改,或者在工作完成后调用detectChanges()

可以在Angular高级文档站点上阅读有关此here的更多内容。

答案 1 :(得分:1)

export class Parent {
  @ViewChild(ComponentA) componentA:ComponentA;

  ngAfterViewInit() {
    console.log(this.componentA);
  }
}

有关详细信息,请参阅https://stackoverflow.com/a/35209681/217408