如何为 tabPanel 定义静态索引

How to define static index for tabPanel

我正在使用 PrimeNG tabView 组件来显示步骤,假设我有 3 个步骤 [1,2,3],并且我正在使用 handleTabViewChange(e) 使用步骤的索引加载数据

对应的索引是[0,1,2],第2步有条件显示,不显示时我有2步[1 ,3] with index [0,1], 所以第3步的index变为[1] :

有没有办法为每个选项卡设置一个静态索引,所以当第2步不显示时,第3步的索引保持不变[2]:

<p-tabView (onChange)="handleTabViewChange($event)">
    <p-tabPanel header="Step 1" >

    </p-tabPanel>
    <p-tabPanel header="Step 2" *ngIf="false" >
        
    </p-tabPanel>
    <p-tabPanel header="Step 3" >
        
    </p-tabPanel>
</p-tabView>

handleTabViewChange(e):

  data: any[];

  handleTabViewChange(e) {    
    var step= e.index + 1;
    this.data = this.loadData(step);
  }

您可以通过使用向面板添加静态 index 属性 的指令,并使用 ViewChildren 枚举呈现的面板来执行此操作:

指令:

@Directive({
  selector: 'p-tabPanel'
})
export class MyTabPanelDirective {
  @Input() index: number;

  constructor() {}
}

模板中的选项卡视图应如下所示:

<p-tabView (onChange)="handleTabViewChange($event)">
    <p-tabPanel header="Step 1" [index]="0">

    </p-tabPanel>
    <p-tabPanel header="Step 2" [index]="1" *ngIf="false" >
        
    </p-tabPanel>
    <p-tabPanel header="Step 3" [index]="2" >
        
    </p-tabPanel>
</p-tabView>

最后您的组件可以像这样处理 onChange

  @ViewChildren(MyTabPanelDirective) panels: QueryList<MyTabPanelDirective>;

  handleTabViewChange(e) {
    const step = this.panels.get(e.index + 1).index;
    this.data = this.loadData(step);
  }

它的工作方式是事件参数包括 DOM 中面板的索引,这与视图子查询列表相匹配(有一个警告,见下文)。然后在事件处理程序中,我们获取 DOM 索引,加 1,并从指令中提取静态索引。

注意事项 1 如果您在代码中操作选项卡面板,通过添加、删除或重新排序,则查询列表顺序可能与 DOM 顺序不匹配。

注意事项 2 如果您在同一个模板中有两个选项卡视图,则必须稍作更改。

警告 #2 的解决方案

多个选项卡视图

使用选项卡视图提供的服务(因此每个选项卡视图都有自己的实例)- 并将其注入面板。然后每个面板将自己注册到该服务,这将根据 DOM 的顺序进行(至少在最初,请参见警告 #1)。

然后在事件处理程序中,通过索引查询面板视图的选项卡服务。

服务:

@Injectable({ providedIn: 'root' })
export class TabRepoService {
  panels: MyTabPanelDirective[] = [];

  constructor() {}

  addPanel(panel: MyTabPanelDirective) {
    this.panels.push(panel);
  }
}

选项卡视图指令附加到所有选项卡视图并为其自身及其子项(面板)创建服务实例:

@Directive({
  selector: 'p-tabView',
  exportAs: 'myTabView',

  // This means there will be a separate instance per tab view
  providers: [TabRepoService] 
})
export class MyTabViewDirective {
  constructor(public repo: TabRepoService) {}

  getPanelAtIndex(i: number) {
    return this.repo.panels[i];
  }
}

接下来,修改面板指令,使用此服务来注册自己:

@Directive({
  selector: 'p-tabPanel',
})
export class MyTabPanelDirective {
  @Input() index: number;

  constructor(private repo: TabRepoService) {
    repo.addPanel(this);
  }
}

html用法:

<p-tabView #tab1="myTabView" (onChange)="handleTabViewChange($event, tab1)">
  <p-tabPanel header="Header 1" [index]="0">
    Content 1
  </p-tabPanel>
  <p-tabPanel header="Header 2" [index]="1" *ngIf="false">
    Content 2
  </p-tabPanel>
  <p-tabPanel header="Header 3" [index]="2">
    Content 3
  </p-tabPanel>
</p-tabView>

请注意,我们将选项卡视图指令传递给事件处理程序,以便它可以通过索引查询它的子项。

最后是事件处理程序:

  handleTabViewChange(e, t: MyTabViewDirective) {
    const step = t.getPanelAtIndex(e.index).index;
    this.data = this.loadData(step);
  }

注意:在此解决方案中 ViewChildren 不是必需的。

例子StackBlitz