Add reference table
parent
80771eded3
commit
260e105504
@ -0,0 +1,93 @@
|
||||
<div class="table-page-wrapper">
|
||||
<div class="table-control">
|
||||
<form [formGroup]="addForm">
|
||||
<mat-form-field>
|
||||
<input
|
||||
type="text"
|
||||
matInput
|
||||
placeholder="Element hinzufügen"
|
||||
formControlName="addGroup"
|
||||
[matAutocomplete]="autoGroup"
|
||||
/>
|
||||
<mat-autocomplete #autoGroup="matAutocomplete">
|
||||
<mat-option
|
||||
*ngFor="let element of possibleValueOptions"
|
||||
[value]="nameToShowInSelection(element)"
|
||||
(click)="addReference(element)"
|
||||
>
|
||||
{{ nameToShowInSelection(element) }}
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
||||
</form>
|
||||
<mat-form-field class="filter">
|
||||
<mat-label>Tabelle filtern</mat-label>
|
||||
<input
|
||||
matInput
|
||||
(input)="applyTableFilter()"
|
||||
[(ngModel)]="tableFilterString"
|
||||
placeholder="Suchbegriff eingeben..."
|
||||
/>
|
||||
</mat-form-field>
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="[15, 25]"
|
||||
showFirstLastButtons
|
||||
></mat-paginator>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<table mat-table class="mat-elevation-z8" matSort [dataSource]="dataSource">
|
||||
<!--- Note that these columns can be defined in any order.
|
||||
The actual rendered columns are set as a property on the row definition" -->
|
||||
|
||||
<!-- Other Columns -->
|
||||
<ng-container
|
||||
*ngFor="let column of columnInfo"
|
||||
[matColumnDef]="column.name"
|
||||
[sticky]="isStickyColumn(column.name)"
|
||||
>
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>
|
||||
{{ column.translation || column.name }}
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<app-cell
|
||||
*ngIf="column.type === 'Boolean'; else stringValue"
|
||||
[editable]="false"
|
||||
[(value)]="element[column.name]"
|
||||
[inputType]="column.type"
|
||||
></app-cell>
|
||||
<ng-template #stringValue>
|
||||
<span *ngIf="!column.link">{{ element[column.name] }}</span>
|
||||
<a
|
||||
mat-button
|
||||
color="primary"
|
||||
*ngIf="column.link"
|
||||
[routerLink]="column.link(element)"
|
||||
>{{ element[column.name] }}</a
|
||||
>
|
||||
</ng-template>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Buttons Column -->
|
||||
<ng-container matColumnDef="buttons" stickyEnd>
|
||||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<div class="button-wrapper">
|
||||
<button
|
||||
mat-icon-button
|
||||
matTooltip="Entfernen"
|
||||
(click)="delete(element)"
|
||||
[disabled]="!editable"
|
||||
>
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Table Definition -->
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,60 @@
|
||||
.table-page-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
.headline {
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
.table-control {
|
||||
margin: 0.5em;
|
||||
flex: none;
|
||||
.table-control-button {
|
||||
margin: 0.25em;
|
||||
}
|
||||
.filter {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
.mat-paginator {
|
||||
display: inline-block;
|
||||
width: 50em;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
}
|
||||
.table-container {
|
||||
flex: 1;
|
||||
width: auto;
|
||||
margin-left: 0.5em;
|
||||
margin-right: 0.5em;
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
table {
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
.mat-header-cell,
|
||||
.mat-footer-cell,
|
||||
.mat-cell {
|
||||
min-width: 3em;
|
||||
box-sizing: border-box;
|
||||
padding: 0 0.25em;
|
||||
::ng-deep.mat-form-field-infix {
|
||||
width: auto !important;
|
||||
min-width: 50px !important;
|
||||
}
|
||||
}
|
||||
::ng-deep.mat-form-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mat-table-sticky {
|
||||
filter: brightness(90%);
|
||||
//opacity: 1;
|
||||
}
|
||||
|
||||
.button-wrapper {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,189 @@
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
Output,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { flatten } from 'src/app/helperFunctions/flattenObject';
|
||||
import { SchemaService } from 'src/app/services/schema.service';
|
||||
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { MatPaginator } from '@angular/material/paginator';
|
||||
import { MatSort } from '@angular/material/sort';
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'app-reference-table',
|
||||
templateUrl: './reference-table.component.html',
|
||||
styleUrls: ['./reference-table.component.scss'],
|
||||
})
|
||||
export class ReferenceTableComponent {
|
||||
/** this array defines the columns and translations of the table and the order they are displayed */
|
||||
@Input()
|
||||
columnInfo: {
|
||||
name: string;
|
||||
translation: string;
|
||||
sticky?: boolean;
|
||||
type?: string;
|
||||
link?: (row: any) => string;
|
||||
}[] = [];
|
||||
|
||||
@Input()
|
||||
dataServiceThatProvidesThePossibleData: any;
|
||||
@Input()
|
||||
nameToShowInSelection: any;
|
||||
@Input()
|
||||
set editable(value: boolean) {
|
||||
this._editable = value;
|
||||
value
|
||||
? this.addForm.get('addGroup').enable()
|
||||
: this.addForm.get('addGroup').disable();
|
||||
}
|
||||
get editable() {
|
||||
return this._editable;
|
||||
}
|
||||
_editable: boolean = false;
|
||||
|
||||
@Input()
|
||||
set data(newdata) {
|
||||
if (!newdata) return;
|
||||
this.dataSource.data = [];
|
||||
for (const element of newdata) {
|
||||
this.dataSource.data.push(flatten(element));
|
||||
}
|
||||
this.dataSource.data = this.dataSource.data;
|
||||
this.onReferenceChange();
|
||||
}
|
||||
get data(): any {
|
||||
return this.dataSource.data;
|
||||
}
|
||||
|
||||
@Input()
|
||||
tableDataGQLType: string;
|
||||
|
||||
@Output() referenceIds = new EventEmitter();
|
||||
|
||||
@ViewChild(MatPaginator) paginator: MatPaginator;
|
||||
@ViewChild(MatSort) sort: MatSort;
|
||||
|
||||
displayedColumns: string[] = [];
|
||||
|
||||
/** data source of the table */
|
||||
dataSource: MatTableDataSource<any> = new MatTableDataSource([]);
|
||||
possibleValues: Array<any> = [];
|
||||
possibleValueOptions: Array<any> = [];
|
||||
|
||||
reloadingTable = false;
|
||||
|
||||
addForm: FormGroup = new FormGroup({ addGroup: new FormControl() });
|
||||
|
||||
tableFilterString: string = '';
|
||||
|
||||
constructor(private schemaService: SchemaService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.addColumnPropertiesFromGQLSchemaToColumnInfo();
|
||||
this.columnInfo.forEach((column) => {
|
||||
this.displayedColumns.push(column.name);
|
||||
});
|
||||
this.displayedColumns.push('buttons');
|
||||
|
||||
this.addForm
|
||||
.get('addGroup')
|
||||
.valueChanges.subscribe(() => this.filterPossibleValueOptions());
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.dataSource.paginator = this.paginator;
|
||||
this.dataSource.sort = this.sort;
|
||||
|
||||
this.dataSource.sortingDataAccessor = (item, columnName) => {
|
||||
if (typeof item[columnName] === 'string') {
|
||||
return item[columnName].toLocaleLowerCase();
|
||||
}
|
||||
return item[columnName];
|
||||
};
|
||||
|
||||
this.dataServiceThatProvidesThePossibleData.tableData.subscribe((data) => {
|
||||
this.possibleValues = [];
|
||||
if (data) {
|
||||
for (const row of data) {
|
||||
this.possibleValues.push(flatten(row));
|
||||
}
|
||||
}
|
||||
this.filterPossibleValueOptions();
|
||||
});
|
||||
this.dataServiceThatProvidesThePossibleData.loadTableData();
|
||||
}
|
||||
|
||||
addColumnPropertiesFromGQLSchemaToColumnInfo() {
|
||||
for (const column of this.columnInfo) {
|
||||
const typeInformation = this.schemaService.getTypeInformation(
|
||||
this.tableDataGQLType,
|
||||
column.name
|
||||
);
|
||||
column.type = column.type || typeInformation.type;
|
||||
}
|
||||
}
|
||||
|
||||
isStickyColumn(propertyName: string) {
|
||||
return (
|
||||
this.columnInfo.find((column) => column.name === propertyName)?.sticky ||
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
delete(row: any) {
|
||||
const index = this.dataSource.data.findIndex(
|
||||
(element) => element.id === row.id
|
||||
);
|
||||
if (index === -1) return;
|
||||
this.dataSource.data.splice(index, 1);
|
||||
this.dataSource.data = this.dataSource.data; //needed to trigger update lol
|
||||
this.filterPossibleValueOptions();
|
||||
this.onReferenceChange();
|
||||
}
|
||||
|
||||
addReference(row: any) {
|
||||
this.addForm.get('addGroup').reset();
|
||||
this.dataSource.data = [row, ...this.dataSource.data];
|
||||
|
||||
this.tableFilterString = '';
|
||||
this.applyTableFilter();
|
||||
this.filterPossibleValueOptions();
|
||||
this.onReferenceChange();
|
||||
}
|
||||
|
||||
getRowById(id: string) {
|
||||
return this.dataSource.data.find((row) => row.id === id);
|
||||
}
|
||||
|
||||
filterPossibleValueOptions() {
|
||||
this.possibleValueOptions = this.possibleValues.filter(
|
||||
(element) => !this.dataSource.data.find((row) => row.id === element.id)
|
||||
);
|
||||
let searchString = this.addForm.get('addGroup').value;
|
||||
if (!searchString) {
|
||||
return;
|
||||
}
|
||||
searchString = searchString.toLocaleLowerCase();
|
||||
this.possibleValueOptions = this.possibleValueOptions.filter((element) =>
|
||||
this.nameToShowInSelection(element)
|
||||
.toLocaleLowerCase()
|
||||
.includes(searchString)
|
||||
);
|
||||
}
|
||||
|
||||
applyTableFilter() {
|
||||
this.dataSource.filter = this.tableFilterString;
|
||||
}
|
||||
|
||||
onReferenceChange() {
|
||||
const ids = [];
|
||||
for (const element of this.data) {
|
||||
ids.push(element.id);
|
||||
}
|
||||
this.referenceIds.emit(ids);
|
||||
}
|
||||
}
|
@ -1,21 +1,24 @@
|
||||
export function deepen(object: Object): any {
|
||||
let newObject = {};
|
||||
for (const prop in object) {
|
||||
if (prop.includes('.')) {
|
||||
const splittedProp = prop.split('.');
|
||||
const outerProp = splittedProp[0];
|
||||
const innerProp = splittedProp.slice(1).join('.');
|
||||
if (!newObject[outerProp]) {
|
||||
newObject[outerProp] = {};
|
||||
}
|
||||
newObject[outerProp][innerProp] = object[prop];
|
||||
} else {
|
||||
newObject[prop] = object[prop];
|
||||
let newObject;
|
||||
if (Array.isArray(object)) {
|
||||
newObject = new Array();
|
||||
} else newObject = new Object();
|
||||
for (const prop in object) {
|
||||
if (prop.includes('.')) {
|
||||
const splittedProp = prop.split('.');
|
||||
const outerProp = splittedProp[0];
|
||||
const innerProp = splittedProp.slice(1).join('.');
|
||||
if (!newObject[outerProp]) {
|
||||
newObject[outerProp] = {};
|
||||
}
|
||||
newObject[outerProp][innerProp] = object[prop];
|
||||
} else {
|
||||
newObject[prop] = object[prop];
|
||||
}
|
||||
for (const prop in newObject) {
|
||||
if (typeof newObject[prop] === 'object' && newObject[prop] !== null)
|
||||
newObject[prop] = deepen(newObject[prop]);
|
||||
}
|
||||
return newObject;
|
||||
}
|
||||
for (const prop in newObject) {
|
||||
if (typeof newObject[prop] === 'object' && newObject[prop] !== null)
|
||||
newObject[prop] = deepen(newObject[prop]);
|
||||
}
|
||||
return newObject;
|
||||
}
|
||||
|
@ -1,16 +1,20 @@
|
||||
export function flatten(object: Object, prefix: string = ''): any {
|
||||
let newObject = {};
|
||||
for (const prop in object) {
|
||||
let propName = prefix + prop;
|
||||
if (typeof object[prop] === 'object' && object[prop] !== null) {
|
||||
const flattenedObject = flatten(object[prop], propName + '.');
|
||||
for (const flattenedProperty in flattenedObject) {
|
||||
newObject[flattenedProperty] =
|
||||
flattenedObject[flattenedProperty];
|
||||
}
|
||||
} else {
|
||||
newObject[propName] = object[prop];
|
||||
let newObject = {};
|
||||
for (const prop in object) {
|
||||
let propName = prefix + prop;
|
||||
if (Array.isArray(object[prop])) {
|
||||
newObject[propName] = [];
|
||||
for (const arrayElement of object[prop]) {
|
||||
newObject[propName].push(flatten(arrayElement));
|
||||
}
|
||||
} else if (typeof object[prop] === 'object' && object[prop] !== null) {
|
||||
const flattenedObject = flatten(object[prop], propName + '.');
|
||||
for (const flattenedProperty in flattenedObject) {
|
||||
newObject[flattenedProperty] = flattenedObject[flattenedProperty];
|
||||
}
|
||||
} else {
|
||||
newObject[propName] = object[prop];
|
||||
}
|
||||
return newObject;
|
||||
}
|
||||
return newObject;
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TableOverviewComponent } from './table-overview.component';
|
||||
|
||||
describe('TableOverviewComponent', () => {
|
||||
let component: TableOverviewComponent;
|
||||
let fixture: ComponentFixture<TableOverviewComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ TableOverviewComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TableOverviewComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue