Skip to content

Instantly share code, notes, and snippets.

@devtronic
Created June 7, 2020 12:08
Show Gist options
  • Save devtronic/807e8bfc712330ef13a5c9b8bf5a71cf to your computer and use it in GitHub Desktop.
Save devtronic/807e8bfc712330ef13a5c9b8bf5a71cf to your computer and use it in GitHub Desktop.
Form builder tab group example
// tab-group.type.ts
import {TabGroupOptions} from './tab-group.options';
import {AbstractLayoutType, Constructor, FormModel} from '@mintware-de/form-builder';
import {TabGroupComponent} from './tab-group.component';
export class TabGroup<T extends FormModel = FormModel> extends AbstractLayoutType<TabGroupOptions<any>> {
public readonly component: Constructor = TabGroupComponent;
constructor(options: TabGroupOptions<T>) {
super(options);
}
}
// tab-group.component.ts
import {AfterContentInit, Component, OnDestroy} from '@angular/core';
import {AbstractControl} from '@angular/forms';
import {TabGroupOptions} from './tab-group.options';
import {AbstractLayoutComponent, AbstractLayoutType, IGroupTypeOptions} from '@mintware-de/form-builder';
import {Subscription} from 'rxjs';
@Component({
selector: 'app-tab-group',
template: `
<mat-tab-group>
<mat-tab *ngFor="let tab of mwFieldType.options.tabs; index as tabIndex">
<ng-template mat-tab-label>
<i style="margin-right: 8px" class="fas fa-fw"></i>
{{tab.caption }}
<i style="margin-left: 8px; color: red"
class="fas fa-fw"
[ngClass]="{'fa-exclamation-circle': tabsWithWarning[tabIndex] }"></i>
</ng-template>
<mat-card-content>
<ng-container *ngFor="let controlName of tab.controls"
mwFormSlot
[mwFieldName]="controlName"></ng-container>
</mat-card-content>
</mat-tab>
</mat-tab-group>
`,
})
export class TabGroupComponent extends AbstractLayoutComponent<AbstractLayoutType<TabGroupOptions<any>>>
implements AfterContentInit, OnDestroy {
public tabsWithWarning: boolean[] = [];
private subscriptions: Subscription[] = [];
public ngAfterContentInit(): void {
super.ngAfterContentInit();
const realControlsPerTab: AbstractControl[][] = [];
this.mwFieldType.options.tabs.forEach((tab, index) => {
realControlsPerTab[index] = [];
tab.controls.forEach((ctrlName: string) => {
this.nestedElements(ctrlName).forEach((path) => {
const el = this.mwFormGroup.get(path);
if (el) {
realControlsPerTab[index].push(el);
}
});
});
});
realControlsPerTab.forEach((controls, index) => {
const updateFunction = () => {
this.mwFormGroup.updateValueAndValidity();
this.tabsWithWarning[index] = controls.reduce((valid, ctrl) => valid || ctrl.invalid, false);
};
this.subscriptions.push(...controls.map((ctrl) => ctrl.statusChanges.subscribe(() => {
updateFunction();
})));
});
}
public ngOnDestroy(): void {
this.subscriptions.forEach((s) => s.unsubscribe());
}
private nestedElements(fieldName: string): string[] {
if (!(this.mwFieldType.options.model[fieldName] instanceof AbstractLayoutType)) {
return [this.fieldPaths[fieldName]];
}
const childrenToCheck: Array<{ path: string, layout: AbstractLayoutType<IGroupTypeOptions> }> = [
{path: this.fieldPaths[fieldName], layout: this.mwFieldType.options.model[fieldName]},
];
const allElements: string[] = [];
do {
// @ts-ignore
const {path, layout} = childrenToCheck.shift();
Object.keys(layout.options.model).forEach((k) => {
if (layout.options.model[k] instanceof AbstractLayoutType) {
childrenToCheck.push({path, layout: layout.options.model[k]});
} else {
const childPath = [path, k].filter((x) => x).join('.');
if (childPath !== '') {
allElements.push(childPath);
}
}
});
} while (childrenToCheck.length > 0);
return allElements;
}
}
// tab-group.options.ts
import {FormModel, IGroupTypeOptions} from '@mintware-de/form-builder';
export interface TabGroupOptions<T extends FormModel> extends IGroupTypeOptions {
model: T;
readonly tabs: Array<{ caption: string, controls: Array<keyof T>}>;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment