带有自定义组件的角度包裹角度材料选项卡组件

Un1*_*r3w 7 frontend typescript angular-material angular ng-content

我在这里想要实现的是我想将角度材料选项卡组件包装在我的共享组件中。

所以,这是我试图包装的组件:

PS:我可以在每个选项卡中显示组件:

<mat-tab-group>
  <mat-tab label="First"> Content 1 </mat-tab>
  <mat-tab label="Second"> Content 2 </mat-tab>
  <mat-tab label="Third"> Content 3 </mat-tab>
</mat-tab-group>
Run Code Online (Sandbox Code Playgroud)

所以,我想使用包装器来使用它,例如:

<app-wrapper>
  <app-item>
    <app-item-header title="first"></app-item-header>
    <app-item-content>
      <app-needs-to-be-displayed></app-needs-to-be-displayed>
    </app-item-content>
  </app-item>
</app-wrapper>
Run Code Online (Sandbox Code Playgroud)

应用程序包装.html

<mat-tab-group>
  <ng-content></ng-content>
</mat-tab-group>
Run Code Online (Sandbox Code Playgroud)

TS 类别没有变化

应用程序项目.html

<mat-tab>
    <ng-content></ng-content>
</mat-tab>
Run Code Online (Sandbox Code Playgroud)

TS 类别没有变化

应用程序项目标题.html

<ng-template mat-tab-label>
  {{title}}
</ng-template>
Run Code Online (Sandbox Code Playgroud)

app-item-header.ts 类

@Input() title:string = ''
Run Code Online (Sandbox Code Playgroud)

应用程序项目内容.html

<div>
   <ng-content></ng-content>
</div>
Run Code Online (Sandbox Code Playgroud)

TS 类没有变化,这可以容纳实际的组件

这在控制台中不会给出任何错误,但页面上也不会显示任何内容。

这是正在运行的 stackblitz 版本

https://stackblitz.com/edit/angular-wrapping-component-with-ng-content

PS:请建议大家这是实现此目标的最佳解决方案还是还有其他解决方案?

Ale*_*esD 6

您看不到任何内容,因为mat-tab组件必须是mat-tab-group组件的直接子级,否则它不知道存在任何选项卡。因此,你需要以不同的方式处理事情。您需要从自定义组件中获取内容,然后渲染包装器组件内的所有内容。我认为最简单的方法是将内容作为模板,它也非常接近您创建的内容。我将从最低的组件开始,然后向上移动。

app-item-content组件可以是您创建时的样子。需要app-item-header改变。与选项卡一样,该mat-tab-label指令也需要直接应用于mat-tab-group. 这就是我将其从模板中删除的原因。

<ng-template>
  {{title}}
</ng-template>
Run Code Online (Sandbox Code Playgroud)

主要的变化是我使用ViewChildng-template装饰器将组件内部也公开为代码中的属性。这使得也可以从组件外部访问模板。

@Component({
  selector: 'app-item-header',
  templateUrl: './item-header.component.html',
  styleUrls: ['./item-header.component.css']
})
export class ItemHeaderComponent implements OnInit {
  @ViewChild(TemplateRef) public headerTemplate: TemplateRef<any>;
  @Input() title:string = ''
}
Run Code Online (Sandbox Code Playgroud)

之后就可以移动到该app-item组件了。这里也只是对模板进行了一个小小的更改。使用与标题组件中相同的方法,您可以获得项目内容的模板。

<ng-template>
    <ng-content select="app-item-content"t></ng-content>
<ng-template>
Run Code Online (Sandbox Code Playgroud)

在代码中,您可以像以前一样再次使用ViewChild装饰器来获取内容模板。标头是项目的子组件,要获取它,您需要使用不同但相似的ContentChild装饰器来访问提供标头模板的标头组件。

@Component({
  selector: 'app-item',
  templateUrl: './item.component.html',
  styleUrls: ['./item.component.css']
})
export class ItemComponent implements OnInit {
  @ViewChild(TemplateRef) public contentTemplate: TemplateRef<any>;
  @ContentChild(ItemHeaderComponent) public itemHeader: ItemHeaderComponent;
}
Run Code Online (Sandbox Code Playgroud)

此时,您可以访问渲染mat-tab组件内内容所需的一切。在模板中,您需要渲染与每个组件相对应的mat-tab-group和 a以及之前公开的模板中的内容。mat-tabapp-item

<mat-tab-group>
  <mat-tab *ngFor="let item of appItems">
    <ng-template mat-tab-label>
    <ng-container *ngTemplateOutlet="item.itemHeader.headerTemplate"></ng-container>
    </ng-template>
    <ng-container *ngTemplateOutlet="item.contentTemplate"></ng-container>
  </mat-tab>
</mat-tab-group>
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,您可以为每个项目创建一个选项卡并在该项目的内容内进行渲染,这非常简单。有点棘手的事情是您需要在另一个模板中渲染模板的标头ng-template,以便能够添加mat-tab-label将其识别为标头内容的指令。要获取app-item包装器组件内的所有组件,您可以使用ContentChildren装饰器,它提供内容内的组件列表。与前面的示例一样,我们只是在代码中添加一个属性,其余部分由 Angular 处理。

@Component({
  selector: 'app-wrapper',
  templateUrl: './wrapper.component.html',
  styleUrls: ['./wrapper.component.css']
})
export class WrapperComponent implements OnInit {
  @ContentChildren(ItemComponent) public appItems: QueryList<ItemComponent>;
}
Run Code Online (Sandbox Code Playgroud)

我还为您创建了 StackBlitz 的一个分支,您可以在其中看到它的工作情况。