Hi There
I'm trying to change the height of a grouped row header without success. I've tried to follow your guide here:
https://www.bryntum.com/products/grid/docs/api/Grid/feature/Group
No matter what values I put in for Size.Height no change is made to the row height:
Also when I collapse a header row, one cell has its contents disapear but the child rows do not collapse:
I'm sure I'm doing something wrong. Please can someone assist?
This is the HTML for my component:
<div style = "height:100%">
<div class="flex-container">
<bryntum-grid
#mainGrid
[data] = "personData"
[tbar]= "mainGridConfig.tbar!"
[columns] = "mainGridConfig.columns!"
[groupFeature]= "mainGridConfig.features!.group!"
[groupSummaryFeature]="mainGridConfig.features!.groupSummary!"
></bryntum-grid>
</div>
</div>
The Config:
import { group } from "@angular/animations";
import { GridConfig, Group, SplitterConfig, SubGrid, SubGridConfig } from "@bryntum/grid";
import { Person } from "src/PutneyBankRESTAPI/Person";
export const mainGridConfig: Partial<GridConfig> = {
columns : [
{ field : 'firstName', text : 'First Name', width : 100, readOnly : true },
{ field : 'lastName', text : 'Last Name', width : 100, readOnly : true },
{ field : 'adjustedContractedHours', text : 'Contracted Hours', width : 100, readOnly : true, sum : 'sum' },
{ field : 'totalHours', text : 'Total Hours', width : 100, readOnly : true, sum : 'sum' }
],
tbar : [
{
type : 'combo',
ref : 'wardCombo',
label : 'Ward',
displayField : 'name',
value : 'all',
name : 'ward',
editable : false
},
{
type : 'combo',
ref : 'templateCombo',
label : 'Template',
displayField : 'name',
value : 'all',
name : 'template',
editable : false
},
{
type : 'button',
ref : 'saveButton',
icon : 'b-fa-disk',
text : 'Save'
},
{
type : 'button',
ref : 'generateButton',
icon : 'b-fa-plus',
text : 'Generate'
}],
features : {
group :
{
field: 'jobType.name',
groupSortFn : (a : Person, b : Person) =>
{
a.jobType.orderNumber < b.jobType.orderNumber ? -1 : 1
},
renderer(data: { value : String; record : any; size : any }) : String
{
data.size.height = 300;
return data.value;
}
},
groupSummary :{
collapseToHeader : true,
target : 'header'
}
}
};
And the component itself:
import { Component, OnInit, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { BryntumGridComponent } from '@bryntum/schedulerpro-angular'
import { Column, Combo, Grid, TextField, ColumnStore, Store, Field, Button, SummaryConfig } from '@bryntum/schedulerpro';
import { empty, forkJoin, Observable, Subscription } from 'rxjs';
import { ILocation } from 'src/PutneyBankRESTAPI/Location';
import { LocationDataService } from 'src/PutneyBankRESTAPI/Location.Service';
import { IPerson, Person } from 'src/PutneyBankRESTAPI/Person';
import { PersonService } from 'src/PutneyBankRESTAPI/Person.Service';
import { mainGridConfig } from './app.config';
import { GridWidgetMap } from './gridwidgetmap';
import { StaffingRequirementTemplateService } from 'src/PutneyBankRESTAPI/StaffingRequirementTemplate.Service';
import { IStaffingRequirementTemplate } from 'src/PutneyBankRESTAPI/StaffingRequirementTemplate';
import { IStaffingShiftTemplate } from 'src/PutneyBankRESTAPI/StaffingLocationShiftTemplate';
import { StaffingLocationShiftTemplateService } from 'src/PutneyBankRESTAPI/StaffingLocationShiftTemplate.Service';
import { IStaffingRequirementTemplateAllocation } from 'src/PutneyBankRESTAPI/StaffingRequirementTemplateAllocation';
import { StaffingRequirementTemplateAllocationService } from 'src/PutneyBankRESTAPI/StaffingRequirementTemplateAllocation.Service';
import { MetaData } from 'src/PutneyBankRESTAPI/MetaData';
import { ActivatedRoute, Router } from '@angular/router';
import { IStaffingRequirementBaseline } from 'src/PutneyBankRESTAPI/StaffingRequirementBaseline';
import { StaffingRequirementBaselineService } from 'src/PutneyBankRESTAPI/StaffingRequirementBaseline.Service';
import { ColumnSummaryConfig, GridRowModel, Summary } from '@bryntum/grid';
import { ExtendedColumn } from 'src/ExtendedBryntumClasses/Column';
@Component({
selector: 'st-roster-template',
templateUrl: './roster-template.component.html',
styleUrls: ['./roster-template.component.scss', './grid.classic.scss']
})
export class RosterTemplateComponent implements OnInit {
@ViewChild('mainGrid') mainGridComponent!: BryntumGridComponent;
mainGridConfig = mainGridConfig;
//private router: Router;
//grid!: Grid;
locationId! : number;
templateId! : number;
mainGrid! : Grid;
personDataSub! : Subscription;
personDataObservable! : Observable<Person[]>;
errorMessage : string = "";
summaryData! : IStaffingRequirementBaseline[];
personData! : Person[];
personDataStore! : Store;
templateDataSub! : Subscription;
templateData! : IStaffingRequirementTemplate;
locationDataSub! : Subscription;
locationData = {} as ILocation[];
gridWidgetMap! : GridWidgetMap;
staffingRequirementTemplateSub! : Subscription;
staffingRequirementTemplateData! : IStaffingRequirementTemplate[];
staffingLocationShiftTemplateSub! : Subscription;
staffingLocationShiftTemplateData! : IStaffingShiftTemplate[];
combinedCallObservable : Observable<[Person[], IStaffingRequirementTemplateAllocation[]]>;
staffingRequirementBaselineSub : Subscription;
staffingRequirementBaselineData! : IStaffingRequirementBaseline[];
staffingRequirementBaselineDataStore! : Store;
staffingRequirementBaselineObservable! : Observable<IStaffingRequirementBaseline[]>;
shiftTemplateStore! : Store;
templateAllocationData! : IStaffingRequirementTemplateAllocation[];
templateAllocationDataSub! : Subscription;
templateAllocationObservable! : Observable<IStaffingRequirementTemplateAllocation[]>;
constructor(private personDataService : PersonService,
private locationDataService : LocationDataService,
private staffingRequirementTemplateDataService : StaffingRequirementTemplateService,
private staffingLocationShiftTemplateService : StaffingLocationShiftTemplateService,
private staffingRequirementTemplateAllocationDataService : StaffingRequirementTemplateAllocationService,
private staffingRequirementBaselineService : StaffingRequirementBaselineService,
public editor : MatDialog,
private router : Router,
private route : ActivatedRoute) {
}
ngOnInit(): void
{
this.updateLocations();
}
ngAfterViewInit(): void
{
this.mainGrid = this.mainGridComponent.instance;
this.gridWidgetMap = new GridWidgetMap();
this.gridWidgetMap.wardCombo = this.mainGrid.widgetMap["wardCombo"] as Combo;
this.gridWidgetMap.templateCombo = this.mainGrid.widgetMap["templateCombo"] as Combo;
this.gridWidgetMap.saveButton = this.mainGrid.widgetMap["saveButton"] as Button;
this.gridWidgetMap.generateButton = this.mainGrid.widgetMap["generateButton"] as Button;
this.shiftTemplateStore = new Store();
this.personDataStore = this.mainGrid.store as Store;
this.gridWidgetMap.wardCombo.onChange = (event : any) => {
console.log(event.value.data.name);
this.locationId = event.value.data.locationId;
if(this.locationId != null)
{
this.updateStaffingRequirementTemplates(this.locationId);
this.updateStaffingShiftTemplates(this.locationId);
this.refreshStaffingRequirementBaselineData (this.locationId);
}
};
this.gridWidgetMap.saveButton.onClick = (event : any) => {
this.saveToDatabase();
};
this.gridWidgetMap.generateButton.onClick = (event : any) => {
this.generatePotentialRoster();
}
this.gridWidgetMap.templateCombo.onChange = (event : any) => {
console.log(event.value.data.name);
this.templateId = event.value.data.staffingRequirementTemplateId;
if(this.templateId != null)
{
this.templateDataSub = this.staffingRequirementTemplateDataService.getStaffingRequirementTemplate(this.templateId).subscribe({
next: templateDataDownload => {
this.templateData = templateDataDownload;
console.log("Template Data receieved");
var colStore : ColumnStore = this.mainGrid.columns as ColumnStore;
var prefixColumnCount : number = 4;
this.UpdateMainGridForCorrectNumberOfWeeks(this.templateData.numberOfWeeks, colStore, prefixColumnCount);
},
error: err =>
{
console.log(err);
this.errorMessage = err;
}
});
this.refreshGrid(this.locationId, this.templateId);
}
}
}
generatePotentialRoster()
{
this.router.navigate(['rosterpotential'], { queryParams: { templateId: this.templateId } });
}
saveToDatabase()
{
this.mainGrid.maskBody("Saving Data");
console.log("Save Pressed");
var listOfAllocations : IStaffingRequirementTemplateAllocation[] = [] as IStaffingRequirementTemplateAllocation[];
this.personDataStore.forEach(p =>
{
p.staffingRequirementTemplateAllocations.forEach(ad =>
{
const srta : IStaffingRequirementTemplateAllocation =
{
staffingRequirementTemplateAllocationId : ad.staffingRequirementTemplateAllocationId,
jobTypeId : ad.jobTypeId,
staffingShiftTemplateId : ad.staffingShiftTemplateId,
personId : ad.personId,
staffingRequirementTemplateId : ad.staffingRequirementTemplateId,
dayNumber : ad.dayNumber,
jobType : null,
staffingShiftTemplate : null,
person : null
};
listOfAllocations.push(srta);
});
});
this.staffingRequirementTemplateAllocationDataService.updateListOfAllocations(listOfAllocations, this.templateId);
//console.log(listOfAllocations);
window.alert("Template Saved");
window.location.reload();
this.mainGrid.unmaskBody();
}
onDropDownEditEnds(context : any)
{
var newId = context.value;
var p : any = context.record;
var column = context.editorContext.column;
p.staffingRequirementTemplateAllocations[(column.parentIndex - 4)].staffingShiftTemplateId = newId;
let pObject : Person = Object.assign(new Person(), p.data);
p.totalHours = pObject.updateTotalHours();
return true;
}
GetSummary(sum : any, record : any)
{
var i = 1;
}
UpdateMainGridForCorrectNumberOfWeeks(numberOfWeeks : number, colStore : ColumnStore, prefixColumnCount : number)
{
console.log("Updating Main grid for " + numberOfWeeks + " weeks.")
const weekday = ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"];
while(colStore.count >= (prefixColumnCount + 1))
{
colStore.remove(colStore.last);
}
var numberOfDays : number = 7 * numberOfWeeks;
for(var num = 0; num < numberOfDays; num++)
{
var fieldString = "data.staffingRequirementTemplateAllocations[" + (num) + "].staffingShiftTemplateId";
const col:ExtendedColumn = <ExtendedColumn><unknown>({
text: weekday[num % 7],
field: fieldString,
dayIndexNumber: num + 1,
flex: 1,
finalizeCellEdit : this.onDropDownEditEnds,
editor : {
type : 'combo',
items : this.shiftTemplateStore,
// specify valueField'/'displayField' to match the data format in the store
valueField : 'staffingShiftTemplateId',
displayField : 'name'
},
summaries : [
],
renderer(data: { value : String; record : any; column : Column, grid : Grid, size : any }) : String
{
var id : Number | null = null;
if(data != null && data.record != null && data.record.staffingRequirementTemplateAllocations != null && data.column != null && data.column.parentIndex != null)
{
var indexToUse : number = (data.column.parentIndex - 4);
if(data.record.staffingRequirementTemplateAllocations[indexToUse] != null)
{
id = data.record.staffingRequirementTemplateAllocations[indexToUse].staffingShiftTemplateId;
}
}
var grid : Grid = data.grid;
var result : string = "";
if(id != null)
{
//result = id.toString();
var x = (data.column as any);
var y = x.editor.store;
var z = y.findByField("staffingShiftTemplateId", id);
var v = z[0].data.name;
result = v;
}
return result;
}
});
col.summaries = [];
this.staffingLocationShiftTemplateData.forEach(t =>
{
var s =
{ sum : (sum, record) =>
{
var store : Store = record.stores[0];
var people = store.records.filter((p : any) => p.jobTypeId == record.jobTypeId);
var result : String = "";
var assignedShifts : number = 0;
if(people.length > 0)
{
var relevantAllocations : IStaffingRequirementTemplateAllocation[] = [];
var dayNumber : number = col.dayIndexNumber;
people.forEach((p) => {
if((p as any).staffingRequirementTemplateAllocations != null)
{
var allocations = (p as any).staffingRequirementTemplateAllocations.filter((a : any) => a.dayNumber == dayNumber && a.staffingShiftTemplateId == t.staffingShiftTemplateId);
if(allocations.length > 0)
{
relevantAllocations = relevantAllocations.concat(allocations);
}
}
});
assignedShifts = relevantAllocations.length;
var baseline : number = 0;
var baselineRecords = this.staffingRequirementBaselineData.filter((a : IStaffingRequirementBaseline) => a.dayOfWeekId == (weekday.indexOf(col.text) + 1) && a.jobTypeId == (people[0] as any).jobTypeId && a.staffingShiftTemplateId == t.staffingShiftTemplateId);
if(baselineRecords.length > 0)
{
baseline = baselineRecords[0].staffRequired;
}
}
result += t.name + " " + assignedShifts.toString() + "/" + baseline.toString();
return result;
}
};
col.summaries.push(s);
});
colStore.add(col);
}
}
refreshStaffingRequirementBaselineData(locationId : number)
{
this.staffingRequirementBaselineObservable = this.staffingRequirementBaselineService.getMaximumBaselineByLocation(locationId);
this.staffingRequirementBaselineSub = this.staffingRequirementBaselineObservable.subscribe({
next: baselineData =>{
this.staffingRequirementBaselineData = baselineData;
console.log("Baseline Data Received");
},
error: err =>
{
console.log(err);
this.errorMessage = err;
}
});
}
processPersonAndTemplateResult(personResult : Person[], templateResult : IStaffingRequirementTemplateAllocation[])
{
this.personData = personResult;
console.log("Person Data Received");
this.templateAllocationData = templateResult;
console.log("Template Allocation Data Recieved");
this.personDataStore.removeAll();
this.personData.forEach(p => {
p.staffingRequirementTemplateAllocations = this.templateAllocationData.filter(t => t.personId == p.personId)
let pObject : Person = Object.assign(new Person(), p);
p.totalHours = pObject.updateTotalHours();
p.adjustedContractedHours = p.contractedHours * this.templateData.numberOfWeeks;
this.personDataStore.add(p);
});
}
refreshGrid(locationId : number, templateId : number) : void
{
this.mainGrid.mask("Loading Data");
if(locationId != null && templateId != null)
{
this.templateAllocationObservable = this.staffingRequirementTemplateAllocationDataService.getStaffingRequirementTemplatesAllocations(templateId);
this.personDataObservable = this.personDataService.getPermanentStaffForLocation(locationId);
this.combinedCallObservable = forkJoin([this.personDataObservable, this.templateAllocationObservable]);
this.combinedCallObservable.subscribe({
next: resultVar => {
this.processPersonAndTemplateResult(resultVar[0], resultVar[1])
this.mainGrid.refreshRows();
this.mainGrid.unmaskBody();
}
});
console.log("Test");
}
else
{
this.mainGrid.unmaskBody();
}
}
updateLocations() : void
{
this.locationDataSub = this.locationDataService.getLocations().subscribe({
next: locationDataDownload => {
this.locationData = locationDataDownload;
console.log("Location Data received");
var s : Store = this.gridWidgetMap.wardCombo.store as Store;
this.locationData.forEach(l => {
s.add(l);
});
},
error: err =>
{
console.log(err);
this.errorMessage = err;
}
})
}
updateStaffingShiftTemplates(locationId : number) : void
{
this.staffingLocationShiftTemplateSub = this.staffingLocationShiftTemplateService.getStaffingLocationShiftTemplate(locationId).subscribe({
next: staffingLocationShiftTemplateDataDownload => {
this.staffingLocationShiftTemplateData = staffingLocationShiftTemplateDataDownload;
console.log("Staffing Location Templates Data received");
var emptyShift : IStaffingShiftTemplate = { endTime : "00:00", "breakMinutes" : 0, startTime : "00:00", staffingShiftTemplateId : 0, name : "None", orderNumber : 0};
this.shiftTemplateStore.removeAll();
MetaData.StaffingShiftTemplates = this.staffingLocationShiftTemplateData;
this.shiftTemplateStore.add(emptyShift);
this.staffingLocationShiftTemplateData.forEach(t => {
this.shiftTemplateStore.add(t);
})
},
error: err =>
{
console.log(err);
this.errorMessage = err;
}
})
}
updateStaffingRequirementTemplates(locationId : number) : void
{
this.staffingRequirementTemplateSub = this.staffingRequirementTemplateDataService.getStaffingRequirementTemplates(locationId).subscribe({
next: staffingRequirementTemplateDataDownload => {
this.staffingRequirementTemplateData = staffingRequirementTemplateDataDownload;
console.log("Staffing Requirement Template Data received");
var s : Store = this.gridWidgetMap.templateCombo.store as Store;
s.removeAll();
this.staffingRequirementTemplateData.forEach(t => {
s.add(t);
});
},
error: err =>
{
console.log(err);
this.errorMessage = err;
}
})
}
}
Also if it is relevant this is the ExtendedColumn class:
import { Column } from "@bryntum/grid";
export class ExtendedColumn extends Column{
summaries : Object[];
dayIndexNumber : number;
}