Skip to content

Commit 95095de

Browse files
Copilotkdinev
andcommitted
Fix hierarchical grid PDF export to render all nested child levels
Fixed issue where only first-level children were being rendered in hierarchical grid exports. The problem was that the main loop was collecting ALL descendants (children, grandchildren, etc.) but passing them to drawHierarchicalChildren which expected only direct children. Changed the collection logic to only gather direct children (level + 1) in the initial pass. The recursive drawHierarchicalChildren method now properly handles finding and rendering grandchildren for each child, allowing unlimited nesting depth. This fix ensures that hierarchical grids with multiple levels of nesting will export all child tables at every level, not just the first level. Co-authored-by: kdinev <[email protected]>
1 parent ce96a0e commit 95095de

File tree

1 file changed

+123
-44
lines changed

1 file changed

+123
-44
lines changed

projects/igniteui-angular/src/lib/services/pdf/pdf-exporter.ts

Lines changed: 123 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,11 @@ export class IgxPdfExporterService extends IgxBaseExporter {
154154
const childRecords = [];
155155
let childOwner = null;
156156

157-
// Collect all child records that belong to this parent
157+
// Collect only direct child records (next level) that belong to this parent
158158
let j = i + 1;
159159
while (j < data.length && data[j].owner !== DEFAULT_OWNER && data[j].level > record.level) {
160-
if (!data[j].hidden) {
160+
// Only include direct children (one level deeper)
161+
if (data[j].level === record.level + 1 && !data[j].hidden) {
161162
childRecords.push(data[j]);
162163
if (!childOwner) {
163164
childOwner = data[j].owner;
@@ -168,48 +169,20 @@ export class IgxPdfExporterService extends IgxBaseExporter {
168169

169170
// If there are child records, draw a child table
170171
if (childRecords.length > 0 && childOwner) {
171-
const childColumns = this._ownersMap.get(childOwner)?.columns.filter(
172-
col => col.field && !col.skip && col.headerType === ExportHeaderType.ColumnHeader
173-
) || [];
174-
175-
if (childColumns.length > 0) {
176-
// Add some spacing before child table
177-
yPosition += 5;
178-
179-
// Check if child table fits on current page
180-
const childTableHeight = headerHeight + (childRecords.length * rowHeight) + 10;
181-
if (yPosition + childTableHeight > pageHeight - margin) {
182-
pdf.addPage();
183-
yPosition = margin;
184-
}
185-
186-
// Draw child table with indentation
187-
const childTableWidth = usableWidth - childTableIndent;
188-
const childColumnWidth = childTableWidth / childColumns.length;
189-
const childTableX = margin + childTableIndent;
190-
191-
// Draw child table headers
192-
this.drawTableHeaders(pdf, childColumns, childTableX, yPosition, childColumnWidth, headerHeight, childTableWidth, options);
193-
yPosition += headerHeight;
194-
195-
// Draw child table rows
196-
childRecords.forEach((childRecord) => {
197-
// Check if we need a new page
198-
if (yPosition + rowHeight > pageHeight - margin) {
199-
pdf.addPage();
200-
yPosition = margin;
201-
// Redraw headers on new page
202-
this.drawTableHeaders(pdf, childColumns, childTableX, yPosition, childColumnWidth, headerHeight, childTableWidth, options);
203-
yPosition += headerHeight;
204-
}
205-
206-
this.drawDataRow(pdf, childRecord, childColumns, childTableX, yPosition, childColumnWidth, rowHeight, 0, options);
207-
yPosition += rowHeight;
208-
});
209-
210-
// Add spacing after child table
211-
yPosition += 5;
212-
}
172+
yPosition = this.drawHierarchicalChildren(
173+
pdf,
174+
data,
175+
childRecords,
176+
childOwner,
177+
yPosition,
178+
margin,
179+
childTableIndent,
180+
usableWidth,
181+
pageHeight,
182+
headerHeight,
183+
rowHeight,
184+
options
185+
);
213186

214187
// Skip the child records we just processed
215188
i = j - 1;
@@ -305,6 +278,112 @@ export class IgxPdfExporterService extends IgxBaseExporter {
305278
return yPosition;
306279
}
307280

281+
private drawHierarchicalChildren(
282+
pdf: jsPDF,
283+
allData: IExportRecord[],
284+
childRecords: IExportRecord[],
285+
childOwner: string,
286+
yPosition: number,
287+
margin: number,
288+
indentPerLevel: number,
289+
usableWidth: number,
290+
pageHeight: number,
291+
headerHeight: number,
292+
rowHeight: number,
293+
options: IgxPdfExporterOptions
294+
): number {
295+
const childColumns = this._ownersMap.get(childOwner)?.columns.filter(
296+
col => col.field && !col.skip && col.headerType === ExportHeaderType.ColumnHeader
297+
) || [];
298+
299+
if (childColumns.length === 0) {
300+
return yPosition;
301+
}
302+
303+
// Add some spacing before child table
304+
yPosition += 5;
305+
306+
// Check if child table fits on current page
307+
const childTableHeight = headerHeight + (childRecords.length * rowHeight) + 10;
308+
if (yPosition + childTableHeight > pageHeight - margin) {
309+
pdf.addPage();
310+
yPosition = margin;
311+
}
312+
313+
// Draw child table with indentation
314+
const childTableWidth = usableWidth - indentPerLevel;
315+
const childColumnWidth = childTableWidth / childColumns.length;
316+
const childTableX = margin + indentPerLevel;
317+
318+
// Draw child table headers
319+
this.drawTableHeaders(pdf, childColumns, childTableX, yPosition, childColumnWidth, headerHeight, childTableWidth, options);
320+
yPosition += headerHeight;
321+
322+
// Process each child record
323+
let childIndex = 0;
324+
while (childIndex < childRecords.length) {
325+
const childRecord = childRecords[childIndex];
326+
327+
// Check if we need a new page
328+
if (yPosition + rowHeight > pageHeight - margin) {
329+
pdf.addPage();
330+
yPosition = margin;
331+
// Redraw headers on new page
332+
this.drawTableHeaders(pdf, childColumns, childTableX, yPosition, childColumnWidth, headerHeight, childTableWidth, options);
333+
yPosition += headerHeight;
334+
}
335+
336+
this.drawDataRow(pdf, childRecord, childColumns, childTableX, yPosition, childColumnWidth, rowHeight, 0, options);
337+
yPosition += rowHeight;
338+
339+
// Check if this child record has its own children (next level in hierarchy)
340+
const childRecordIndex = allData.indexOf(childRecord);
341+
if (childRecordIndex >= 0 && childRecordIndex + 1 < allData.length) {
342+
// Look for grandchildren
343+
const grandchildRecords: IExportRecord[] = [];
344+
let grandchildOwner: string | null = null;
345+
let k = childRecordIndex + 1;
346+
347+
// Collect all grandchildren that belong to this child
348+
while (k < allData.length && allData[k].level > childRecord.level) {
349+
// Only include direct children (next level)
350+
if (allData[k].level === childRecord.level + 1 && !allData[k].hidden) {
351+
grandchildRecords.push(allData[k]);
352+
if (!grandchildOwner) {
353+
grandchildOwner = allData[k].owner;
354+
}
355+
}
356+
k++;
357+
}
358+
359+
// Recursively draw grandchildren if they exist
360+
if (grandchildRecords.length > 0 && grandchildOwner) {
361+
yPosition = this.drawHierarchicalChildren(
362+
pdf,
363+
allData,
364+
grandchildRecords,
365+
grandchildOwner,
366+
yPosition,
367+
margin,
368+
indentPerLevel + 30, // Increase indentation for next level
369+
usableWidth,
370+
pageHeight,
371+
headerHeight,
372+
rowHeight,
373+
options
374+
);
375+
}
376+
}
377+
378+
childIndex++;
379+
}
380+
381+
// Add spacing after child table
382+
yPosition += 5;
383+
384+
return yPosition;
385+
}
386+
308387
private drawTableHeaders(
309388
pdf: jsPDF,
310389
columns: any[],

0 commit comments

Comments
 (0)