В Angular, как мне вставить конкретные экземпляры компонентов без использования директив / шаблонов? - PullRequest
6 голосов
/ 13 июня 2019

Итак, допустим, у меня есть компонент ExampleComponent, который создает QueryList из SomeOtherComponent компонентов в своем представлении содержимого.

import {Component, ContentChildren, QueryList} from '@angular/core'
import {SomeOtherComponent}                    from '../some-other-component/some-other-component.component'

@Component({
    selector   : 'app-example',
    templateUrl: './example.component.html',
    styleUrls  : ['./example.component.css']
})
export class ExampleComponent {
    @ContentChildren(SomeOtherComponent)
    someOtherComponents: QueryList<SomeOtherComponent>
}

В своем шаблоне у меня есть NgFor, которыйследует вставлять элемент <hr /> после каждого.

<ng-container *ngFor="let component of someOtherComponents">
    <!-- put component here -->
    <hr />
</ng-container>

, чтобы, например, это (в каком-то другом компоненте):

<app-example>
    <app-some-other-component>blablabla</app-some-other-component>
    <app-some-other-component>hello world</app-some-other-component>
    <app-some-other-component>testing</app-some-other-component>
</app-example>

привело бы к этому (вHTML):

<app-example>
    <app-some-other-component>blablabla</app-some-other-component>
    <hr />
    <app-some-other-component>hello world</app-some-other-component>
    <hr />
    <app-some-other-component>testing</app-some-other-component>
    <hr />
</app-example>

Однако здесь возникает проблема.Как мне вставить этот экземпляр SomeOtherComponent?ng-content Атрибут select не поддерживает экземпляры компонентов, Порталы CDK Я не люблю меня, я не хочу создавать какое-то сложное решение с использованием шаблонов и требовать от пользователя обернуть всеих дети в шаблонах ... Что мне делать?

Чтобы уточнить: я НЕ хочу создавать SomeOtherComponent экземпляров.Я хочу сделать что-то похожее на <ng-content select="app-some-other-component">, но вместо этого вставить определенный INSTANCE (например, один внутри QueryList, возвращаемого ContentChildren).Я также не хочу использовать директиву / шаблон (например, ставить *thisDirectiveIMadeForJustOneComponentWhichMakesItRequireBeingPlacedInItsOwnModule на всех дочерних элементах).

Примечание. Существуют и другие способы вставки горизонтальных правил после компонентов, и не стесняйтесь упоминать их, простоне забудьте ответить на вопрос тоже.Это всего лишь пример.

Ответы [ 2 ]

6 голосов
/ 17 июня 2019

ваш вопрос "как вставить отдельные экземпляры компонентов?" но из вашего объяснения я понимаю, что вы хотите добавить строки под уже вставленными экземплярами компонентов через ng-content. Поскольку у вас уже есть QueryList элементов, возвращаемых ContentChildren.

С этого момента нам нужно понять одну важную вещь о ViewContainerRef;

  1. 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>

демо здесь

Я надеюсь, что смог правильно понять ваши требования, и все они как-то полезны:)

0 голосов
/ 16 июня 2019

Насколько я понимаю, ваш вопрос звучит так, как будто вы хотите динамически вставлять компоненты. Это можно сделать, поместив нужное количество SomeOtherComponent в массив, затем внутри вашего ngFor вы создадите someOtherComponent, например, так:

import {Component, ContentChildren, QueryList} from '@angular/core'
import {SomeOtherComponent}                    from '../some-other-component/some-other-component.component'

@Component({
    selector   : 'app-example',
    templateUrl: './example.component.html',
    styleUrls  : ['./example.component.css']
})
export class ExampleComponent {
    someOtherComponents: any[];
    constructor(){
    this.someOtherComponents.push({data: "insert_stuff_here"});
    this.someOtherComponents.push({data: "insert_stuff_here"})
    }
}

Тогда в вашем ngFor

 <ng-container *ngFor="let component of someOtherComponents">
    <someOtherComponent [possibleInputHere]="component.data"></someOtherComponent>
    <hr />
</ng-container>

Надеюсь, это то, что вы ищете!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...