ваш вопрос "как вставить отдельные экземпляры компонентов?" но из вашего объяснения я понимаю, что вы хотите добавить строки под уже вставленными экземплярами компонентов через ng-content
. Поскольку у вас уже есть QueryList
элементов, возвращаемых ContentChildren
.
С этого момента нам нужно понять одну важную вещь о ViewContainerRef;
- ViewContainerRef раздел этой статьи
Что интересно, Angular не вставляет представления внутри элемента, а добавляет их после элемента, привязанного к ViewContainer.
Так что, если мы можем получить доступ к ViewContainerRef
элементам в нашем QueryList
, мы можем легко добавить новые элементы к этим элементам. И мы можем получить доступ к ViewContainerRef
элементам, используя read свойство метаданных ContentChildren
query;
@ContentChildren(SomeOtherComponent, { descendants: true, read: ViewContainerRef }) someOtherComponents: QueryList<ViewContainerRef>;
, поскольку у нас есть ViewContainerRef
s наших элементов, мы можем легко добавлять к ним новые элементы, используя createEmbeddedView ()
@Component({
selector: 'app-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css']
})
export class ExampleComponent implements AfterContentInit {
@ViewChild("templateToAppend", {static: true}) templateToAppend: TemplateRef<any>;
@ContentChildren(SomeOtherComponent, { descendants: true, read: ViewContainerRef }) someOtherComponents: QueryList<ViewContainerRef>;
ngAfterContentInit() {
this.someOtherComponents.forEach(ap => ap.createEmbeddedView(this.templateToAppend));
}
}
шаблон
<ng-content></ng-content>
<ng-template #templateToAppend>
<hr style="color: blue"/>
</ng-template>
демо здесь
, используя этот подход для создания директивы, чтобы выполнить ваше требование иметь что-то похожее на <ng-content select="app-some-other-component">
мы можем создать директиву, которая принимает TemplateRef
как @Input()
и добавляет ее к ViewContainerRef
export class CustomAppendable { }
@Directive({
selector: '[appMyCustomAppender]'
})
export class MyCustomAppenderDirective {
@ContentChildren(CustomAppendable, { descendants: true, read: ViewContainerRef }) appendables: QueryList<ViewContainerRef>;
@Input() appMyCustomAppender: TemplateRef<any>;
constructor() { }
ngAfterContentInit() {
setTimeout(() => {
this.appendables.forEach(ap => ap.createEmbeddedView(this.appMyCustomAppender));
});
}
}
с этим подходом, чтобы не создавать тесную связь между нашей SomeOtherComponent
и нашей директивой, мы делаем наши компоненты как-то универсальными, создавая общий тип CustomAppendable
и используем его в качестве псевдонима для компонентов, которые мы хотим запросить в ContentChildren
ПРИМЕЧАНИЕ: Я не смог найти способ заставить ContentChildren
работать с селекторами шаблонов. Как объяснено здесь мы можем использовать ContentChildren
со ссылочными переменными шаблона или типами компонентов. вот почему я создал псевдоним.
@Component({
selector: 'app-some-other-component',
templateUrl: './some-other-component.component.html',
styleUrls: ['./some-other-component.component.css'],
providers: [{ provide: CustomAppendable, useExisting: SomeOtherComponent }]
})
export class SomeOtherComponent implements OnInit {
constructor() { }
ngOnInit() {}
}
также при таком подходе нам не нужен контейнерный компонент и применяется наша директива для любого элемента.
<div [appMyCustomAppender]="templateToAppend">
<app-some-other-component>underlined</app-some-other-component>
<app-some-other-component>underlined</app-some-other-component>
<app-some-other-component2>not underlined</app-some-other-component2>
<br />
<app-some-other-component2>not underlined</app-some-other-component2>
</div>
<br />
<app-some-other-component>but not underlined!</app-some-other-component>
<ng-template #templateToAppend>
<hr style="color: red"/>
<br />
</ng-template>
демо здесь
Я надеюсь, что смог правильно понять ваши требования, и все они как-то полезны:)