Angular-mat-grid-list不显示<ng-content>传递的子代

时间:2018-08-11 15:44:06

标签: javascript angular material-design angular6

我正在使用角度材料设计组件,并希望创建一个自定义的网格列表组件。该组件将根据其大小调整网格列表列的数量。组件模板如下所示:

<mat-grid-list [cols]="adaptiveCols">
    <ng-content></ng-content>
</mat-grid-list>

该组件将像这样使用:

<my-grid>
    <mat-grid-tile>tile_content</mat-grid-tile>
    <mat-grid-tile>tile_content</mat-grid-tile>
    <mat-grid-tile>tile_content</mat-grid-tile>
</my-grid>

因此,您会认为这将显示其中包含3个<mat-grid-list>的{​​{1}},并且结果DOM将包含这些元素。但是您看不到图块或它们的内容。

向网格列表中添加更多图块并使用固定的<mat-grid-tile>不会影响网格列表的rowHeight属性(据我所知,网格列表会计算出其高度取决于瓷砖的数量和色散/行跨度)。这使您认为网格列表没有“看到” height子级。查看source code of the grid-list并在<mat-grid-tile>上设置一个断点,确认了这一点,在此方法中_layoutTiles()为空。
我的猜测是,角度this._tiles-注释(here)在第一个代码段中找不到平铺子对象,因为它仅适用于DOM的第一层,而我们只有第一层ContentChildren

期望的结果很明确,我希望能够使用自定义的网格列表组件,为其添加一些子代并使用其功能。但是出于某种原因,angular不想让我成功,或者我仍然有很多东西要学习。

我正在使用angular 6.1,并使用official docs安装了材料组件。

1 个答案:

答案 0 :(得分:1)

mat-grid-list在其自己的实现中使用内容投影,因此它需要能够查询其投影内容。尝试添加另一级别的内容投影将使这不可能,因为mat-grid-tile并不是实际上由列表而是由组件投影的。

答案很简短:这行不通。

可能有一种方法可以实现您的目标,但内容投影却不是。如果您能澄清您的意图,我可能会提供帮助。您仍然需要使用模板。

这是简单的情况:在mat-grid-list周围编写包装器,该包装器接受数据数组作为输入,您可以使用ngFor进行迭代以创建图块

@Component({
  selector: 'my-grid',
  templateUrl: './my-grid.html'
})
export class MyGrid {
  @Input() data: string[];
}


<mat-grid-list>
  <mat-grid-tile *ngFor="let d of data">{{d}}</mat-grid-tile>
</mat-grid-list>

这在mat-grid-list周围提供了可重用的包装,为您摘要了一下。但是,如果网格内容比字符串复杂(可能)或在每次使用时都需要自定义模板,那么这样做就不好了。在这种情况下,您需要使用模板和指令的更为精细的方法:

@Directive({
  selector: 'gridTemplate'
})
export class GridTemplate {
   @Input() key: string;
   constructor(private elementRef: ElementRef, public template: TemplateRef<any>) {}
}

此指令将标识您的自定义模板,然后在网格列表中,您可以使用这些模板来填充图块

@Component({
  selector: 'my-grid',
  templateUrl: './my-grid.html'
})
export class MyGrid implements AfterContentInit {
  //structure the data so it can be assigned to a template by key
  @Input() data: {templateKey:string, data: any}[];

  //use content children to find projected templates
  @ContentChildren(GridTemplate)
  gridTemplates: QueryList<GridTemplate>;

  gridTemplateMap: {[key:string]: TemplateRef<any>} = {};

  ngAfterContentInit() { 
    //store queried templates in map for use in template
    this.gridTemplates.forEach(t => this.gridTemplateMap[t.key] = t.template);
  }
}


<ng-template #defaultGridTemp let-d="data">{{d}}</ng-template>
<mat-grid-list>
  <mat-grid-tile *ngFor="let d of data">
    <!-- here we use the template map to place the correct templates or use a default -->
    <!-- we also feed the data property as context into the template -->
    <ng-container [ngTemplateOutlet]="(gridTemplateMap[d.templateKey] || defaultGridTemp)" 
                  [ngTemplateOutletContext]="{ data: d.data }" ></ng-container>
  </mat-grid-tile>
</mat-grid-list>

然后您对组件的用法如下:

@Component({...})
export class MyComponent {
   data = [
    { templateKey:'MyKey1', data: {message: 'Projected Message', number: 1} },
    { templateKey:'MyKey2', data: {whatever: 'Message', youWant: 'to place'} }
   ];
}


<my-grid [data]="data">
  <ng-template gridTemplate key="MyKey1" let-d="data">
     <div>{{d.message}}</div>
     <div>{{d.number}}</div>
     <div>Any other arbitrary content</div>
  </ng-template>
  <ng-template gridTemplate key="MyKey2" let-d="data">
     <div>{{d.whatever}}</div>
     <div>{{d.youWant}}</div>
     <div>Any other arbitrary content</div>
  </ng-template>
</my-grid>

使用这种方法,您可以在网格组件中向所需的mat-grid-list添加任何可重用的逻辑,但仍保持向网格提供自定义模板的灵活性。缺点是您必须采取额外的步骤来构造数据以利用这种方法。