@@ -2,27 +2,24 @@ import { NgTemplateOutlet } from '@angular/common';
2
2
import {
3
3
booleanAttribute ,
4
4
ChangeDetectionStrategy ,
5
- ChangeDetectorRef ,
6
5
Component ,
7
6
DoCheck ,
8
7
ElementRef ,
9
- EventEmitter ,
10
8
HostListener ,
11
9
inject ,
12
- Input ,
13
10
IterableDiffer ,
14
11
IterableDiffers ,
15
12
KeyValueDiffer ,
16
13
KeyValueDiffers ,
17
- OnChanges ,
18
14
OnInit ,
19
- Output ,
20
15
signal ,
21
- SimpleChanges ,
22
- ViewChild
16
+ input ,
17
+ output ,
18
+ viewChild ,
19
+ linkedSignal
23
20
} from '@angular/core' ;
24
21
25
- import { Group , GroupContext , Row , RowDetailContext , RowOrGroup } from '../../types/public.types' ;
22
+ import { Group , Row , RowOrGroup , RowDetailContext , GroupContext } from '../../types/public.types' ;
26
23
import { DATATABLE_COMPONENT_TOKEN } from '../../utils/table-token' ;
27
24
import { DatatableRowDetailDirective } from '../row-detail/row-detail.directive' ;
28
25
import { DatatableGroupHeaderDirective } from './body-group-header.directive' ;
@@ -31,40 +28,42 @@ import { DatatableGroupHeaderDirective } from './body-group-header.directive';
31
28
selector : 'datatable-row-wrapper' ,
32
29
imports : [ NgTemplateOutlet ] ,
33
30
template : `
34
- @if (isGroup(row) && groupHeader?.template) {
31
+ @let row = this.row();
32
+ @let groupHeader = this.groupHeader();
33
+ @if (isGroup(row) && groupHeader && groupHeader.template) {
35
34
<div
36
35
class="datatable-group-header"
37
- [style.height.px]="groupHeaderRowHeight"
38
- [style.width.px]="innerWidth"
36
+ [style.height.px]="groupHeaderRowHeight() "
37
+ [style.width.px]="innerWidth() "
39
38
>
40
39
<div class="datatable-group-cell">
41
- @if (groupHeader! .checkboxable) {
40
+ @if (groupHeader.checkboxable) {
42
41
<div>
43
42
<label class="datatable-checkbox">
44
43
<input
45
44
#select
46
45
type="checkbox"
47
- [attr.aria-label]="ariaGroupHeaderCheckboxMessage"
46
+ [attr.aria-label]="ariaGroupHeaderCheckboxMessage() "
48
47
[checked]="selectedGroupRows().length === row.value.length"
49
48
(change)="onCheckboxChange(select.checked, row)"
50
49
/>
51
50
</label>
52
51
</div>
53
52
}
54
53
<ng-template
55
- [ngTemplateOutlet]="groupHeader! .template! "
56
- [ngTemplateOutletContext]="context"
54
+ [ngTemplateOutlet]="groupHeader.template"
55
+ [ngTemplateOutletContext]="context() "
57
56
/>
58
57
</div>
59
58
</div>
60
59
}
61
- @if ((groupHeader?.template && expanded) || !groupHeader || !groupHeader.template) {
60
+ @if ((groupHeader?.template && expanded()) || !groupHeader || !groupHeader? .template) {
62
61
<ng-content />
63
62
}
64
- @let rowDetailTemplate = rowDetail?.template();
65
- @if (rowDetailTemplate && expanded) {
66
- <div class="datatable-row-detail" [style.height.px]="detailRowHeight">
67
- <ng-template [ngTemplateOutlet]="rowDetailTemplate" [ngTemplateOutletContext]="context" />
63
+ @let rowDetailTemplate = rowDetail() ?.template();
64
+ @if (rowDetailTemplate && expanded() ) {
65
+ <div class="datatable-row-detail" [style.height.px]="detailRowHeight() ">
66
+ <ng-template [ngTemplateOutlet]="rowDetailTemplate" [ngTemplateOutletContext]="context() " />
68
67
</div>
69
68
}
70
69
` ,
@@ -74,99 +73,96 @@ import { DatatableGroupHeaderDirective } from './body-group-header.directive';
74
73
class : 'datatable-row-wrapper'
75
74
}
76
75
} )
77
- export class DataTableRowWrapperComponent < TRow extends Row = any >
78
- implements DoCheck , OnInit , OnChanges
79
- {
80
- @ViewChild ( 'select' ) checkBoxInput ! : ElementRef < HTMLInputElement > ;
81
- @Input ( ) innerWidth ! : number ;
82
- @Input ( ) rowDetail ?: DatatableRowDetailDirective ;
83
- @Input ( ) groupHeader ?: DatatableGroupHeaderDirective ;
84
- @Input ( ) offsetX ! : number ;
85
- @Input ( ) detailRowHeight ! : number ;
86
- @Input ( ) groupHeaderRowHeight ! : number ;
87
- @Input ( ) row ! : RowOrGroup < TRow > ;
88
- @Input ( ) groupedRows ?: Group < TRow > [ ] ;
89
- @Input ( ) selected ! : TRow [ ] ;
90
- @Input ( ) disabled ?: boolean ;
91
- @Output ( ) readonly rowContextmenu = new EventEmitter < {
76
+ export class DataTableRowWrapperComponent < TRow extends Row = any > implements DoCheck , OnInit {
77
+ readonly checkBoxInput = viewChild < ElementRef < HTMLInputElement > > ( 'select' ) ;
78
+ readonly innerWidth = input . required < number > ( ) ;
79
+ readonly rowDetail = input < DatatableRowDetailDirective > ( ) ;
80
+ readonly groupHeader = input < DatatableGroupHeaderDirective > ( ) ;
81
+ readonly offsetX = input . required < number > ( ) ;
82
+ readonly detailRowHeight = input . required < number > ( ) ;
83
+ readonly groupHeaderRowHeight = input . required < number > ( ) ;
84
+ readonly row = input . required < RowOrGroup < TRow > > ( ) ;
85
+ readonly groupedRows = input < Group < TRow > [ ] > ( ) ;
86
+ readonly selected = input . required < TRow [ ] > ( ) ;
87
+ readonly disabled = input < boolean > ( ) ;
88
+ readonly rowContextmenu = output < {
92
89
event : MouseEvent ;
93
90
row : RowOrGroup < TRow > ;
94
- } > ( false ) ;
91
+ } > ( ) ;
95
92
96
- @ Input ( ) rowIndex ! : number ;
93
+ readonly rowIndex = input . required < number > ( ) ;
97
94
98
95
readonly selectedGroupRows = signal < TRow [ ] > ( [ ] ) ;
99
96
100
- @ Input ( { transform : booleanAttribute } ) expanded = false ;
101
- @ Input ( { required : true } ) ariaGroupHeaderCheckboxMessage ! : string ;
97
+ readonly expanded = input ( false , { transform : booleanAttribute } ) ;
98
+ readonly ariaGroupHeaderCheckboxMessage = input . required < string > ( ) ;
102
99
103
- context ! : RowDetailContext < TRow > | GroupContext < TRow > ;
100
+ readonly context = linkedSignal < RowDetailContext < TRow > | GroupContext < TRow > > ( ( ) => {
101
+ const row = this . row ( ) ;
102
+ if ( this . isGroup ( row ) ) {
103
+ return {
104
+ group : row ,
105
+ expanded : this . expanded ( ) ,
106
+ rowIndex : this . rowIndex ( )
107
+ } ;
108
+ } else {
109
+ return {
110
+ row,
111
+ expanded : this . expanded ( ) ,
112
+ rowIndex : this . rowIndex ( ) ,
113
+ disabled : this . disabled ( )
114
+ } ;
115
+ }
116
+ } ) ;
104
117
105
118
private rowDiffer : KeyValueDiffer < keyof RowOrGroup < TRow > , any > = inject ( KeyValueDiffers )
106
119
. find ( { } )
107
120
. create ( ) ;
108
121
private iterableDiffers = inject ( IterableDiffers ) ;
109
122
private selectedRowsDiffer ! : IterableDiffer < TRow > ;
110
123
private tableComponent = inject ( DATATABLE_COMPONENT_TOKEN ) ;
111
- private cd = inject ( ChangeDetectorRef ) ;
112
-
113
- ngOnChanges ( changes : SimpleChanges ) : void {
114
- if ( changes . row ) {
115
- // this component renders either a group header or a row. Never both.
116
- if ( this . isGroup ( this . row ) ) {
117
- this . context = {
118
- group : this . row ,
119
- expanded : this . expanded ,
120
- rowIndex : this . rowIndex
121
- } ;
122
- } else {
123
- this . context = {
124
- row : this . row ,
125
- expanded : this . expanded ,
126
- rowIndex : this . rowIndex ,
127
- disabled : this . disabled
128
- } ;
129
- }
130
- }
131
- if ( changes . rowIndex ) {
132
- this . context . rowIndex = this . rowIndex ;
133
- }
134
- if ( changes . expanded ) {
135
- this . context . expanded = this . expanded ;
136
- }
137
- }
138
124
139
125
ngOnInit ( ) : void {
140
- this . selectedRowsDiffer = this . iterableDiffers . find ( this . selected ?? [ ] ) . create ( ) ;
126
+ this . selectedRowsDiffer = this . iterableDiffers . find ( this . selected ( ) ?? [ ] ) . create ( ) ;
141
127
}
142
128
143
129
ngDoCheck ( ) : void {
144
- if ( this . rowDiffer . diff ( this . row ) ) {
145
- if ( 'group' in this . context ) {
146
- this . context . group = this . row as Group < TRow > ;
130
+ const row = this . row ( ) ;
131
+ if ( this . rowDiffer . diff ( row ) ) {
132
+ if ( 'group' in this . context ( ) ) {
133
+ this . context . set ( {
134
+ group : row as Group < TRow > ,
135
+ expanded : this . expanded ( ) ,
136
+ rowIndex : this . rowIndex ( )
137
+ } ) ;
147
138
} else {
148
- this . context . row = this . row as TRow ;
139
+ this . context . set ( {
140
+ row : row as TRow ,
141
+ expanded : this . expanded ( ) ,
142
+ rowIndex : this . rowIndex ( ) ,
143
+ disabled : this . disabled ( )
144
+ } ) ;
149
145
}
150
- this . cd . markForCheck ( ) ;
151
146
}
152
147
// When groupheader is used with chechbox we use iterableDiffer
153
148
// on currently selected rows to check if it is modified
154
149
// if any of the row of this group is not present in `selected` rows array
155
150
// mark group header checkbox state as indeterminate
156
151
if (
157
- this . isGroup ( this . row ) &&
158
- this . groupHeader ?. checkboxable &&
159
- this . selectedRowsDiffer . diff ( this . selected )
152
+ this . isGroup ( row ) &&
153
+ this . groupHeader ( ) ?. checkboxable &&
154
+ this . selectedRowsDiffer . diff ( this . selected ( ) )
160
155
) {
161
- const thisRow = this . row ;
162
- const selectedRows = this . selected . filter ( row =>
163
- thisRow . value . find ( ( item : TRow ) => item === row )
156
+ const thisRow = row ;
157
+ const selectedRows = this . selected ( ) . filter ( rowItem =>
158
+ thisRow . value . find ( ( item : TRow ) => item === rowItem )
164
159
) ;
165
- if ( this . checkBoxInput ) {
166
- if ( selectedRows . length && selectedRows . length !== this . row . value . length ) {
167
- this . checkBoxInput . nativeElement . indeterminate = true ;
160
+ const checkBoxInput = this . checkBoxInput ( ) ;
161
+ if ( checkBoxInput ) {
162
+ if ( selectedRows . length && selectedRows . length !== row . value . length ) {
163
+ checkBoxInput . nativeElement . indeterminate = true ;
168
164
} else {
169
- this . checkBoxInput . nativeElement . indeterminate = false ;
165
+ checkBoxInput . nativeElement . indeterminate = false ;
170
166
}
171
167
}
172
168
this . selectedGroupRows . set ( selectedRows ) ;
@@ -175,27 +171,27 @@ export class DataTableRowWrapperComponent<TRow extends Row = any>
175
171
176
172
@HostListener ( 'contextmenu' , [ '$event' ] )
177
173
onContextmenu ( $event : MouseEvent ) : void {
178
- this . rowContextmenu . emit ( { event : $event , row : this . row } ) ;
174
+ this . rowContextmenu . emit ( { event : $event , row : this . row ( ) } ) ;
179
175
}
180
176
181
177
onCheckboxChange ( groupSelected : boolean , group : Group < TRow > ) : void {
182
178
// First remove all rows of this group from `selected`
183
- this . selected = [
184
- ...this . selected . filter ( row => ! group . value . find ( ( item : TRow ) => item === row ) )
179
+ let selected = [
180
+ ...this . selected ( ) . filter ( row => ! group . value . find ( ( item : TRow ) => item === row ) )
185
181
] ;
186
182
// If checkbox is checked then add all rows of this group in `selected`
187
183
if ( groupSelected ) {
188
- this . selected = [ ...this . selected , ...group . value ] ;
184
+ selected = [ ...this . selected ( ) , ...group . value ] ;
189
185
}
190
186
// Update `selected` of DatatableComponent with newly evaluated `selected`
191
- this . tableComponent . selected = [ ...this . selected ] ;
187
+ this . tableComponent . selected = [ ...selected ] ;
192
188
// Emit select event with updated values
193
189
this . tableComponent . onBodySelect ( {
194
- selected : this . selected
190
+ selected : [ ... selected ]
195
191
} ) ;
196
192
}
197
193
198
194
isGroup ( row : RowOrGroup < TRow > ) : row is Group < TRow > {
199
- return ! ! this . groupHeader ;
195
+ return ! ! this . groupHeader ( ) ;
200
196
}
201
197
}
0 commit comments