目前我试图在子组件中投影第三个组件,该子组件投影在 ngFor
循环内(子内),但在父级中,每当我使用索引更改或设置投影内容中的某些属性时父级中的查询列表(ViewChildren('#thirdComponent')) 所有子级的投影内容显示相同的变化。有什么正确的方法可以做到这一点。
是否是由于在子组件中的内容投影位置复制了 select 属性绑定(bind)。子组件的投影是在 Accordion 内完成的,一次打开一个或多个面板。
@Component({
selector: "my-app",
template: `
<child-comp #child>
<ng-container selected>
<some-other-comp #someOtherComp></some-other-comp>
</ng-container>
</child-comp>
`,
styleUrls: ["./app.component.css"]
})
export class AppComponent implements AfterViewInit {
h = 0;
i = 1;
j = 2;
k = 3;
@ViewChildren("someOtherComp") otherCompList: QueryList<SomeOtherComponent>;
ngAfterViewInit(): void {
this.otherCompList.toArray()[this.h].prop = this.h;
// below will result in undefined due to QueryList of size 1
// this.otherCompList.toArray()[this.i].prop = this.i;
// this.otherCompList.toArray()[this.j].prop = this.j;
// this.otherCompList.toArray()[this.k].prop = this.k;
}
}
@Component({
selector: "child-comp",
template: `
<div *ngFor="let value of [1, 2, 3]; let i = index">
<!-- if ngIf is removed than only the last projection is diplayed -->
<div *ngIf="i === 0">
<ng-content select="[selected]"> </ng-content>
</div>
</div>
`,
styleUrls: ["./app.component.css"]
})
export class ChildComponent {}
@Component({
selector: "some-other-comp",
template: `
<p>{{ prop }}</p>
`,
styleUrls: ["./app.component.css"]
})
export class SomeOtherComponent {
prop: any;
}
最佳答案
利用 *ngTemplateOutlet 和 let 变量
我们可以将模板传递到我们的子组件中,并利用 @Input()
装饰器结合 *ngTemplateOutlet
直接从父级的 HTML 模板访问属性。
例子
首先,我在父组件中定义了一个数组,我想将其用作外部子组件中循环的基础。
父组件
@Component({
selector: 'parent',
templateUrl: 'parent.component.html',
styleUrls: ['parent.component.scss']
})
export class ParentComponent implements OnInit {
dataItems: { title: string, description: string }[] = [{
title: 'First Element',
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eveniet, nihil!'
}...] // remaining items truncated for brevity.
constructor() {
}
ngOnInit(): void {
}
}
然后这个父组件有一个子组件,它接受整个项目列表的输入
<child [items]="dataItems"></child>
子组件(拳头级)
@Component({
selector: 'child',
templateUrl: 'child.component.html',
styleUrls: ['child.component.scss']
})
export class ChildComponent implements OnInit {
@Input() items!: any[];
constructor() {
}
ngOnInit(): void {
}
}
<ng-container *ngFor="let childItem of items">
<projected [item]="childItem">
<ng-template let-item>
<h4>{{item.title}}</h4>
<p>{{item.description}}</p>
</ng-template>
</projected>
</ng-container>
投影组件(子子)
@Component({
selector: 'projected',
templateUrl: 'projected.component.html',
styleUrls: ['projected.component.scss']
})
export class ProjectedComponent implements OnInit {
@Input() item: any;
@ContentChild(TemplateRef) templateOutlet!: TemplateRef<any>
constructor() {
}
ngOnInit(): void {
}
}
<ng-container *ngTemplateOutlet="templateOutlet; context: {$implicit: item}"></ng-container>
<ng-content></ng-content>
它是如何工作的
父组件在这种关系中并不是绝对必要的,因为我们不会将内容直接从父组件投影到 ProjectedComponent
中,我只是选择在此处定义一个项目列表,以保持与您的问题类似的层次结构。
子组件
子组件做了两件事:
- 定义一个 *ngFor 循环来遍历一些元素集合。
- 定义如何在
ProjectedComponent
中使用这些元素的模板。的模板。
在ProjectedComponent
我们利用 @ContentChild
装饰器选择我们希望通过 <ng-content>
给出的 TemplateRef
然后使用 *ngTemplateOutlet
将该模板放入容器中这也允许我们为局部变量创建数据绑定(bind)上下文。
context: {$implicit: item}
告诉 Angular 任何 let-*
在模板上定义的没有任何显式绑定(bind)的变量应该绑定(bind)到我们组件中的 item 属性。
因此,我们能够在父组件级别的模板中引用此属性。
编辑
从技术上讲,如果您想直接在子组件内部定义模板,则不需要上下文绑定(bind),因为您可以直接引用 *ngFor
模板,但是如果你想将模板提升到 ParentComponent
就变得有必要了使解决方案更具可重用性。
关于javascript - *ngFor 中的 Angular 内容投影是否共享相同的数据?如何从父访问子组件的项目内容?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67175347/