Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { IFilteringOperation } from './filtering-condition';
import { IExpressionTree } from './filtering-expressions-tree';

/* mustCoerceToInt */
export enum FilteringLogic {
And,
Or
}
/**
* Enumeration representing different filtering logic operators.
* - And: Logical AND operator - all conditions must be met.
* - Or: Logical OR operator - at least one condition must be met.
*/
export const FilteringLogic = {
And: 'and',
Or: 'or'
} as const;
export type FilteringLogic = (typeof FilteringLogic)[keyof typeof FilteringLogic];

/* marshalByValue */
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,8 @@
<span>{{filteringService.getOperatorAsString(item.afterOperator)}}</span>
</button>
<igx-drop-down #operators (selectionChanging)="onLogicOperatorChanged($event, item)">
<igx-drop-down-item [value]="0" [selected]="item.afterOperator === 0">{{filteringService.grid.resourceStrings.igx_grid_filter_operator_and}}</igx-drop-down-item>
<igx-drop-down-item [value]="1" [selected]="item.afterOperator === 1">{{filteringService.grid.resourceStrings.igx_grid_filter_operator_or}}</igx-drop-down-item>
<igx-drop-down-item [value]="0" [selected]="isOperatorSelected(item, 0)">{{filteringService.grid.resourceStrings.igx_grid_filter_operator_and}}</igx-drop-down-item>
<igx-drop-down-item [value]="1" [selected]="isOperatorSelected(item, 1)">{{filteringService.grid.resourceStrings.igx_grid_filter_operator_or}}</igx-drop-down-item>
</igx-drop-down>
</span>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,9 @@ export class IgxGridFilteringRowComponent implements AfterViewInit, OnDestroy {
*/
public onLogicOperatorChanged(eventArgs: ISelectionEventArgs, expression: ExpressionUI) {
if (eventArgs.oldSelection) {
expression.afterOperator = (eventArgs.newSelection as IgxDropDownItemComponent).value;
const value = (eventArgs.newSelection as IgxDropDownItemComponent).value;
// Convert numeric values to FilteringLogic constants for backwards compatibility
expression.afterOperator = value === 0 ? FilteringLogic.And : (value === 1 ? FilteringLogic.Or : value);
this.expressionsList[this.expressionsList.indexOf(expression) + 1].beforeOperator = expression.afterOperator;

// update grid's filtering on the next cycle to ensure the drop-down is closed
Expand All @@ -671,6 +673,19 @@ export class IgxGridFilteringRowComponent implements AfterViewInit, OnDestroy {
}
}

/**
* Check if the given button index matches the expression's afterOperator.
* Handles backwards compatibility with old numeric enum values (0, 1).
*/
public isOperatorSelected(expression: ExpressionUI, buttonIndex: number): boolean {
const operator = expression?.afterOperator;
if (buttonIndex === 0) {
return operator === FilteringLogic.And || (operator as any) === 0;
} else {
return operator === FilteringLogic.Or || (operator as any) === 1;
}
}

/**
* Scrolls the chips into the chip area when left or right arrows are pressed.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
#andButton
(keydown)="onLogicOperatorKeyDown($event, 0)"
tabindex="0"
[selected]="expressionUI.afterOperator === 0"
[selected]="isOperatorSelected(0)"
type="button"
(click)="onLogicOperatorButtonClicked($event, 0)">
{{ grid.resourceStrings.igx_grid_filter_operator_and }}
Expand All @@ -90,7 +90,7 @@
#orButton
tabindex="0"
(keydown)="onLogicOperatorKeyDown($event, 1)"
[selected]="expressionUI.afterOperator === 1"
[selected]="isOperatorSelected(1)"
type="button"
(click)="onLogicOperatorButtonClicked($event, 1)">
{{ grid.resourceStrings.igx_grid_filter_operator_or }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
tabindex="0"
#andButton
(keydown)="onLogicOperatorKeyDown($event, 0)"
[selected]="expressionUI.afterOperator === 0"
[selected]="isOperatorSelected(0)"
type="button"
(click)="onLogicOperatorButtonClicked($event, 0)">
{{ grid.resourceStrings.igx_grid_filter_operator_and }}
Expand All @@ -56,7 +56,7 @@
tabindex="0"
#orButton
(keydown)="onLogicOperatorKeyDown($event, 1)"
[selected]="expressionUI.afterOperator === 1"
[selected]="isOperatorSelected(1)"
type="button"
(click)="onLogicOperatorButtonClicked($event, 1)">
{{ grid.resourceStrings.igx_grid_filter_operator_or }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ export class IgxExcelStyleDefaultExpressionComponent implements AfterViewInit {
} else {
this.logicOperatorChanged.emit({
target: this.expressionUI,
newValue: buttonIndex as FilteringLogic
newValue: buttonIndex === 0 ? FilteringLogic.And : FilteringLogic.Or
});
}
}
Expand All @@ -173,11 +173,24 @@ export class IgxExcelStyleDefaultExpressionComponent implements AfterViewInit {
this.logicOperatorButtonGroup.selectButton(buttonIndex);
this.logicOperatorChanged.emit({
target: this.expressionUI,
newValue: buttonIndex as FilteringLogic
newValue: buttonIndex === 0 ? FilteringLogic.And : FilteringLogic.Or
});
}
}

/**
* Check if the given button index matches the expression's afterOperator.
* Handles backwards compatibility with old numeric enum values (0, 1).
*/
public isOperatorSelected(buttonIndex: number): boolean {
const operator = this.expressionUI?.afterOperator;
if (buttonIndex === 0) {
return operator === FilteringLogic.And || (operator as any) === 0;
} else {
return operator === FilteringLogic.Or || (operator as any) === 1;
}
}

public onRemoveButtonClick() {
this.expressionRemoved.emit(this.expressionUI);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { IgxExcelStylePinningComponent } from './excel-style-pinning.component';
import { IgxExcelStyleMovingComponent } from './excel-style-moving.component';
import { IgxExcelStyleSortingComponent } from './excel-style-sorting.component';
import { IgxExcelStyleHeaderComponent } from './excel-style-header.component';
import { ColumnType, FilteringExpressionsTree, GridColumnDataType, GridTypeBase, IFilteringExpressionsTree, IgxFilterItem, IgxOverlayService, isTree, PlatformUtil, SortingDirection } from 'igniteui-angular/core';
import { ColumnType, FilteringExpressionsTree, FilteringLogic, GridColumnDataType, GridTypeBase, IFilteringExpressionsTree, IgxFilterItem, IgxOverlayService, isTree, PlatformUtil, SortingDirection } from 'igniteui-angular/core';

@Directive({
selector: 'igx-excel-style-column-operations,[igxExcelStyleColumnOperations]',
Expand Down Expand Up @@ -461,7 +461,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent
}

const selectableExpressionsCount = this.expressionsList.filter(exp =>
(exp.beforeOperator === 1 || exp.afterOperator === 1) &&
(exp.beforeOperator === FilteringLogic.Or || exp.afterOperator === FilteringLogic.Or) &&
(exp.expression.condition.name === 'equals' ||
exp.expression.condition.name === 'at' ||
exp.expression.condition.name === 'true' ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ export class IgxFilteringService implements OnDestroy {
* Returns the string representation of the FilteringLogic operator.
*/
public getOperatorAsString(operator: FilteringLogic): any {
if (operator === 0) {
if (operator === FilteringLogic.And) {
return this.grid.resourceStrings.igx_grid_filter_operator_and;
} else {
return this.grid.resourceStrings.igx_grid_filter_operator_or;
Expand Down
31 changes: 30 additions & 1 deletion projects/igniteui-angular/grids/core/src/state-base.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { IgxColumnComponent } from './columns/column.component';
import { IgxColumnGroupComponent } from './columns/column-group.component';
import { GridSelectionRange } from './common/types';
import { GridType, IGX_GRID_BASE, IPinningConfig, PivotGridType } from './common/grid.interface';
import { cloneArray, cloneValue, ColumnType, FieldType, GridColumnDataType, IExpressionTree, IFilteringExpressionsTree, IGroupByExpandState, IGroupingExpression, IGroupingState, IPagingState, ISortingExpression, recreateTreeFromFields } from 'igniteui-angular/core';
import { cloneArray, cloneValue, ColumnType, FieldType, FilteringLogic, GridColumnDataType, IExpressionTree, IFilteringExpressionsTree, IGroupByExpandState, IGroupingExpression, IGroupingState, IPagingState, ISortingExpression, recreateTreeFromFields } from 'igniteui-angular/core';
import { IgxColumnLayoutComponent } from './columns/column-layout.component';
import { IPivotConfiguration, IPivotDimension } from './pivot-grid.interface';
import { PivotUtil } from './pivot-util';
Expand Down Expand Up @@ -670,13 +670,42 @@ export class IgxGridStateBaseDirective {
return null;
}

// Normalize operator to handle both old enum values (0, 1) and new string values ('and', 'or')
this.normalizeOperators(exprTreeObject);

if (this.currGrid.type === 'pivot') {
return recreateTreeFromFields(exprTreeObject, this.currGrid.allDimensions.map(d => ({ dataType: d.dataType, field: d.memberName })) as FieldType[]) as IExpressionTree;
}

return recreateTreeFromFields(exprTreeObject, this.currGrid.columns) as IExpressionTree;
}

/**
* Recursively normalize FilteringLogic operators to handle backwards compatibility
* with old numeric enum values (0, 1) and new string values ('and', 'or')
*/
private normalizeOperators(tree: IExpressionTree): void {
if (!tree) {
return;
}

// Normalize the operator at this level - convert old numeric values to new string values
if ((tree.operator as any) === 0) {
tree.operator = FilteringLogic.And;
} else if ((tree.operator as any) === 1) {
tree.operator = FilteringLogic.Or;
}

// Recursively normalize child trees
if (tree.filteringOperands) {
for (const operand of tree.filteringOperands) {
if (operand && 'operator' in operand && 'filteringOperands' in operand) {
this.normalizeOperators(operand as IExpressionTree);
}
}
}
}

protected stringifyCallback(key: string, val: any) {
if (key === 'searchVal' && val instanceof Set) {
return Array.from(val);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1687,15 +1687,15 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => {
// Spy for error messages in the console
const consoleSpy = spyOn(console, 'error');
// Apply advanced filter through API.
const innerTree = new FilteringExpressionsTree(0, undefined, 'childData', ['ID']);
const innerTree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'childData', ['ID']);
innerTree.filteringOperands.push({
fieldName: 'ID',
ignoreCase: false,
conditionName: IgxStringFilteringOperand.instance().condition('contains').name,
searchVal: '39'
});

const tree = new FilteringExpressionsTree(0, undefined, 'rootData', ['ID']);
const tree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'rootData', ['ID']);
tree.filteringOperands.push({
fieldName: 'ID',
conditionName: IgxStringFilteringOperand.instance().condition('inQuery').name,
Expand All @@ -1719,15 +1719,15 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => {
// Spy for error messages in the console
const consoleSpy = spyOn(console, 'error');

const innerTree = new FilteringExpressionsTree(0, undefined, 'childData', ['ID']);
const innerTree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'childData', ['ID']);
innerTree.filteringOperands.push({
fieldName: 'ID',
ignoreCase: false,
conditionName: IgxStringFilteringOperand.instance().condition('contains').name,
searchVal: '39'
});

const tree = new FilteringExpressionsTree(0, undefined, 'rootData', ['ID']);
const tree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'rootData', ['ID']);
tree.filteringOperands.push({
fieldName: 'ID',
conditionName: IgxStringFilteringOperand.instance().condition('inQuery').name,
Expand Down Expand Up @@ -1820,15 +1820,15 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => {
tick(200);
fix.detectChanges();

const innerTree = new FilteringExpressionsTree(0, undefined, 'childData', ['ID']);
const innerTree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'childData', ['ID']);
innerTree.filteringOperands.push({
fieldName: 'ID',
ignoreCase: false,
conditionName: IgxStringFilteringOperand.instance().condition('contains').name,
searchVal: '39'
});

const tree = new FilteringExpressionsTree(0, undefined, 'rootData', ['ID']);
const tree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'rootData', ['ID']);
tree.filteringOperands.push({
fieldName: 'ID',
conditionName: IgxStringFilteringOperand.instance().condition('inQuery').name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy {
/**
* @hidden @internal
*/
public initialOperator = 0;
public initialOperator: FilteringLogic = FilteringLogic.And;

/**
* @hidden @internal
Expand Down Expand Up @@ -1313,7 +1313,8 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy {
event.cancel = true;

if (event.newSelection.value === 'switchCondition') {
const newOperator = (!this.expressionTree ? this.initialOperator : (this.contextualGroup ?? this._expressionTree).operator) === 0 ? 1 : 0;
const currentOperator = !this.expressionTree ? this.initialOperator : (this.contextualGroup ?? this._expressionTree).operator;
const newOperator = currentOperator === FilteringLogic.And ? FilteringLogic.Or : FilteringLogic.And;
this.selectFilteringLogic(newOperator);
} else if (event.newSelection.value === 'ungroup') {
this.ungroup();
Expand Down Expand Up @@ -1342,17 +1343,17 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy {
/**
* @hidden @internal
*/
public selectFilteringLogic(index: number) {
public selectFilteringLogic(operator: FilteringLogic) {
if (!this.expressionTree) {
this.initialOperator = index;
this.initialOperator = operator;
return;
}

if (this.contextualGroup) {
this.contextualGroup.operator = index as FilteringLogic;
this.contextualGroup.operator = operator;
this.commitOperandEdit();
} else if (this.expressionTree) {
this._expressionTree.operator = index as FilteringLogic;
this._expressionTree.operator = operator;
}

this.initialOperator = null;
Expand Down
4 changes: 2 additions & 2 deletions projects/igniteui-angular/test-utils/grid-samples.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2133,7 +2133,7 @@ export class IgxGridAdvancedFilteringSerializedTreeComponent extends BasicGridCo
"searchVal": 200
}
],
"operator": 0
"operator": "and"
}`);

this.filterTreeObject = {
Expand All @@ -2151,7 +2151,7 @@ export class IgxGridAdvancedFilteringSerializedTreeComponent extends BasicGridCo
"searchTree": null
}
],
"operator": 1,
"operator": "or",
"returnFields": [
"ID",
"ProductName"
Expand Down