代码之家  ›  专栏  ›  技术社区  ›  Gary McGill

我可以有一个不呈现伪元素的组件吗?

  •  3
  • Gary McGill  · 技术社区  · 6 年前

    我正在尝试创建一组允许我呈现表的组件,其中“用户”只需要提供:

    • 数据(二维数组)
    • 每列的单元格模板

    所以, 理想的 ,您可以使用这些组件来呈现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 标签等。

    为了达到这个目标,我运气不好。到目前为止,我能想到的最好的办法是 this StackBlitz ,我有一个 应用程序表 呈现 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>
    

    这不是我真正想要的:我想要 APP细胞 组件以提供 TD 标记本身,否则它不能(例如)在该元素上设置类。

    目前, APP细胞 组件具有 HostBinding 这会添加一个类(“foo”),但会添加到 APP细胞 元素本身,而不是 TD 需要的元素:

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

    很明显要做的是移动 TD 标签进入 APP细胞 组件,但如果我 do that ,则呈现的HTML如下所示:

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

    [好的,所以这个类仍然应用在错误的地方,但是很容易看到如何将其向下移动到 TD 标签。

    但是,这个额外的伪元素位于 土耳其 TD 元素让我担心——我使用的浏览器似乎并不介意,但我怀疑我的大部分CSS都会被它抛出,因为有选择器希望 TD 成为 TR .

    有没有办法去掉多余的伪元素?通常我可能会考虑使用一个指令,但我不知道在这里如何做到这一点,因为 APP细胞 组件需要模板,而指令没有模板…

    2 回复  |  直到 6 年前
        1
  •  2
  •   Bunyamin Coskuner    6 年前

    这些类型的组件是我最喜欢的。在一天结束的时候,如果你有这样的数据,你需要如下的东西

    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>
    

    这是你能做的。(我从角材料中得到了这个技巧,并且涂了底漆,效果很好)

    你可以找到一个有效的例子 here

    首先为单元格的自定义正文模板定义一个指令。(您也可以对标题执行相同的操作)

    @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 一直以来,我们都可以得到在中定义的模板的引用。这个 template 将在 *ngTemplateOutlet
    • @ContentChild(AppCellBodyTemplate) customBody 在内部 应用程序单元组件 是为了检测是否存在 自定义正文单元格 定义为 ContentChild
    • @ContentChildren(AppCellComponent) cells: QueryList<AppCellComponent> 是要获取的实例 cells (你做了类似的事)。
    • context: {$implicit: row} 是向此组件的使用者公开一些数据。 $implicit 使您能够使用所需的任何模板变量检索此上下文。自从我用过 隐式美元 在这里,我能做到 <ng-template custom-cell-body let-car> 否则,我将不得不定义 car 像这样的变量 let-car="rowData"
        2
  •  0
  •   Gary McGill    6 年前

    回答我自己的问题-因为我发现 an answer 在“相关”侧边栏中:我可以使用属性选择器而不是元素选择器。

    这个 StackBlitz 显示结果。

    推荐文章