projects/cobbler-frontend/src/app/signatures/signatures.component.ts
OnInit
OnDestroy
selector | cobbler-signatures |
standalone | true |
imports |
MatTree
MatTreeNode
MatIcon
MatIconButton
MatTreeNodeToggle
MatTreeNodePadding
MatTreeNodeDef
MatTable
MatHeaderCell
MatCell
MatHeaderRow
MatRow
MatColumnDef
MatHeaderCellDef
MatCellDef
MatHeaderRowDef
MatRowDef
MatDivider
AsyncPipe
MatList
MatListItem
MatProgressSpinner
NgForOf
NgIf
|
templateUrl | ./signatures.component.html |
styleUrl | ./signatures.component.scss |
Properties |
|
Methods |
constructor(userService: UserService, cobblerApiService: CobblerApiService, _snackBar: MatSnackBar)
|
||||||||||||
Parameters :
|
generateSignatureUiData |
generateSignatureUiData()
|
Returns :
void
|
ngOnDestroy |
ngOnDestroy()
|
Returns :
void
|
ngOnInit |
ngOnInit()
|
Returns :
void
|
updateSignatures |
updateSignatures()
|
Returns :
void
|
Private _transformer |
Default value : () => {...}
|
dataSource |
Default value : new MatTreeFlatDataSource(this.treeControl, this.treeFlattener)
|
displayedColumns |
Default value : this.columns.map((c) => c.columnDef)
|
hasChild |
Default value : () => {...}
|
hasOsVersion |
Default value : () => {...}
|
Public isLoading |
Default value : true
|
Private ngUnsubscribe |
Default value : new Subject<void>()
|
treeControl |
Default value : new FlatTreeControl<OsBreedFlatNode>(
(node) => node.level,
(node) => node.expandable,
)
|
treeFlattener |
Default value : new MatTreeFlattener(
this._transformer,
(node) => node.level,
(node) => node.expandable,
(node) => node.children,
)
|
Public userService |
Type : UserService
|
import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { MatDivider } from '@angular/material/divider';
import { MatList, MatListItem } from '@angular/material/list';
import { MatProgressSpinner } from '@angular/material/progress-spinner';
import { MatSnackBar } from '@angular/material/snack-bar';
import {
MatCell,
MatCellDef,
MatColumnDef,
MatHeaderCell,
MatHeaderCellDef,
MatHeaderRow,
MatHeaderRowDef,
MatRow,
MatRowDef,
MatTable,
} from '@angular/material/table';
import { filter, repeat, take, takeUntil } from 'rxjs/operators';
import { UserService } from '../services/user.service';
import { CobblerApiService } from 'cobbler-api';
import {
MatTree,
MatTreeFlatDataSource,
MatTreeFlattener,
MatTreeNode,
MatTreeNodeDef,
MatTreeNodePadding,
MatTreeNodeToggle,
} from '@angular/material/tree';
import { FlatTreeControl } from '@angular/cdk/tree';
import { MatIcon } from '@angular/material/icon';
import { MatIconButton } from '@angular/material/button';
import { Subject } from 'rxjs';
import Utils from '../utils';
interface TableRow {
key: string;
value: any;
}
/**
* Food data with nested structure.
* Each node has a name and an optional list of children.
*/
interface OsNode {
data: string | Array<TableRow>;
children?: OsNode[];
}
/** Flat node with expandable and level information */
interface OsBreedFlatNode {
expandable: boolean;
data: string | Array<TableRow>;
level: number;
}
@Component({
selector: 'cobbler-signatures',
standalone: true,
imports: [
MatTree,
MatTreeNode,
MatIcon,
MatIconButton,
MatTreeNodeToggle,
MatTreeNodePadding,
MatTreeNodeDef,
MatTable,
MatHeaderCell,
MatCell,
MatHeaderRow,
MatRow,
MatColumnDef,
MatHeaderCellDef,
MatCellDef,
MatHeaderRowDef,
MatRowDef,
MatDivider,
AsyncPipe,
MatList,
MatListItem,
MatProgressSpinner,
NgForOf,
NgIf,
],
templateUrl: './signatures.component.html',
styleUrl: './signatures.component.scss',
})
export class SignaturesComponent implements OnInit, OnDestroy {
// Unsubscribe
private ngUnsubscribe = new Subject<void>();
// Table
columns = [
{
columnDef: 'key',
header: 'Attribute',
cell: (element: TableRow) => `${element.key}`,
},
{
columnDef: 'value',
header: 'Value',
cell: (element: TableRow) => `${element.value}`,
},
];
displayedColumns = this.columns.map((c) => c.columnDef);
// Tree
private _transformer = (node: OsNode, level: number) => {
return {
expandable: !!node.children && node.children.length > 0,
data: node.data,
level: level,
};
};
treeControl = new FlatTreeControl<OsBreedFlatNode>(
(node) => node.level,
(node) => node.expandable,
);
treeFlattener = new MatTreeFlattener(
this._transformer,
(node) => node.level,
(node) => node.expandable,
(node) => node.children,
);
dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);
// Spinner
public isLoading = true;
constructor(
public userService: UserService,
private cobblerApiService: CobblerApiService,
private _snackBar: MatSnackBar,
) {}
ngOnInit(): void {
this.generateSignatureUiData();
}
ngOnDestroy(): void {
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
}
hasChild = (_: number, node: OsBreedFlatNode) => node.expandable;
hasOsVersion = (_: number, node: OsBreedFlatNode) =>
typeof node.data !== 'string';
generateSignatureUiData(): void {
this.cobblerApiService
.get_signatures(this.userService.token)
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe(
(value) => {
const newData: Array<OsNode> = [];
for (const k in value.breeds) {
const children: Array<OsNode> = [];
for (const j in value.breeds[k]) {
const osVersionData: Array<TableRow> = [];
for (const i in value.breeds[k][j]) {
osVersionData.push({ key: i, value: value.breeds[k][j][i] });
}
children.push({
data: j,
children: [{ data: osVersionData, children: [] }],
});
}
newData.push({ data: k, children: children });
}
this.dataSource.data = newData;
this.isLoading = false;
},
(error) => {
// HTML encode the error message since it originates from XML
this._snackBar.open(Utils.toHTML(error.message), 'Close');
},
);
}
updateSignatures(): void {
this.isLoading = true;
this.cobblerApiService
.background_signature_update(this.userService.token)
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe(
(value) => {
this.cobblerApiService
.get_task_status(value)
.pipe(
repeat(),
filter(
(data) => data.state === 'failed' || data.state === 'complete',
),
take(1),
)
.subscribe((value1) => {
this.isLoading = false;
this.generateSignatureUiData();
});
},
(error) => {
// HTML encode the error message since it originates from XML
this._snackBar.open(Utils.toHTML(error.message), 'Close');
},
);
}
}
<div class="title-table">
<div class="title-row">
<h1 class="title title-cell-text">Signatures</h1>
<span class="title-cell-button">
<button mat-icon-button (click)="this.updateSignatures()">
<mat-icon>refresh</mat-icon>
</button>
</span>
</div>
</div>
<mat-spinner *ngIf="isLoading" style="margin: 0 auto"></mat-spinner>
<ng-container *ngIf="!isLoading">
<mat-tree [dataSource]="dataSource" [treeControl]="treeControl">
<!-- This is the tree node template for leaf nodes -->
<mat-tree-node *matTreeNodeDef="let node" matTreeNodePadding>
<!-- use a disabled button to provide padding for tree leaf -->
<button mat-icon-button disabled></button>
{{ node.data }}
</mat-tree-node>
<!-- This is the tree node template for expandable nodes -->
<mat-tree-node
*matTreeNodeDef="let node; when: hasChild"
matTreeNodePadding
>
<button
mat-icon-button
matTreeNodeToggle
[attr.aria-label]="'Toggle ' + node.data"
>
<mat-icon class="mat-icon-rtl-mirror">
{{ treeControl.isExpanded(node) ? "expand_more" : "chevron_right" }}
</mat-icon>
</button>
{{ node.data }}
</mat-tree-node>
<!-- This is the tree node template for expandable nodes with an OS version as data -->
<mat-tree-node
*matTreeNodeDef="let node; when: hasOsVersion"
matTreeNodePadding
>
<button mat-icon-button disabled></button>
<table mat-table [dataSource]="node.data" class="mat-elevation-z8">
@for (column of columns; track column) {
<ng-container [matColumnDef]="column.columnDef">
<th mat-header-cell *matHeaderCellDef>
{{ column.header }}
</th>
<td mat-cell *matCellDef="let row">
{{ column.cell(row) }}
</td>
</ng-container>
}
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
</mat-tree-node>
</mat-tree>
</ng-container>