Skip to content

Commit 3c9bc9b

Browse files
authored
Merge branch '20.1.x' into pmoleri/fix-template-outlet-leak
2 parents 8d28d5b + 7f538ff commit 3c9bc9b

File tree

15 files changed

+195
-24
lines changed

15 files changed

+195
-24
lines changed

projects/igniteui-angular/src/lib/data-operations/merge-strategy.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { GridType } from '../grids/common/grid.interface';
77
export interface IMergeByResult {
88
rowSpan: number;
99
root?: any;
10+
childRecords?: any[];
1011
}
1112

1213
/**
@@ -75,10 +76,11 @@ export class DefaultMergeStrategy implements IGridMergeStrategy {
7576
continue;
7677
}
7778
const recToUpdateData = recData ?? { recordRef: grid.isGhostRecord(rec) ? rec.recordRef : rec, cellMergeMeta: new Map<string, IMergeByResult>(), ghostRecord: rec.ghostRecord };
78-
recToUpdateData.cellMergeMeta.set(field, { rowSpan: 1 });
79+
recToUpdateData.cellMergeMeta.set(field, { rowSpan: 1, childRecords: [] });
7980
if (prev && comparer.call(this, prev.recordRef, recToUpdateData.recordRef, field, isDate, isTime) && prev.ghostRecord === recToUpdateData.ghostRecord) {
8081
const root = prev.cellMergeMeta.get(field)?.root ?? prev;
8182
root.cellMergeMeta.get(field).rowSpan += 1;
83+
root.cellMergeMeta.get(field).childRecords.push(recToUpdateData);
8284
recToUpdateData.cellMergeMeta.get(field).root = root;
8385
}
8486
prev = recToUpdateData;

projects/igniteui-angular/src/lib/directives/input/input.directive.spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,22 @@ describe('IgxInput', () => {
908908

909909
expect(formControl.touched).toBe(true);
910910
}));
911+
912+
it('should update validity when control is marked as touched', fakeAsync(() => {
913+
const fixture = TestBed.createComponent(ReactiveFormComponent);
914+
fixture.detectChanges();
915+
916+
const component = fixture.componentInstance;
917+
const igxInput = component.strIgxInput;
918+
919+
expect(igxInput.valid).toBe(IgxInputState.INITIAL);
920+
921+
component.markAllAsTouched();
922+
tick();
923+
fixture.detectChanges();
924+
925+
expect(igxInput.valid).toBe(IgxInputState.INVALID);
926+
}));
911927
});
912928

913929
@Component({
@@ -1201,6 +1217,12 @@ class ReactiveFormComponent {
12011217
this.textareaControl.markAsTouched();
12021218
this.textareaControl.updateValueAndValidity();
12031219
}
1220+
1221+
public markAllAsTouched() {
1222+
if (!this.form.valid) {
1223+
this.form.markAllAsTouched();
1224+
}
1225+
}
12041226
}
12051227

12061228
@Component({

projects/igniteui-angular/src/lib/directives/input/input.directive.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ import {
1616
import {
1717
AbstractControl,
1818
NgControl,
19-
NgModel
19+
NgModel,
20+
TouchedChangeEvent
2021
} from '@angular/forms';
21-
import { Subscription } from 'rxjs';
22+
import { filter, Subscription } from 'rxjs';
2223
import { IgxInputGroupBase } from '../../input-group/input-group.common';
2324

2425
const nativeValidationAttributes = [
@@ -100,6 +101,7 @@ export class IgxInputDirective implements AfterViewInit, OnDestroy {
100101
private _valid = IgxInputState.INITIAL;
101102
private _statusChanges$: Subscription;
102103
private _valueChanges$: Subscription;
104+
private _touchedChanges$: Subscription;
103105
private _fileNames: string;
104106
private _disabled = false;
105107

@@ -313,6 +315,14 @@ export class IgxInputDirective implements AfterViewInit, OnDestroy {
313315
this._valueChanges$ = this.ngControl.valueChanges.subscribe(
314316
this.onValueChanged.bind(this)
315317
);
318+
319+
if (this.ngControl.control) {
320+
this._touchedChanges$ = this.ngControl.control.events
321+
.pipe(filter(e => e instanceof TouchedChangeEvent))
322+
.subscribe(
323+
this.updateValidityState.bind(this)
324+
);
325+
}
316326
}
317327

318328
this.cdr.detectChanges();
@@ -326,6 +336,10 @@ export class IgxInputDirective implements AfterViewInit, OnDestroy {
326336
if (this._valueChanges$) {
327337
this._valueChanges$.unsubscribe();
328338
}
339+
340+
if (this._touchedChanges$) {
341+
this._touchedChanges$.unsubscribe();
342+
}
329343
}
330344
/**
331345
* Sets a focus on the igxInput.

projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-conditional-filter.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
[igxDropDownItemNavigation]="subMenu"
88
role="menuitem"
99
aria-haspopup="true"
10-
[attr.aria-controls]="this.subMenu?.listId"
11-
[attr.aria-activedescendant]="!this.subMenu?.collapsed ? this.subMenu?.focusedItem?.id : null">
10+
[attr.aria-controls]="subMenu?.listId"
11+
[attr.aria-activedescendant]="!subMenu?.collapsed ? subMenu?.focusedItem?.id : null">
1212
<span class="igx-excel-filter__filter-number">
1313
{{ subMenuText }}
1414
@if (filterNumber > 0) { ({{filterNumber}}) }

projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ import {
4444
IgxGridExternalESFTemplateComponent,
4545
IgxGridDatesFilteringComponent,
4646
LoadOnDemandFilterStrategy,
47-
IgxGridFilteringNumericComponent
47+
IgxGridFilteringNumericComponent,
48+
IgxGridConditionalFilteringComponent
4849
} from '../../test-utils/grid-samples.spec';
4950
import { GridSelectionMode, FilterMode, Size } from '../common/enums';
5051
import { ControlsFunction } from '../../test-utils/controls-functions.spec';
@@ -7083,6 +7084,27 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => {
70837084
}));
70847085

70857086
});
7087+
7088+
describe('IgxGrid - Conditional Filter', () => {
7089+
let fix: ComponentFixture<IgxGridConditionalFilteringComponent>;
7090+
let grid: IgxGridComponent;
7091+
beforeEach(fakeAsync(() => {
7092+
fix = TestBed.createComponent(IgxGridConditionalFilteringComponent);
7093+
fix.detectChanges();
7094+
grid = fix.componentInstance.grid;
7095+
grid.filterMode = FilterMode.excelStyleFilter;
7096+
fix.detectChanges();
7097+
}));
7098+
7099+
it('Should not throw console error on opening the drop-down.', async () => {
7100+
spyOn(console, 'error');
7101+
GridFunctions.clickExcelFilterIconFromCodeAsync(fix, grid, 'Downloads');
7102+
fix.detectChanges();
7103+
await wait(100);
7104+
7105+
expect(console.error).not.toHaveBeenCalled();
7106+
});
7107+
});
70867108
});
70877109

70887110
describe('IgxGrid - Custom Filtering Strategy #grid', () => {

projects/igniteui-angular/src/lib/grids/grid/grid.component.html

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@
6969
| gridRowPinning:id:true:pipeTrigger
7070
| gridFiltering:filteringExpressionsTree:filterStrategy:advancedFilteringExpressionsTree:id:pipeTrigger:filteringPipeTrigger:true
7171
| gridSort:sortingExpressions:groupingExpressions:sortStrategy:id:pipeTrigger:true
72-
| gridCellMerge:columnsToMerge:cellMergeMode:mergeStrategy:activeRowIndexes:true:pipeTrigger; as pinnedData) {
72+
| gridCellMerge:columnsToMerge:cellMergeMode:mergeStrategy:pipeTrigger
73+
| gridUnmergeActive:columnsToMerge:activeRowIndexes:true:pipeTrigger; as pinnedData) {
7374
@if (pinnedData.length > 0) {
7475
<div #pinContainer
7576
[ngClass]="{
@@ -96,7 +97,8 @@
9697
| gridDetails:hasDetails:expansionStates:pipeTrigger
9798
| gridAddRow:false:pipeTrigger
9899
| gridRowPinning:id:false:pipeTrigger
99-
| gridCellMerge:columnsToMerge:cellMergeMode:mergeStrategy:activeRowIndexes:false:pipeTrigger"
100+
| gridCellMerge:columnsToMerge:cellMergeMode:mergeStrategy:pipeTrigger
101+
| gridUnmergeActive:columnsToMerge:activeRowIndexes:false:pipeTrigger"
100102
let-rowIndex="index" [igxForScrollOrientation]="'vertical'" [igxForScrollContainer]="verticalScroll"
101103
[igxForContainerSize]="calcHeight"
102104
[igxForItemSize]="hasColumnLayouts ? rowHeight * multiRowLayoutRowSize + 1 : renderedRowHeight"

projects/igniteui-angular/src/lib/grids/grid/grid.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import { IGridGroupingStrategy } from '../common/strategy';
3434
import { IgxGridValidationService } from './grid-validation.service';
3535
import { IgxGridDetailsPipe } from './grid.details.pipe';
3636
import { IgxGridSummaryPipe } from './grid.summary.pipe';
37-
import { IgxGridGroupingPipe, IgxGridPagingPipe, IgxGridSortingPipe, IgxGridFilteringPipe, IgxGridCellMergePipe } from './grid.pipes';
37+
import { IgxGridGroupingPipe, IgxGridPagingPipe, IgxGridSortingPipe, IgxGridFilteringPipe, IgxGridCellMergePipe, IgxGridUnmergeActivePipe } from './grid.pipes';
3838
import { IgxSummaryDataPipe } from '../summaries/grid-root-summary.pipe';
3939
import { IgxGridTransactionPipe, IgxHasVisibleColumnsPipe, IgxGridRowPinningPipe, IgxGridAddRowPipe, IgxGridRowClassesPipe, IgxGridRowStylesPipe, IgxStringReplacePipe } from '../common/pipes';
4040
import { IgxGridColumnResizerComponent } from '../resizing/resizer.component';
@@ -154,6 +154,7 @@ export interface IGroupingDoneEventArgs extends IBaseEventArgs {
154154
IgxGridDetailsPipe,
155155
IgxStringReplacePipe,
156156
IgxGridCellMergePipe,
157+
IgxGridUnmergeActivePipe,
157158
IgxScrollInertiaDirective
158159
],
159160
schemas: [CUSTOM_ELEMENTS_SCHEMA]

projects/igniteui-angular/src/lib/grids/grid/grid.pipes.ts

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,76 @@ export class IgxGridCellMergePipe implements PipeTransform {
8686

8787
constructor(@Inject(IGX_GRID_BASE) private grid: GridType) { }
8888

89-
public transform(collection: any, colsToMerge: ColumnType[], mergeMode: GridCellMergeMode, mergeStrategy: IGridMergeStrategy, activeRowIndexes: number[], pinned: boolean, _pipeTrigger: number) {
89+
public transform(collection: any, colsToMerge: ColumnType[], mergeMode: GridCellMergeMode, mergeStrategy: IGridMergeStrategy, _pipeTrigger: number) {
90+
if (colsToMerge.length === 0) {
91+
return collection;
92+
}
93+
const result = DataUtil.merge(collection, colsToMerge, mergeStrategy, [], this.grid);
94+
return result;
95+
}
96+
}
97+
98+
@Pipe({
99+
name: 'gridUnmergeActive',
100+
standalone: true
101+
})
102+
export class IgxGridUnmergeActivePipe implements PipeTransform {
103+
104+
constructor(@Inject(IGX_GRID_BASE) private grid: GridType) { }
105+
106+
public transform(collection: any, colsToMerge: ColumnType[], activeRowIndexes: number[], pinned: boolean, _pipeTrigger: number) {
90107
if (colsToMerge.length === 0) {
91108
return collection;
92109
}
93110
if (this.grid.hasPinnedRecords && !pinned && this.grid.pinning.rows !== RowPinningPosition.Bottom) {
94111
activeRowIndexes = activeRowIndexes.map(x => x - this.grid.pinnedRecordsCount);
95112
}
96-
const result = DataUtil.merge(cloneArray(collection), colsToMerge, mergeStrategy, activeRowIndexes, this.grid);
113+
activeRowIndexes = Array.from(new Set(activeRowIndexes)).filter(x => !isNaN(x));
114+
const rootsToUpdate = [];
115+
activeRowIndexes.forEach(index => {
116+
const target = collection[index];
117+
if (target) {
118+
colsToMerge.forEach(col => {
119+
const colMeta = target.cellMergeMeta.get(col.field);
120+
const root = colMeta.root || (colMeta.rowSpan > 1 ? target : null);
121+
if (root) {
122+
rootsToUpdate.push(root);
123+
}
124+
});
125+
}
126+
});
127+
const uniqueRoots = Array.from(new Set(rootsToUpdate));
128+
if (uniqueRoots.length === 0) {
129+
// if nothing to update, return
130+
return collection;
131+
}
132+
let result = cloneArray(collection) as any;
133+
uniqueRoots.forEach(x => {
134+
const index = result.indexOf(x);
135+
const colKeys = [...x.cellMergeMeta.keys()];
136+
const cols = colsToMerge.filter(col => colKeys.indexOf(col.field) !== -1);
137+
let res = [];
138+
for (const col of cols) {
139+
140+
let childData = x.cellMergeMeta.get(col.field).childRecords;
141+
const childRecs = childData.map(rec => rec.recordRef);
142+
const isDate = col?.dataType === 'date' || col?.dataType === 'dateTime';
143+
const isTime = col?.dataType === 'time' || col?.dataType === 'dateTime';
144+
res = this.grid.mergeStrategy.merge(
145+
[x.recordRef, ...childRecs],
146+
col.field,
147+
col.mergingComparer,
148+
res,
149+
activeRowIndexes.map(ri => ri - index),
150+
isDate,
151+
isTime,
152+
this.grid);
153+
154+
}
155+
result = result.slice(0, index).concat(res, result.slice(index + res.length));
156+
});
157+
158+
97159
return result;
98160
}
99161
}

projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.html

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@
5151
| gridRowPinning:id:true:pipeTrigger
5252
| gridFiltering:filteringExpressionsTree:filterStrategy:advancedFilteringExpressionsTree:id:pipeTrigger:filteringPipeTrigger:true
5353
| gridSort:sortingExpressions:[]:sortStrategy:id:pipeTrigger:true
54-
| gridCellMerge:columnsToMerge:cellMergeMode:mergeStrategy:activeRowIndexes:true:pipeTrigger; as pinnedData
54+
| gridCellMerge:columnsToMerge:cellMergeMode:mergeStrategy:pipeTrigger
55+
| gridUnmergeActive:columnsToMerge:activeRowIndexes:true:pipeTrigger; as pinnedData
5556
) {
5657
@if (pinnedData.length > 0) {
5758
<div #pinContainer class="igx-grid__tr--pinned"
@@ -76,7 +77,8 @@
7677
| gridHierarchical:expansionStates:id:primaryKey:childLayoutKeys:pipeTrigger
7778
| gridAddRow:false:pipeTrigger
7879
| gridRowPinning:id:false:pipeTrigger
79-
| gridCellMerge:columnsToMerge:cellMergeMode:mergeStrategy:activeRowIndexes:false:pipeTrigger"
80+
| gridCellMerge:columnsToMerge:cellMergeMode:mergeStrategy:pipeTrigger
81+
| gridUnmergeActive:columnsToMerge:activeRowIndexes:false:pipeTrigger"
8082
[igxForScrollOrientation]="'vertical'" [igxForScrollContainer]="verticalScroll"
8183
[igxForContainerSize]="calcHeight" [igxForItemSize]="renderedRowHeight" [igxForTrackBy]="trackChanges"
8284
#verticalScrollContainer (chunkPreload)="dataLoading($event)" (dataChanging)="dataRebinding($event)" (dataChanged)="dataRebound($event)">

projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ import { IgxGridValidationService } from '../grid/grid-validation.service';
5050
import { IgxGridHierarchicalPipe, IgxGridHierarchicalPagingPipe } from './hierarchical-grid.pipes';
5151
import { IgxSummaryDataPipe } from '../summaries/grid-root-summary.pipe';
5252
import { IgxGridTransactionPipe, IgxHasVisibleColumnsPipe, IgxGridRowPinningPipe, IgxGridAddRowPipe, IgxGridRowClassesPipe, IgxGridRowStylesPipe, IgxStringReplacePipe } from '../common/pipes';
53-
import { IgxGridSortingPipe, IgxGridFilteringPipe, IgxGridCellMergePipe } from '../grid/grid.pipes';
53+
import { IgxGridSortingPipe, IgxGridFilteringPipe, IgxGridCellMergePipe, IgxGridUnmergeActivePipe } from '../grid/grid.pipes';
5454
import { IgxGridColumnResizerComponent } from '../resizing/resizer.component';
5555
import { IgxRowEditTabStopDirective } from '../grid.rowEdit.directive';
5656
import { IgxIconComponent } from '../../icon/icon.component';
@@ -353,7 +353,8 @@ export class IgxChildGridRowComponent implements AfterViewInit, OnInit {
353353
IgxGridHierarchicalPagingPipe,
354354
IgxStringReplacePipe,
355355
IgxGridCellMergePipe,
356-
IgxScrollInertiaDirective
356+
IgxScrollInertiaDirective,
357+
IgxGridUnmergeActivePipe
357358
],
358359
schemas: [CUSTOM_ELEMENTS_SCHEMA]
359360
})

0 commit comments

Comments
 (0)