我想了解@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 {
}
答案 0 :(得分:4)
我想尝试一下这个答案,因为我最近已经完成了这个,并且需要进行相当多的研究才能完全理解。 Angular2中的变化检测流程非常强大,但也非常不直观。我将回答你的问题,然后再详细介绍一下。
@ViewChildren
和AfterViewChecked
如前面的答案所指定,您可以使用@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框架大力鼓励@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);
}
}