Могу ли я иметь компонент, который не отображает псевдоэлемент? - PullRequest
0 голосов
/ 04 декабря 2018

Я пытаюсь создать набор компонентов, которые позволят мне визуализировать таблицу, где «пользователю» нужно только предоставить:

  • данные (2-й массив)
  • шаблон ячейки для каждого столбца

Итак, в идеале , вы можете использовать эти компоненты для рендеринга таблицы из 3 столбцов, например:

<app-table [data]='sampleData'>
  <td>{{cell.toString().toUpperCase()}}</td>
  <td class="myClass">{{cell}}</td>
  <app-cell [cell]="cell"></app-cell>
</app-table>

... идея состоит в том, что компонент app-table будет отображать три app-column компонента для каждой строки и магически предоставлять значение ячейки для каждого столбца как cell.Пользователь компонента получает полный контроль над тем, как визуализируется каждый столбец, в том числе какие классы применяются к тегу td и т. Д.

Мне не повезло, пытаясь добиться этого.Лучшее, что я придумала до сих пор, можно увидеть в этом StackBlitz , где у меня есть app-table компонент, который отображает tr, и который ожидает, что его содержимое включает один ng-template длякаждый столбец.Разметка не так чиста, как указанная выше псевдо-разметка:

<app-table [data]='sampleData'>
  <ng-template let-cell="cell"><td>{{cell.toString().toUpperCase()}}</td></ng-template>
  <ng-template let-cell="cell"><td class="myClass">{{cell}}</td></ng-template>
  <ng-template let-cell="cell"><td><app-cell [data]="cell"></app-cell></td></ng-template>
</app-table>

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

Вы заметите, что в последнем «столбце» используется компонент app-cell, но этот компонент все еще заключен в тег td:

<ng-template let-cell="cell">
  <td>
    <app-cell [data]="cell"></app-cell>
  </td>
</ng-template>

Это не совсем то, что яwanted: я бы хотел, чтобы компонент app-cell предоставил сам тег td, поскольку в противном случае он не может (например) установить классы для этого элемента.

В настоящее время компонент app-cell имеетHostBinding, который добавляет класс ("foo"), но добавляется к самому элементу app-cell, а не к элементу td, где это необходимо:

<td>
  <app-cell class="foo">
    <div>...</div>
  </app-cell>
</td>

Очевидная вещь длясделать это, чтобы переместить тег td в компонент app-cell, но если я сделаю это , то визуализированный HTML-код будет выглядеть следующим образом:

<app-cell class="foo">
  <td>
    <div>...</div>
  </td>
</app-cell>

[OK, поэтомукласс по-прежнему применяется не в том месте, но легко увидеть, как переместить его в td tag.]

Однако этот дополнительный псевдоэлемент, расположенный между элементами tr и td, беспокоит меня - браузер, который я использую, кажется, не возражает, но я подозреваю, что оченьмоего CSS будет выброшен им, потому что есть селекторы, которые ожидают, что td будет прямым потомком tr.

Есть ли способ избавиться от этой дополнительной псевдо-элемент?Обычно я мог бы взглянуть на использование директивы, но я не вижу, как я могу это сделать здесь, потому что компоненту app-cell нужен шаблон, а директивам его нет ...

Ответы [ 2 ]

0 голосов
/ 04 декабря 2018

Эти типы компонентов являются моими любимыми.В конце дня вы хотите что-то вроде следующего, предполагая, что у вас есть данные, подобные этим

carData = [{
   model: 'Mercedes',
   year: 2015,
   km: 10000
}, {
   model: 'Audi',
   year: 2016,
   km: 5000
}];

И вы хотите отобразить первые два столбца как есть, а для последнего вы хотите отобразить какой-то пользовательский шаблон.

<app-table [data]="carData">
   <app-cell header="Model" field="model"></app-cell>
   <app-cell header="Year" field="year"></app-cell>
   <app-cell header="Km" field="km" styleClass="km-cell">
      <ng-template custom-cell-body let-car>
          {{car.km | number}} km
      </ng-template>
   </app-cell>
</app-table>

Вот что вы можете сделать, (я получил этот трюк из Angular Material и Primeng, который прекрасно работает)

Вы можете найти рабочий пример здесь

Сначала определите директиву для пользовательских шаблонов тела для ячеек.(Вы можете сделать то же самое для заголовков)

@Directive({selector: '[custom-cell-body]'})
export class AppCellBodyTemplate {
  constructor(public template: TemplateRef<any>) {}
}

И давайте определим наши AppCellComponent

app-cell.component.ts

@Component({
  selector: 'app-cell',
  template: `<ng-content></ng-content>`
})
export class AppCellComponent {
  @Input() header;
  @Input() field;
  @Input() styleClass;

  @ContentChild(AppCellBodyTemplate) customBody: AppCellBodyTemplate;
}

Давайте склеим их всевместе в AppTableComponent

app-table.component.ts

@Component({
  selector: 'app-table',
  templateUrl: './app-table.component.html' 
})
export class AppTableComponent {
  @Input() data: any[];

  @ContentChildren(AppCellComponent) cells: QueryList<AppCellComponent>;
}

app-table.component.html

  <table>
    <thead>
      <tr>
        <!-- loop through cells and create header part of the table -->
        <ng-container *ngFor="let cell of cells">
          <th>{{cell.header}}</th>
        </ng-container>
      </tr>
    </thead>
    <tbody>
      <tr *ngFor="let row of data">
        <!-- loop through cells -->
        <ng-container *ngFor="let cell of cells">
          <td [ngClass]="cell.styleClass">
            <!-- if cell has a custom body, render that -->
            <ng-container *ngIf="cell.customBody; else defaultBody">
              <ng-container *ngTemplateOutlet="cell.customBody.template;
                context: {$implicit: row}">
              </ng-container>
            </ng-container>
            <!-- else render default cell body -->
            <ng-template #defaultBody>{{row[cell.field]}}</ng-template>
          </td>
        </ng-container>
      </tr>
    </tbody>
  </table>

Вот некоторые моменты, которые необходимо объяснить.

  • template: TemplateRef<any> в пределах AppCellBodyTemplate Поскольку мы будем использовать custom-body-cell с ng-template s все время, мы можем получить ссылку на шаблон, определенный в.Этот template будет использоваться в *ngTemplateOutlet
  • @ContentChild(AppCellBodyTemplate) customBody в AppCellComponent для определения, существует ли custom-body-cell, определенный как ContentChild
  • @ContentChildren(AppCellComponent) cells: QueryList<AppCellComponent> вполучить экземпляр cells (вы сделали то же самое).
  • context: {$implicit: row} - предоставить некоторые данные потребителям этого компонента.$implicit позволяет вам получить этот контекст с любой переменной шаблона, которую вы хотите.Поскольку я использовал $implicit здесь, я смог сделать это <ng-template custom-cell-body let-car> В противном случае мне пришлось бы определить мою car переменную примерно так: let-car="rowData"
0 голосов
/ 04 декабря 2018

Отвечая на мой собственный вопрос - потому что я нашел ответ на «связанной» боковой панели: я могу использовать селектор атрибута, а не селектор элемента.

Это StackBlitz показывает результаты.

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