Usages Cell Expansion

Cell Expansion

Cell Expansion is a feature to display any component or template under a row. The trigger is from a cell in which the column definition has expansion property defined. It is a small button in the left hand side or right of the cell. Below is the way to define an expansion:

{ field: 'country', expansion: {
    //component or template defined using ng-template
    component: NestedTableComponent, 

    //optional. Put button at the start / end of the cell
    buttonPosition: 'end', 

    //optional. Call back to enable/disable expansion for each row
    isDisabled?: (row: People) => row.country !== 'ID'
  }
}

Each columns can have an expansion. In below example, Email column has expansion to display a form to send an email. Country column has one to display detail table and Action column (the edit button) can display an edit form programmatically.

Id
Name
Email
Country
Last Login
Last Login
Last Login
Last Login
Last Login
1 Abagail Kingscote
Philippines
2 Nicolina Coit
Kazakhstan
3 Sarene Greim
United States
4 Blair Millbank
Philippines
5 Kliment Sprowle
Indonesia
6 Winifred Dikle
Kazakhstan
7 Chadd Nacci
China
8 Matthiew Morland
Greece
9 Perceval Glasheen
Netherlands
10 Fenelia Oblein
Sweden
11 Lisette Ornells
Cuba
12 Mellie Anthill
Philippines
13 Charles Simkovitz
Brazil
14 Brett Hew
Macedonia
15 Netti Treend
Peru
16 Donetta Scotland
Russia
17 Larry Candish
Mongolia
18 Ianthe Nijssen
Georgia
19 Rivalee Heavyside
Indonesia
20 Ekaterina Heyburn
Indonesia
21 Cynthia Garrard
Jamaica
22 Bruis Henryson
Russia
23 Brittani Swaby
Ukraine
24 Johnna von Hagt
Ukraine
25 Brooks Kalberer
Sweden
26 Osmond Beevens
China
27 Stephi Forsdicke
China
28 Kristan Collingworth
China
29 Bord Petkov
Sweden
30 Nara Boorne
Argentina
31 Ransell Bullingham
Philippines
32 Kerr Eastcott
China
33 Victor Hallgalley
China
34 Delia Rosendale
France
35 Mickey Belding
China
36 Pippo MacColl
France
37 Radcliffe Castellet
China
38 Arvie Maxweell
Thailand
39 Darnell Valler
Poland
40 Mirilla Ziems
China
41 Werner Dorin
Philippines
42 Dael Lesek
China
43 Diarmid Dmytryk
Brazil
44 Eunice Reily
Indonesia
45 Matty Duddle
Macedonia
46 Luciano Godilington
France
47 Stacie Vassie
Ukraine
48 Aharon Samter
Oman
49 Hillery Canedo
Iran
50 Jacquelin Crowcombe
Morocco
51 Allayne Bachelor
Poland
52 Lindie Bodley
Armenia
53 Sharia Roskelly
Bulgaria
54 Saul Ubsdale
France
55 Nate Churms
Colombia
56 Tracey Bucknall
Afghanistan
57 Carole Sparrow
Russia
58 Jennica Pashba
Vietnam
59 Carlotta Turvey
Philippines
60 Drucill Roaf
China
61 Lucien Steuart
Japan
62 Daryl Slimon
Portugal
63 Sheelah Laydon
Poland
64 Bertie Brewood
Ivory Coast
65 Louise Rosin
Hungary
66 Ara Gaskins
Bulgaria
67 Roshelle Tollemache
Mexico
68 Mabelle Schwanden
Vietnam
69 Fayina Parsonson
China
70 Chancey Blackway
France
71 Josee Dasent
China
72 Larisa Dillway
China
73 Oby Woolard
Russia
74 Marylou Lebang
Spain
75 Nollie Rillett
Ivory Coast
76 Jillayne Geal
China
77 Andreas Marling
Senegal
78 Edwin Kilmister
Ukraine
79 Hershel Cornelis
Indonesia
80 Nancy Tomanek
Russia
81 Kikelia Hawgood
Albania
82 Cele Thridgould
Poland
83 Petronella Fairbank
Czech Republic
84 Thorn Maruszewski
Syria
85 Jodi Tall
Kyrgyzstan
86 Bail Ehlerding
China
87 Natal Thorald
Croatia
88 Krishna Burwood
Poland
89 Zarah Mousdall
China
90 Torre Moss
China
91 Merry Paridge
China
92 Laurene Lucius
Philippines
93 Isa Braine
Panama
94 Leisha Tompsett
China
95 Florry O'Doireidh
Denmark
96 Sophi Roberti
China
97 Leanora Nester
China
98 Giacopo Stallion
China
99 Euell Yanshonok
Czech Republic
100 Cyrillus Gilstoun
China
101 Syman Navarijo
China
102 Alix Spottiswoode
China
103 Papagena Yokelman
Morocco
104 Major Charleston
Pakistan
105 Lelia Cade
Peru
106 Nixie Cescon
China
107 Toddy Readett
Nigeria
108 Sauveur Grigoire
Netherlands
109 Vincenz Niese
France
110 Minny Bulfit
Russia
111 Jolie Amys
Indonesia
112 Wayne Pinfold
Philippines
113 Esra Cawdery
Poland
114 Norrie McElhinney
Poland
115 Allyce Audrey
Tunisia
116 Callida Lotte
Indonesia
117 Wald Awcock
Indonesia
118 Ketti Gannon
China
119 Tris Coleshill
Indonesia
120 Currey Ivanov
Brazil
121 Larry Pinnocke
Philippines
122 Johnny Delepine
Pakistan
123 Flinn Lewson
Brazil
124 Roosevelt Neat
Ukraine
125 Mohammed Sandwich
Russia
126 Muriel Duckerin
China
127 Riordan Whimpenny
Indonesia
128 Matilde Charman
Greece
129 Olympia Mariolle
Indonesia
130 Quint Gloy
Philippines
131 Sharity Rignall
Indonesia
132 Maisey Gammet
Sierra Leone
133 Dede Glawsop
Afghanistan
134 Hew Dunge
China
135 Johanna Hylton
Canada
136 Penny MacPeake
France
137 Agnese Savoury
Brazil
138 Brnaby Searle
Poland
139 Clayborn Well
China
140 Bryant O'Drought
China
141 Raddy Curling
Haiti
142 Kacey Murcutt
China
143 Val Kingsworth
Thailand
144 Bendite Boutwell
France
145 Blondelle Wrigley
Indonesia
146 Shantee Finding
China
147 Tommy Drennan
China
148 Lauri Filipiak
South Africa
149 Hagan Amorts
United States
150 Sabina Bromige
Brazil
151 Kevin Wykes
Indonesia
152 Christy Macci
Russia
153 Dar Boyse
China
154 Reuben Liebrecht
Madagascar
155 Carny Cromarty
Bosnia and Herzegovina
156 Ranee Amott
Indonesia
157 Xerxes Avramov
Finland
158 Marne Rawdall
China
159 Stavros Walkey
Portugal
160 Dode Deniskevich
Nigeria
161 Tremain Mussalli
Georgia
162 Holt Jaffra
Poland
163 Karney Karolyi
Greece
164 Erek Willatt
China
165 Guss Easman
Brazil
166 Ara Drury
Mexico
167 Mariska Blaes
Nigeria
168 Candace Hazeley
Bangladesh
169 Mildrid Chillistone
Argentina
170 Barbaraanne Waterman
Indonesia
171 Markus Forde
Burkina Faso
172 Clementia Garman
Russia
173 Karia Moquin
Ukraine
174 Gabriellia Glasson
Mexico
175 Brooks Nettle
Poland
176 Moll Straneo
China
177 Audrie Foxton
Honduras
178 Izabel Matoshin
Micronesia
179 Buck Cicccitti
Indonesia
180 Deina Denkin
China
181 Inge Yakovitch
Russia
182 Brantley Losemann
France
183 Rex Scogings
Brazil
184 Skylar Cometti
Philippines
185 Emelina Columbine
China
186 Prissie Shankland
Egypt
187 Rickert Vellacott
Spain
188 Ernestus Tubridy
New Zealand
189 Bale Naismith
Philippines
190 Tabby Magowan
Poland
191 Quincey Ivain
Poland
192 Glory Crumbleholme
Indonesia
193 Conney Semrad
Eritrea
194 Loutitia McNish
Brazil
195 Thornton Chucks
Afghanistan
196 Skylar Abramovitch
Poland
197 Andi Willmer
Indonesia
198 Durand Ruck
Japan
199 Freemon Tesdale
China
200 Lorilee Sesser
Indonesia
201 Pepita Bocken
Peru
202 Selena Milstead
China
203 Jasun Banghe
Ukraine
204 Sergeant Perelli
Russia
205 Rex Ucchino
China
206 Sigismundo Castellino
China
207 Ethelbert Shiers
Portugal
208 Tabbitha Grendon
United States
209 Anna-maria Bordis
Portugal
210 Bernard Custance
Saudi Arabia
211 Faulkner Paskell
Philippines
212 Keelia Pherps
South Africa
213 Kellina Goodhand
China
214 Petronella Janko
Portugal
215 Hertha Reynalds
Poland
216 Allyn Watling
China
217 Nollie Stoop
China
218 Charmaine Esby
Iran
219 Benedick Jina
Guatemala
220 Dahlia McCard
Belize
221 Faber Moulsdall
Brazil
222 Trefor Poad
Guatemala
223 Chris Durram
Indonesia
224 Bili Dyble
Thailand
225 Abeu Watton
South Africa
226 Vaclav Tadman
Norway
227 Trefor Farge
Brazil
228 Ahmad Marin
Thailand
229 Sibylla Wittrington
Portugal
230 Karita Niezen
Serbia
231 Ferdinande Bernakiewicz
Japan
232 Deane Flaverty
Madagascar
233 Tori Schurcke
Indonesia
234 Adler Scading
Mexico
235 Lauretta Bartosch
Argentina
236 Kerri Sommerfeld
Indonesia
237 Jephthah Coventry
Japan
238 Elaine Doncom
Malaysia
239 Oralee Butterfill
Indonesia
240 Charyl Gomer
China
241 Pepe Lardge
China
242 Verene Brickdale
Russia
243 Felisha Hayhow
Greece
244 Kelcy Siverns
Ireland
245 Andie Gerrietz
China
246 Catherina Beeho
China
247 Savina Riches
China
248 Maible Borton
France
249 Tessa Allwright
Argentina
250 Hestia Kybird
Greece
251 Yelena Lackner
China
252 Crawford Pettersen
Ukraine
253 Kass McNirlan
Mauritania
254 Fernandina Engelmann
Indonesia
255 Peyter Fisbey
China
256 Dillie Pring
Israel
257 Bobine Alves
Japan
258 Adriana Vinick
New Zealand
259 Elmore Radenhurst
Russia
260 Mic Aronowitz
Indonesia
261 Kathye Rudd
Czech Republic
262 Penelope Predohl
China
263 Bryn Dron
Norway
264 Libbey Pitrelli
Serbia
265 Cissiee Wandless
China
266 Currey Rozsa
Indonesia

The email is using ng-template. We can specify variables binding to relevant row, column and close function. For example:

<ng-template #rowDetail1 let-row="row" let-column="column" let-close="close">
  <div class="grid grid-cols-[1fr_auto] gap-4 p-4 w-[300px]">

    <!-- Display email address -->
    <div class="col-span-2">Send  to </div>
    ... 
    <!-- collapse the expansion when Send button is clicked -->
    <button (click)=close()>Send</button>
  </div>
</ng-template>

If using angular component, the component should implements ExpansionRowRenderer interface. Below is the source code of the Country cell expansion:

nested-table.component.ts
nested-table.component.html
import { Component, computed, Input, OnInit, Signal } from "@angular/core";
import { PanemuTableComponent, PanemuTableController, PanemuTableDataSource, PanemuTableService, PropertyColumn, ExpansionRowRenderer, TableQuery } from "ngx-panemu-table";
import { People } from "../../model/people";
import { DataService } from "../../service/data.service";

@Component({
  templateUrl: 'nested-table.component.html',
  imports: [PanemuTableComponent],
  standalone: true
})

export class NestedTableComponent implements OnInit, ExpansionRowRenderer<People> {
  row!: People;
  column!: PropertyColumn<People>;
  close!: Function;

  columns = this.pts.buildColumns<People>([
    { field: 'id' },
    { field: 'name' },
    { field: 'email' },
    { field: 'country' },

  ])
  datasource = new PanemuTableDataSource<People>
  controller = PanemuTableController.createWithCustomDataSource<People>(this.columns, this.loadData.bind(this));
  rowCount = computed(() => {
    if (this.controller.getAllDataAsSignal()) {
      return this.controller.getData().length
    }
    return 0
  })   

  constructor(private pts: PanemuTableService, private dataService: DataService) { }
  

  ngOnInit() {
    this.dataService.getPeople().subscribe({
      next: people => {
        this.datasource.setData(people);
        this.controller.reloadData();
      }
    })
    
  }

  private loadData(startIndex: number, maxRows: number, tableQuery: TableQuery) {
    tableQuery.tableCriteria = [{field: 'country', value: this.row.country}];
    return this.datasource.getData(startIndex, maxRows, tableQuery);
  }
}

The Edit People expansion is a bit different. It doesn't have the default button to trigger the expansion. Instead the button is a custom one with pencil icon. This is achieved by providing cellRenderer to the column definition. Then the pencil button click event is bound to PanemuTableController.expand() method.

Below is the ng-template for the pencil button:

<ng-template #actionCellTemplate let-column="column" let-row="row">
  <div class="flex justify-between">
    <button class="action-button" (click)="edit(row)"><span class="material-symbols-outlined text-base leading-5 block">edit</span></button>
  </div>
</ng-template>

Below is the edit function in the .ts file

edit(row: People) {
  this.controller.expand(row, this.clmEditInExpansion)
}

And finally here is the PeopleFormComponent rendered inside the expansion.

people-form.component.ts
people-form.component.html
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, signal } from '@angular/core';
import { PanemuBusyIndicatorComponent, PropertyColumn, ExpansionRowRenderer } from 'ngx-panemu-table';
import { People } from '../../model/people';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';

@Component({
  selector: 'app-people-form',
  templateUrl: 'people-form.component.html',
  standalone: true,
  imports: [PanemuBusyIndicatorComponent, ReactiveFormsModule],
})
export class PeopleFormComponent implements OnInit, ExpansionRowRenderer<People> {
  row!: People;
  column!: PropertyColumn<People>;
  close!: Function;
  ready = signal(false);

  form = this.fb.group({
    name: [''],
    email: ['']
  })

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    setTimeout(() => {
      this.ready.set(true);
    }, 1000);
    this.form.setValue({
      name: this.row.name,
      email: this.row.email || ''
    })
  }

  save() {
    this.row.name = this.form.controls.name.value || ''    
    this.row.email = this.form.controls.email.value || ''

    this.close()
  }
}