如何在Angular组件中呈现重复内容

huangapple go评论65阅读模式
英文:

How to project repetitive content in angular component

问题

我正在构建一个树组件。数据是一个节点数组

export interface TreeNode {
  id: number;
  value: any;
  parent: number | null;
}
...
@Input()
data: TreeNode[] = [];

我完成了绑定,一切都很好,但我希望我的组件是可定制的。
目前,我显示层次结构(表示节点的卡片和连接它们的箭头)

<!-- 简化起见 -->
<div class="tree-container">
  <div *ngFor="let node of nodes" class="tree-node">{{node.value}}</div>
</div>

这适用于简单的值,但我希望可以基于值投影内容。

例如,Angular Material Table 允许以下代码

<ng-container matColumnDef="name">
  <th mat-header-cell *matHeaderCellDef> Name </th>
  <td mat-cell *matCellDef="let element"> {{element.name}} </td>
</ng-container>

我希望我的组件像这样实现

<tree-view [data]="data">
  <div tree-node *nodes="let node"> {{node.id}} <span>Yay,现在我可以显示自定义内容,比如名字 {{node.value.name}}</span></div>
</tree-view>

我怎样才能实现这一点?

我知道内容投影的全部是 ng-content,我知道我可以使用插槽,但我只能在单一位置使用它,而不能用于重复的内容。有什么线索吗?

注意:我正在使用 Angular 15

英文:

I'm building a tree component. where the data is an array of nodes

export interface TreeNode {
  id:number;
  value:any;
  parent:number|null;
}
...
@Input()
data:TreeNode[] = [];

I do my binding and everything is fine, Yet, I want my component to be customizable.
For now, I'm displaying the Hierarchy (cards representing nodes, and arrows to link them)

&lt;!-- for simplicity --&gt;
&lt;div class=&quot;tree-container&quot;&gt;
  &lt;div *ngFor=&quot;let node of nodes&quot; class=&quot;tree-node&quot;&gt;{{node.value}}&lt;/div&gt;
&lt;/div&gt; 

this works fine with simple values but I want it possible to project content based on the value.

For example, the Angular Material Table allows the following code

  &lt;ng-container matColumnDef=&quot;name&quot;&gt;
    &lt;th mat-header-cell *matHeaderCellDef&gt; Name &lt;/th&gt;
    &lt;td mat-cell *matCellDef=&quot;let element&quot;&gt; {{element.name}} &lt;/td&gt;
  &lt;/ng-container&gt;

I want my component to be implemented like that

&lt;tree-view [data]=&quot;data&quot;&gt;
    &lt;div tree-node *nodes=&quot;let node&quot;&gt; {{node.id}} &lt;span&gt;Yay now I can show custom stuff like name {{node.value.name}}&lt;/span&gt;&lt;/div&gt;
&lt;/tree-view&gt;

how can I achieve this ?

All I know about content projection is the &lt;ng-content&gt;, I know that I can use slots but I can only use that with single spots not repetitive content. any clue?
> NOTE: I'm using Angular 15

答案1

得分: 0

经过一些研究和深入挖掘Angular结构性指令后,我终于找到了答案。这里的关键是使用ngTemplateOutlet。下面是我所做的:

组件模板

<div class="tree-container">
  <div *ngFor="let node of nodes" class="tree-node">
  <!-- 这里可能还有其他内容 -->

  <ng-container *ngTemplateOutlet="content.template; context: {$implicit: node}"></ng-container>

  <!-- 这里可能还有更多其他内容 -->
  </div>
</div>

content是一个可以像下面这样创建的自定义指令

指令

@Directive({
  selector: "[nodes]"
})
export class NodesDirective {
  constructor(public template: TemplateRef<any>) {}
}

注意:确保在modules.ts文件中像其他组件一样声明它

组件类

在component.ts文件中添加以下属性

@ContentChild(NodesDirective) content!: NodesDirective;

就是这样。我们准备好了。

要实现这一点,可以像这样使用组件

<tree-view [data]="data">
    <div *nodes="let node">
       <!-- 在这里放入任何你想要的内容 -->
       <h1>{{node.id}}</h1>
    </div>
</tree-view>

希望这对其他人有帮助

英文:

After some research and digging deeper in Angular Structural Directives I finally found the answer myself. The trick here is to use ngTemplateOutlet. Here's what I had to do:

Component template

&lt;div class=&quot;tree-container&quot;&gt;
  &lt;div *ngFor=&quot;let node of nodes&quot; class=&quot;tree-node&quot;&gt;
  &lt;!-- maybe some other stuff here --&gt;

  &lt;ng-container *ngTemplateOutlet=&quot;content.template; context: {$implicit: node}&quot;&gt;&lt;/ng-container&gt;

  &lt;!-- maybe some more other stuff here --&gt;
  &lt;/div&gt;
&lt;/div&gt; 

content is a custom directive that can be created like the following

Directive

@Directive({
  selector: &quot;[nodes]&quot;
})
export class NodesDirective {
  constructor(public template:TemplateRef&lt;any&gt;) {}
}

> NOTE: make sure to declare it like any other component in the modules.ts file

Component Class

In the component.ts file add the following attribute

@ContentChild(NodesDirective) content!: NodesDirective;

And that's it. We're ready to go.

To implement this, the component can be used like this

&lt;tree-view [data]=&quot;data&quot;&gt;
    &lt;div *nodes=&quot;let node&quot;&gt;
       &lt;!-- put whatever you want here --&gt;
       &lt;h1&gt;{{node.id}}&lt;/h1&gt;
    &lt;/div&gt;
&lt;/tree-view&gt;

hope this helps someone else

huangapple
  • 本文由 发表于 2023年3月31日 23:22:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/75900207.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定