Множественное включение с использованием ngFor в Angular2

Я новичок в angular2, и я пытаюсь создать небольшой угловой компонент под названием «grid», который просто перестраивает свое содержимое с помощью включения.

Его шаблон

шаблон компонента сетки (grid.component.ts)

<div class="grid">
  <div class="row">
    <div class="col-xs-4">
      <ng-content select="[grid-item-index=0]"></ng-content>
    </div>
    <div class="col-xs-4">
      <ng-content select="[grid-item-index=1]"></ng-content>
    </div>
    <div class="col-xs-4">
      <ng-content select="[grid-item-index=2]"></ng-content>
    </div>
  </div>
  <div class="row">
    <div class="col-xs-4">
      <ng-content select="[grid-item-index=3]"></ng-content>
    </div>
    <div class="col-xs-4">
      <ng-content select="[grid-item-index=4]"></ng-content>
    </div>
    <div class="col-xs-4">
      <ng-content select="[grid-item-index=5]"></ng-content>
    </div>
  </div>
</div>

И это часть родительского компонента, который его использует.

родительский шаблон

<grid>
  <div *ngFor="let item of items; index as i" [attr.grid-item-index]="i">
    <span>{{item}}</span>
  </div>
</grid>

Вот планкер.

Но результат не отображает содержимое. Но используя...

<grid>
  <div grid-item-index="0">item 0</div>
  <div grid-item-index="1">item 1</div>
  <div grid-item-index="2">item 2</div>
  <div grid-item-index="3">item 3</div>
  <div grid-item-index="4">item 4</div>
  <div grid-item-index="5">item 5</div>
</grid>

он отлично работает, и результат был таким, как я ожидал.

Plunker последнего рабочего случая.

Можно добиться этого результата, используя ngfor или аналогичный.

Я пытался использовать псевдокласс nth-child css, чтобы избежать использования индекса, но он тоже не работает.

ОБНОВИТЬ

Я добился некоторого прогресса, основываясь на ответе @yurzui (Спасибо!!). Он позволяет отображать содержимое со значением grid-item-index в контейнер представления с таким же значением grid-item-index.

parent.component.html

<grid>
    <ng-template *ngFor="let item of items; let i=index" 
    [grid-item-index]="(items.length-1)-i">
        <span >{{item}}</span>
    </ng-template> 
</grid>

директива grid-item-index

@Directive({
  selector: '[grid-item-index]'
})
export class GridItemIndexDirective {
  @Input('grid-item-index') index: any;

  constructor(public vcRef: ViewContainerRef, public tRef: TemplateRef) {}
}

grid.component.ts

@ContentChildren(GridItemIndexDirective) sources: QueryList<GridItemIndexDirective>;
  @ViewChildren(GridItemIndexDirective) containers: QueryList<GridItemIndexDirective>;

  constructor(
    private cdRef:ChangeDetectorRef
  ) {}

  ngAfterViewInit() {
    const len = this.sources.length;
    for (var i = 0; i < len; i++) {
      const destinationContainer = this.containers.find(x => x.index == i);
      const source = this.sources.find(x => x.index == i);
      if (destinationContainer) {
        destinationContainer.vcRef.createEmbeddedView(source.tRef);
        this.cdRef.detectChanges(); // this solves ExpressionChangedAfterItHasBeenCheckedError
      }
    }
  }

Проверьте этот Plunker.


person Jman    schedule 15.06.2017    source источник
comment
Получаются ли ваши данные асинхронно?   -  person Standaa - Remember Monica    schedule 15.06.2017
comment
Нет, это массив, определенный в родительском компоненте.   -  person Jman    schedule 15.06.2017
comment
Не могли бы вы предоставить Plunker вашей проблемы? Трудно понять, что могло вызвать такое поведение.   -  person Standaa - Remember Monica    schedule 15.06.2017
comment
В описание добавлен плунжер. Один для нерабочего случая с ngFor и другой для рабочего случая без ngfor.   -  person Jman    schedule 15.06.2017
comment
Я предполагаю, что структура вашего компонента сетки не распознается *ngFor, компонент является статическим.   -  person Standaa - Remember Monica    schedule 15.06.2017
comment
Насколько я понимаю, компонент сетки должен включать результат ngFor. И этот результат такой же, как и во втором случае (без ngFor). Я не знаю, что не так и есть ли альтернатива.   -  person Jman    schedule 15.06.2017
comment
Вариант 1 plnkr.co/edit/5YZpGSwhC5epCv6gWhjZ?p=preview Поскольку template=" устарел, то plnkr.co/edit/GzaFRu2T8vKqyPdEkBjv?p=preview   -  person yurzui    schedule 15.06.2017
comment
Вариант 2 plnkr.co/edit/u2Jin4DA12t4LhtsRQv7?p=preview   -  person yurzui    schedule 15.06.2017
comment
@yurzui Это работает очень хорошо!! Большое спасибо!. Можно ли добавить селектор, чтобы определить, какой контент добавляется в каждый шаблон ng?   -  person Jman    schedule 16.06.2017
comment
Кстати. Если вы опубликуете свое решение в качестве ответа, я могу принять его.   -  person Jman    schedule 16.06.2017
comment
@Jman Какой плункер вы имели в виду, когда спрашивали о селекторе?   -  person yurzui    schedule 16.06.2017
comment
@yurzui Я говорю о Option2. Думаю лучший вариант. Когда я спрашиваю о селекторе, я имею в виду добавление селектора, который можно использовать для выбора точного контента для добавления в любой ng-шаблон (например, grid-item-index в описании). Спасибо   -  person Jman    schedule 16.06.2017


Ответы (1)


Для этого можно использовать ngTemplateOutlet, но есть и другой подход, использующий низкоуровневый API. :

parent.component.html

<grid>
  <div *ngFor="let item of items">
    <span>{{item}}</span>
  </div>
</grid>

Дополнительная директива, которая поможет нам распознать целевой index.

@Directive({
  selector: '[grid-item-index]'
})
export class GridItemIndexDirective {
  @Input('grid-item-index') index: any;

  constructor(public vcRef: ViewContainerRef) {}
}

grid.component.html

<div class="grid">
  <div class="row">
    <div class="col-xs-4">
      <ng-template grid-item-index="1"></ng-template>
    </div>
    <div class="col-xs-4">
      <ng-template grid-item-index="2"></ng-template>
    </div>
    <div class="col-xs-4">
      <ng-template grid-item-index="3"></ng-template>
    </div>
  </div>
  <div class="row">
    <div class="col-xs-4">
      <ng-template grid-item-index="4"></ng-template>
    </div>
    <div class="col-xs-4">
      <ng-template grid-item-index="5"></ng-template>
    </div>
    <div class="col-xs-4">
      <ng-template grid-item-index="6"></ng-template>
    </div>
  </div>
</div>

grid.component.ts

@ContentChild(TemplateRef, { read: ViewContainerRef }) vcRef: ViewContainerRef;
@ViewChildren(GridItemIndexDirective) containers: QueryList<GridItemIndexDirective>;

ngAfterViewInit() {
  const len = this.vcRef.length;
  for( var i = 1; i <= len; i++) {
    const destinationContainer = this.containers.find(x => x.index == i);
    if(destinationContainer) {
      const view = this.vcRef.detach(0);
      destinationContainer.vcRef.insert(view);
    }
  }
}

Пример планкера

person yurzui    schedule 16.06.2017
comment
Можно ли добавить директиву (item-grid-index) к содержимому, чтобы сопоставить его содержимое с тем же элементом-сеткой-index в представлении? Таким образом, вы можете изменить порядок их добавления и даже удалить контент, не имеющий директивы. - person Jman; 16.06.2017
comment
Вы имеете в виду, как в исходном вопросе <div *ngFor="let item of items; index as i" [attr.grid-item-index]="i">? - person yurzui; 16.06.2017
comment
Вас также может заинтересовать эта ссылка stackoverflow.com/questions/44595942/ Он также создает таблицу. - person yurzui; 16.06.2017
comment
Вот мой пример таблицы github.com/alexzuza/angular2-typescript -systemjs/tree/lection/ - person yurzui; 16.06.2017
comment
Да, это было то, что я имею в виду. Сопоставление элемента содержимого со значением индекса элемента сетки элементу представления с тем же значением индекса элемента сетки. Я посмотрю на упомянутый вопрос. Спасибо. - person Jman; 16.06.2017
comment
Тогда я думаю, что лучше использовать ngTemplateOutlet - person yurzui; 16.06.2017
comment
Недостатком является то, что вам всегда нужно использовать массив. С помощью другого метода контент может поступать из ngFor или вы можете добавить его напрямую, что более гибко. - person Jman; 16.06.2017
comment
В любом случае, эта последняя функция приятно иметь - person Jman; 16.06.2017
comment
Этот плункер должен работать в обоих случаях plnkr.co/edit/OwTevzDOTBoW12EW67to?p=preview - person yurzui; 16.06.2017
comment
Проблема с этим последним заключается в том, что он игнорирует индекс элемента сетки при использовании ngFor. Я добился некоторого прогресса на основе одного из ваших предложений. Я думаю, что это работает довольно хорошо (вы даже можете изменить порядок элементов). Проверьте этот плункер. Любое улучшение приветствуется. Спасибо - person Jman; 17.06.2017