From 74b3d1b13057ef76a1b4615a36766de4bbb4fc47 Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 5 Jul 2022 19:11:06 +0300 Subject: [PATCH 001/149] feat(igxGrid): Grid with reactive forms validation POC - initial commit. --- .../src/lib/grids/cell.component.html | 25 ++++++++++++------- .../src/lib/grids/cell.component.ts | 14 +++++++++++ .../src/lib/grids/common/grid.interface.ts | 2 ++ .../src/lib/grids/grid-base.directive.ts | 4 +++ .../src/lib/grids/grid-common.module.ts | 4 ++- .../grid-cellEditing.component.html | 2 +- 6 files changed, 40 insertions(+), 11 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.html b/projects/igniteui-angular/src/lib/grids/cell.component.html index 115942f7de1..7fc1292b7bd 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/cell.component.html @@ -71,34 +71,37 @@ - + - + - + - + - + - - + + - + {{ currencyCodeSymbol }} {{ currencyCodeSymbol }} - + {{ editValue | percent:column.pipeArgs.digitsInfo:grid.locale }} diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 2c111e25892..4be126eda65 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -28,6 +28,7 @@ import { IgxRowDirective } from './row.directive'; import { ISearchInfo } from './common/events'; import { IgxGridCell } from './grid-public-cell'; import { ISelectionNode } from './common/types'; +import { FormControl, FormGroup, UntypedFormBuilder, Validators } from '@angular/forms'; /** * Providing reference to `IgxGridCellComponent`: @@ -69,6 +70,11 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT @Input() public column: ColumnType; + + public get formGroup() { + return this.grid.rowFromGroup; + } + /** * @hidden * @internal @@ -722,6 +728,14 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT cssProps: {} /* don't disable user-select, etc */ } as HammerOptions); } + // check if control for this field is already added. + const existingControl = this.formGroup.get(this.column.field); + if (!existingControl) { + // TODO get validators described on column. + const control = new FormControl(this.editValue, [ + Validators.required]); + this.formGroup.addControl(this.column.field, control); + } } /** diff --git a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts index 9ec9a6448f3..b89cecf10fc 100644 --- a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts @@ -36,6 +36,7 @@ import { OverlaySettings } from '../../services/overlay/utilities'; import { IPinningConfig } from '../grid.common'; import { IDimensionsChange, IPivotConfiguration, IPivotDimension, IPivotKeys, IPivotValue, IValuesChange, PivotDimensionType } from '../pivot-grid/pivot-grid.interface'; import { IDataCloneStrategy } from '../../data-operations/data-clone-strategy'; +import { FormGroup } from '@angular/forms'; export const IGX_GRID_BASE = new InjectionToken('IgxGridBaseToken'); export const IGX_GRID_SERVICE_BASE = new InjectionToken('IgxGridServiceBaseToken'); @@ -445,6 +446,7 @@ export interface GridType extends IGridDataBindable { processedExpandedFlatData?: any[] | null; processedRecords?: Map; treeGroupArea?: any; + rowFromGroup: FormGroup; activeNodeChange: EventEmitter; gridKeydown: EventEmitter; diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index c5f4e40ac66..8ae6a788933 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -153,6 +153,7 @@ import { IgxGridHeaderComponent } from './headers/grid-header.component'; import { IgxGridFilteringRowComponent } from './filtering/base/grid-filtering-row.component'; import { DefaultDataCloneStrategy, IDataCloneStrategy } from '../data-operations/data-clone-strategy'; import { IgxGridCellComponent } from './cell.component'; +import { FormGroup } from '@angular/forms'; let FAKE_ROW_ID = -1; const DEFAULT_ITEMS_PER_PAGE = 15; @@ -443,6 +444,9 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements @Input() public rowStyles = null; + @Input() + public rowFromGroup = new FormGroup({}); + /** * Gets/Sets the primary key. * diff --git a/projects/igniteui-angular/src/lib/grids/grid-common.module.ts b/projects/igniteui-angular/src/lib/grids/grid-common.module.ts index cb7af853cbf..79bfd18d5d0 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-common.module.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-common.module.ts @@ -41,6 +41,7 @@ import { export * from './common/grid-pipes.module'; import { IgxChipsModule } from '../chips/chips.module'; import { IgxGroupByMetaPipe } from './grouping/group-by-area.directive'; +import { ReactiveFormsModule } from '@angular/forms'; /** * @hidden @@ -118,7 +119,8 @@ import { IgxGroupByMetaPipe } from './grouping/group-by-area.directive'; IgxRowDragModule, IgxPaginatorModule, IgxGridSharedModules, - IgxChipsModule + IgxChipsModule, + ReactiveFormsModule ] }) export class IgxGridCommonModule { } diff --git a/src/app/grid-cellEditing/grid-cellEditing.component.html b/src/app/grid-cellEditing/grid-cellEditing.component.html index 5d2e0d80f67..cae6f4f85e9 100644 --- a/src/app/grid-cellEditing/grid-cellEditing.component.html +++ b/src/app/grid-cellEditing/grid-cellEditing.component.html @@ -3,7 +3,7 @@

Grid with primary key ProductID

- + - + - + From c190a8d310041a2518f165121008e4024a632373 Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 6 Jul 2022 19:19:43 +0300 Subject: [PATCH 003/149] chore(*): Move FormGroup creating to crud service edit row init. Emit event for grid. Add default error template. --- .../src/lib/grids/cell.component.html | 9 +++++++++ .../igniteui-angular/src/lib/grids/cell.component.ts | 8 +------- .../src/lib/grids/common/crud.service.ts | 11 ++++++++++- .../src/lib/grids/common/grid.interface.ts | 2 +- .../src/lib/grids/grid-base.directive.ts | 6 +++--- .../grid-cellEditing/grid-cellEditing.component.html | 2 +- .../grid-cellEditing/grid-cellEditing.component.ts | 4 ++++ 7 files changed, 29 insertions(+), 13 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.html b/projects/igniteui-angular/src/lib/grids/cell.component.html index 7fc1292b7bd..c02bb920edc 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/cell.component.html @@ -162,3 +162,12 @@ + + +
+
+ This is required. +
+
+
\ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 8d9b6f9323b..f0b79a752f7 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -72,7 +72,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT public get formGroup() { - return this.grid.rowFromGroup; + return this.grid.crudService.row?.rowFormGroup; } /** @@ -728,12 +728,6 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT cssProps: {} /* don't disable user-select, etc */ } as HammerOptions); } - // check if control for this field is already added. - const existingControl = this.formGroup.get(this.column.field); - if (!existingControl) { - const control = new FormControl(this.editValue, this.column.validators); - this.formGroup.addControl(this.column.field, control); - } } /** diff --git a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts index c86b848bce9..77551545126 100644 --- a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts +++ b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts @@ -4,13 +4,21 @@ import { IGridEditDoneEventArgs, IGridEditEventArgs, IRowDataEventArgs } from '. import { GridType, RowType } from './grid.interface'; import { Subject } from 'rxjs'; import { copyDescriptors, isEqual } from '../../core/utils'; +import { FormControl, FormGroup } from '@angular/forms'; export class IgxEditRow { public transactionState: any; public state: any; public newData: any; + public rowFormGroup = new FormGroup({}); - constructor(public id: any, public index: number, public data: any, public grid: GridType) { } + constructor(public id: any, public index: number, public data: any, public grid: GridType) { + for (const col of grid.columns) { + const field = col.field; + const control = new FormControl(this.data[field], col.validators); + this.rowFormGroup.addControl(field, control); + } + } public createEditEventArgs(includeNewValue = true, event?: Event): IGridEditEventArgs { const args: IGridEditEventArgs = { @@ -286,6 +294,7 @@ export class IgxRowCrudState extends IgxCellCrudState { if (!this.row || !(this.row.getClassName() === IgxEditRow.name)) { if (!this.row) { this.createRow(this.cell); + this.grid.onFormGroupCreate.emit(this.row.rowFormGroup); } const rowArgs = this.row.createEditEventArgs(false, event); diff --git a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts index cca42477970..36f64794336 100644 --- a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts @@ -447,7 +447,6 @@ export interface GridType extends IGridDataBindable { processedExpandedFlatData?: any[] | null; processedRecords?: Map; treeGroupArea?: any; - rowFromGroup: FormGroup; activeNodeChange: EventEmitter; gridKeydown: EventEmitter; @@ -487,6 +486,7 @@ export interface GridType extends IGridDataBindable { rowDragStart: EventEmitter; rowDragEnd: EventEmitter; rowToggle: EventEmitter; + onFormGroupCreate: EventEmitter; toolbarExporting: EventEmitter; rendered$: Observable; diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index 8ae6a788933..36fd82145a0 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -444,9 +444,6 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements @Input() public rowStyles = null; - @Input() - public rowFromGroup = new FormGroup({}); - /** * Gets/Sets the primary key. * @@ -501,6 +498,9 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements @Output() public cellClick = new EventEmitter(); + @Output() + public onFormGroupCreate = new EventEmitter(); + /** * Emitted when a cell is selected. * diff --git a/src/app/grid-cellEditing/grid-cellEditing.component.html b/src/app/grid-cellEditing/grid-cellEditing.component.html index e4fee6f9f1c..7c60d37d103 100644 --- a/src/app/grid-cellEditing/grid-cellEditing.component.html +++ b/src/app/grid-cellEditing/grid-cellEditing.component.html @@ -3,7 +3,7 @@

Grid with primary key ProductID

- + - + @@ -24,7 +24,7 @@

Grid with primary key ProductID

--> - + diff --git a/src/app/grid-cellEditing/grid-cellEditing.component.ts b/src/app/grid-cellEditing/grid-cellEditing.component.ts index 5098b341d33..7b30cf0a2fd 100644 --- a/src/app/grid-cellEditing/grid-cellEditing.component.ts +++ b/src/app/grid-cellEditing/grid-cellEditing.component.ts @@ -1,10 +1,11 @@ -import { Component, ViewChild } from '@angular/core'; +import { Component, Directive, Input, ViewChild } from '@angular/core'; import { data, dataWithoutPK } from '../shared/data'; import { - IgxGridComponent, GridSelectionMode, IgxDateSummaryOperand, IgxSummaryResult, DisplayDensity + IgxGridComponent, GridSelectionMode, IgxDateSummaryOperand, IgxSummaryResult, DisplayDensity, IgxColumnComponent, IgxColumnValidator } from 'igniteui-angular'; -import { AbstractControl, FormGroup, ValidationErrors, ValidatorFn } from '@angular/forms'; +import { AbstractControl, FormGroup, NG_VALIDATORS, ValidationErrors, Validator, ValidatorFn, Validators } from '@angular/forms'; + export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { return (control: AbstractControl): ValidationErrors | null => { @@ -13,6 +14,20 @@ export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { }; } + @Directive({ + selector: '[appForbiddenName]', + providers: [{provide: NG_VALIDATORS, useExisting: ForbiddenValidatorDirective, multi: true}] + }) + export class ForbiddenValidatorDirective extends IgxColumnValidator { + @Input('appForbiddenName') + public forbiddenName = ''; + + validate(control: AbstractControl): ValidationErrors | null { + return this.forbiddenName ? forbiddenNameValidator(new RegExp(this.forbiddenName, 'i'))(control) + : null; + } + } + @Component({ selector: 'app-grid-cellediting', templateUrl: 'grid-cellEditing.component.html' @@ -57,9 +72,9 @@ export class GridCellEditingComponent { } public formCreateHandler(formGr: FormGroup) { - // add to existing - const prodName = formGr.get('ProductName'); - prodName.addValidators(forbiddenNameValidator(/bob/i)) + // add a validator + // const prodName = formGr.get('ProductName'); + // prodName.addValidators(forbiddenNameValidator(/bob/i)) } public addRow() { From 588e591ff4e74e7f6a27e0de1ea094cd65825393 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 7 Jul 2022 17:24:43 +0300 Subject: [PATCH 009/149] Add custom validation error template. --- .../src/lib/grids/cell.component.html | 169 ++++++------------ .../src/lib/grids/cell.component.ts | 3 + .../src/lib/grids/columns/column.component.ts | 28 ++- .../src/lib/grids/columns/column.module.ts | 3 + .../lib/grids/columns/templates.directive.ts | 7 + .../lib/grids/grid/grid-row.component.html | 1 + .../grid-cellEditing.component.html | 8 +- .../grid-cellEditing.component.ts | 3 + 8 files changed, 103 insertions(+), 119 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.html b/projects/igniteui-angular/src/lib/grids/cell.component.html index 1adc0b45a61..c8ed88b39f0 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/cell.component.html @@ -1,21 +1,11 @@ - {{ grid.resourceStrings.igx_grid_pinned_row_indicator }} + {{ grid.resourceStrings.igx_grid_pinned_row_indicator }} -
{{ - formatter - ? (value | columnFormatter:formatter:rowData:columnData) - : column.dataType === "number" - ? (value | number:column.pipeArgs.digitsInfo:grid.locale) - : (column.dataType === 'date' || column.dataType === 'time' || column.dataType === 'dateTime') - ? (value | date:column.pipeArgs.format:column.pipeArgs.timezone:grid.locale) - : column.dataType === 'currency' - ? (value | currency:currencyCode:column.pipeArgs.display:column.pipeArgs.digitsInfo:grid.locale) - : column.dataType === 'percent' - ? (value | percent:column.pipeArgs.digitsInfo:grid.locale) - : value + " [row]="rowData" [column]="this.column.field" [containerClass]="'igx-grid__td-text'" + [metadata]="searchMetadata">{{ + formatter + ? (value | columnFormatter:formatter:rowData:columnData) + : column.dataType === "number" + ? (value | number:column.pipeArgs.digitsInfo:grid.locale) + : (column.dataType === 'date' || column.dataType === 'time' || column.dataType === 'dateTime') + ? (value | date:column.pipeArgs.format:column.pipeArgs.timezone:grid.locale) + : column.dataType === 'currency' + ? (value | currency:currencyCode:column.pipeArgs.display:column.pipeArgs.digitsInfo:grid.locale) + : column.dataType === 'percent' + ? (value | percent:column.pipeArgs.digitsInfo:grid.locale) + : value }}
- {{ value ? "check" : "close" }} + {{ value ? "check" : "close" }}
-
{{ + (value | percent:column.pipeArgs.digitsInfo:grid.locale) : value" [row]="rowData" [column]="this.column.field" + [containerClass]="'igx-grid__td-text'" [metadata]="searchMetadata">{{ !isEmptyAddRowCell ? value : (column.header || column.field) - }}
+ }}
- + - + - + - + - + - + {{ currencyCodeSymbol }} - - {{ currencyCodeSymbol }} + + {{ currencyCodeSymbol }} - - + + {{ editValue | percent:column.pipeArgs.digitsInfo:grid.locale }} @@ -163,14 +99,15 @@
- -
-
- Validation error. -
- This field is required. -
-
+ + + + + + Validation error. +
+ This field is required. +
\ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 095de8c3380..441c16e766e 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -151,6 +151,9 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT @Input() public cellTemplate: TemplateRef; + @Input() + public cellValidationErrorTemplate: TemplateRef; + @Input() public pinnedIndicator: TemplateRef; diff --git a/projects/igniteui-angular/src/lib/grids/columns/column.component.ts b/projects/igniteui-angular/src/lib/grids/columns/column.component.ts index 9d636a8d804..11881f30e7d 100644 --- a/projects/igniteui-angular/src/lib/grids/columns/column.component.ts +++ b/projects/igniteui-angular/src/lib/grids/columns/column.component.ts @@ -44,7 +44,8 @@ import { IgxCellEditorTemplateDirective, IgxCollapsibleIndicatorTemplateDirective, IgxFilterCellTemplateDirective, - IgxSummaryTemplateDirective + IgxSummaryTemplateDirective, + IgxCellValidationErrorDirective } from './templates.directive'; import { MRLResizeColumnInfo, MRLColumnSizeInfo, IColumnPipeArgs } from './interfaces'; import { DropPosition } from '../moving/moving.service'; @@ -859,6 +860,11 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy */ @ContentChild(IgxCellTemplateDirective, { read: IgxCellTemplateDirective }) protected cellTemplate: IgxCellTemplateDirective; + /** + * @hidden + */ + @ContentChild(IgxCellValidationErrorDirective, { read: IgxCellValidationErrorDirective }) + protected cellValidationErrorTemplate: IgxCellValidationErrorDirective; /** * @hidden */ @@ -1162,6 +1168,19 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy public set summaryTemplate(template: TemplateRef) { this._summaryTemplate = template; } + + /** + * Returns a reference to the `errorTemplate`. + * ```typescript + * let errorTemplate = this.column.errorTemplate; + * ``` + * + * @memberof IgxColumnComponent + */ + public get validationErrorTemplate(): TemplateRef { + return this._errorTemplate; + } + /** * Returns a reference to the `bodyTemplate`. * ```typescript @@ -1613,6 +1632,10 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy * @hidden */ protected _bodyTemplate: TemplateRef; + /** + * @hidden + */ + protected _errorTemplate: TemplateRef; /** * @hidden */ @@ -1734,6 +1757,9 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy if (this.cellTemplate) { this._bodyTemplate = this.cellTemplate.template; } + if (this.cellValidationErrorTemplate) { + this._errorTemplate = this.cellValidationErrorTemplate.template; + } if (this.headTemplate && this.headTemplate.length) { this._headerTemplate = this.headTemplate.toArray()[0].template; } diff --git a/projects/igniteui-angular/src/lib/grids/columns/column.module.ts b/projects/igniteui-angular/src/lib/grids/columns/column.module.ts index b4c70e64ce7..9c1f879f2c9 100644 --- a/projects/igniteui-angular/src/lib/grids/columns/column.module.ts +++ b/projects/igniteui-angular/src/lib/grids/columns/column.module.ts @@ -7,6 +7,7 @@ import { IgxCellFooterTemplateDirective, IgxCellHeaderTemplateDirective, IgxCellTemplateDirective, + IgxCellValidationErrorDirective, IgxCollapsibleIndicatorTemplateDirective, IgxFilterCellTemplateDirective, IgxSummaryTemplateDirective @@ -19,6 +20,7 @@ import { IgxColumnRequiredValidator } from './validators.directive'; IgxFilterCellTemplateDirective, IgxSummaryTemplateDirective, IgxCellTemplateDirective, + IgxCellValidationErrorDirective, IgxCellHeaderTemplateDirective, IgxCellFooterTemplateDirective, IgxCellEditorTemplateDirective, @@ -32,6 +34,7 @@ import { IgxColumnRequiredValidator } from './validators.directive'; IgxFilterCellTemplateDirective, IgxSummaryTemplateDirective, IgxCellTemplateDirective, + IgxCellValidationErrorDirective, IgxCellHeaderTemplateDirective, IgxCellFooterTemplateDirective, IgxCellEditorTemplateDirective, diff --git a/projects/igniteui-angular/src/lib/grids/columns/templates.directive.ts b/projects/igniteui-angular/src/lib/grids/columns/templates.directive.ts index 2297047ff82..574fe7c3948 100644 --- a/projects/igniteui-angular/src/lib/grids/columns/templates.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/columns/templates.directive.ts @@ -16,6 +16,13 @@ export class IgxCellTemplateDirective { constructor(public template: TemplateRef) { } } +@Directive({ + selector: '[igxCellValidationError]' +}) +export class IgxCellValidationErrorDirective { + constructor(public template: TemplateRef) { } +} + @Directive({ selector: '[igxHeader]' }) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-row.component.html b/projects/igniteui-angular/src/lib/grids/grid/grid-row.component.html index dd41b369562..50fdf79cea0 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-row.component.html +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-row.component.html @@ -117,6 +117,7 @@ [visibleColumnIndex]="col.visibleIndex" [value]="data | dataMapper:col.field:grid.pipeTrigger:data[col.field]:col.hasNestedPath" [cellTemplate]="col.bodyTemplate" + [cellValidationErrorTemplate]="col.validationErrorTemplate" [lastSearchInfo]="grid.lastSearchInfo" [active]="isCellActive(col.visibleIndex)" [cellSelectionMode]="grid.cellSelection" diff --git a/src/app/grid-cellEditing/grid-cellEditing.component.html b/src/app/grid-cellEditing/grid-cellEditing.component.html index 4a0cc085a22..b705d5e5f12 100644 --- a/src/app/grid-cellEditing/grid-cellEditing.component.html +++ b/src/app/grid-cellEditing/grid-cellEditing.component.html @@ -24,8 +24,12 @@

Grid with primary key ProductID

--> - - + + +
+ Bob has no soul. +
+
diff --git a/src/app/grid-cellEditing/grid-cellEditing.component.ts b/src/app/grid-cellEditing/grid-cellEditing.component.ts index 7b30cf0a2fd..e966f2e7a3b 100644 --- a/src/app/grid-cellEditing/grid-cellEditing.component.ts +++ b/src/app/grid-cellEditing/grid-cellEditing.component.ts @@ -179,6 +179,9 @@ export class GridCellEditingComponent { const secColumn = this.gridWithoutPK.getColumnByName('OrderDate'); this.gridWithoutPK.moveColumn(column, secColumn); } + public checkValid(cell) { + debugger; + } public updateSelectedCell() { let newValue; const selectedCell = this.gridWithoutPK.selectedCells[0]; From 4ea86ad8e0017764c894d0098d5e9649c22058d7 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 7 Jul 2022 18:16:46 +0300 Subject: [PATCH 010/149] Add isValid argument to edit events. --- .../src/lib/grids/common/crud.service.ts | 5 +++++ .../igniteui-angular/src/lib/grids/common/events.ts | 1 + .../grid-cellEditing.component.html | 8 ++++---- .../grid-cellEditing/grid-cellEditing.component.ts | 13 +++++++++++++ 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts index 4b10ed9a712..0e4db89ccf5 100644 --- a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts +++ b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts @@ -28,6 +28,7 @@ export class IgxEditRow { cancel: false, owner: this.grid, isAddRow: false, + isValid: this.rowFormGroup.valid, event }; if (includeNewValue) { @@ -115,6 +116,7 @@ export class IgxCell { } public createEditEventArgs(includeNewValue = true, event?: Event): IGridEditEventArgs { + const row = this.grid.crudService.row ?? this.row; const args: IGridEditEventArgs = { rowID: this.id.rowID, cellID: this.id, @@ -123,6 +125,7 @@ export class IgxCell { cancel: false, column: this.column, owner: this.grid, + isValid: row.rowFormGroup.get(this.column.field).valid, event }; if (includeNewValue) { @@ -135,6 +138,7 @@ export class IgxCell { const updatedData = this.grid.transactions.enabled ? this.grid.transactions.getAggregatedValue(this.id.rowID, true) : this.rowData; const rowData = updatedData === null ? this.grid.gridAPI.getRowData(this.id.rowID) : updatedData; + const row = this.grid.crudService.row ?? this.row; const args: IGridEditDoneEventArgs = { rowID: this.id.rowID, cellID: this.id, @@ -142,6 +146,7 @@ export class IgxCell { // the only case we use this.rowData directly, is when there is no rowEditing or transactions enabled rowData, oldValue: this.value, + isValid: row.rowFormGroup.get(this.column.field).valid, newValue: value, column: this.column, owner: this.grid, diff --git a/projects/igniteui-angular/src/lib/grids/common/events.ts b/projects/igniteui-angular/src/lib/grids/common/events.ts index 43c7bbeba40..90ff4b25ee9 100644 --- a/projects/igniteui-angular/src/lib/grids/common/events.ts +++ b/projects/igniteui-angular/src/lib/grids/common/events.ts @@ -31,6 +31,7 @@ export interface IGridEditDoneEventArgs extends IBaseEventArgs { column?: ColumnType; owner?: GridType; isAddRow?: boolean; + isValid?: boolean; } export interface IGridEditEventArgs extends CancelableEventArgs, IGridEditDoneEventArgs { diff --git a/src/app/grid-cellEditing/grid-cellEditing.component.html b/src/app/grid-cellEditing/grid-cellEditing.component.html index b705d5e5f12..3975fee852b 100644 --- a/src/app/grid-cellEditing/grid-cellEditing.component.html +++ b/src/app/grid-cellEditing/grid-cellEditing.component.html @@ -3,7 +3,7 @@

Grid with primary key ProductID

- + - +
This name is forbidden. diff --git a/src/app/grid-cellEditing/grid-cellEditing.component.ts b/src/app/grid-cellEditing/grid-cellEditing.component.ts index db90e764c2f..987212fe50f 100644 --- a/src/app/grid-cellEditing/grid-cellEditing.component.ts +++ b/src/app/grid-cellEditing/grid-cellEditing.component.ts @@ -7,26 +7,8 @@ import { import { AbstractControl, FormGroup, NG_VALIDATORS, ValidationErrors, Validator, ValidatorFn, Validators } from '@angular/forms'; -export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { - return (control: AbstractControl): ValidationErrors | null => { - const forbidden = nameRe.test(control.value); - return forbidden ? {forbiddenName: {value: control.value}} : null; - }; - } - @Directive({ - selector: '[appForbiddenName]', - providers: [{provide: NG_VALIDATORS, useExisting: ForbiddenValidatorDirective, multi: true}] - }) - export class ForbiddenValidatorDirective extends IgxColumnValidator { - @Input('appForbiddenName') - public forbiddenName = ''; - - validate(control: AbstractControl): ValidationErrors | null { - return this.forbiddenName ? forbiddenNameValidator(new RegExp(this.forbiddenName, 'i'))(control) - : null; - } - } + @Component({ selector: 'app-grid-cellediting', @@ -71,11 +53,7 @@ export class GridCellEditingComponent { this.selectionMode = GridSelectionMode.multiple; } - public formCreateHandler(formGr: FormGroup) { - // add a validator - // const prodName = formGr.get('ProductName'); - // prodName.addValidators(forbiddenNameValidator(/bob/i)) - } + public cellEdit(evt) { if (!evt.isValid) { diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index c0c48863986..95d218f2d63 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -4,8 +4,16 @@

Grid without transactions

Row edit - + + +
+ This name is required. +
+
+ This name is forbidden. +
+
@@ -14,8 +22,16 @@

Grid with transactions

Row edit - + + +
+ This name is required. +
+
+ This name is forbidden. +
+
diff --git a/src/app/grid-validation/grid-validation.sample.component.ts b/src/app/grid-validation/grid-validation.sample.component.ts index 03eb343fcce..639fb9c029a 100644 --- a/src/app/grid-validation/grid-validation.sample.component.ts +++ b/src/app/grid-validation/grid-validation.sample.component.ts @@ -1,8 +1,30 @@ -import { Component, ViewChild } from '@angular/core'; +import { Component, Directive, ViewChild, Input } from '@angular/core'; import { data } from '../shared/data'; -import { IgxGridComponent, IgxTransactionService, IValidationStatus } from 'igniteui-angular'; -import { FormGroup } from '@angular/forms'; +import { IgxColumnValidator, IgxGridComponent, IgxTransactionService, IValidationStatus } from 'igniteui-angular'; +import { AbstractControl, FormGroup, NG_VALIDATORS, ValidationErrors, ValidatorFn } from '@angular/forms'; + +export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const forbidden = nameRe.test(control.value); + return forbidden ? {forbiddenName: {value: control.value}} : null; + }; + } + +@Directive({ + selector: '[appForbiddenName]', + providers: [{provide: NG_VALIDATORS, useExisting: ForbiddenValidatorDirective, multi: true}] + }) + export class ForbiddenValidatorDirective extends IgxColumnValidator { + @Input('appForbiddenName') + public forbiddenName = ''; + + validate(control: AbstractControl): ValidationErrors | null { + return this.forbiddenName ? forbiddenNameValidator(new RegExp(this.forbiddenName, 'i'))(control) + : null; + } + } + @Component({ selector: 'app-grid-row-edit', From 22767fa760b0bc8fd24b87528cf8ae21568eff14 Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 2 Aug 2022 12:37:10 +0300 Subject: [PATCH 019/149] chore(*): Hide title while error tooltip is showing. --- .../igniteui-angular/src/lib/grids/cell.component.html | 3 ++- projects/igniteui-angular/src/lib/grids/cell.component.ts | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.html b/projects/igniteui-angular/src/lib/grids/cell.component.html index 246d2bc19a7..734fbebcc29 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/cell.component.html @@ -99,7 +99,8 @@ - error + error
diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 2a4e5a2b204..2f56abc6267 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -321,7 +321,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT @HostBinding('attr.title') public get title() { - if (this.editMode || this.cellTemplate) { + if (this.editMode || this.cellTemplate || this.errorShowing) { return ''; } @@ -782,6 +782,12 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT }); } + /** + * @hidden + * @internal + */ + public errorShowing = false; + private toggleErrorTooltip() { const tooltip = this.errorTooltip.toArray()[0]; tooltip.toggle( From 95a176ee6ac81e96e958faa6da663896b5ea6355 Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 3 Aug 2022 13:01:27 +0300 Subject: [PATCH 020/149] chore(*): Add aria attributes for invalid cell. --- .../igniteui-angular/src/lib/grids/cell.component.html | 3 ++- .../igniteui-angular/src/lib/grids/cell.component.ts | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.html b/projects/igniteui-angular/src/lib/grids/cell.component.html index 734fbebcc29..f254abb6b91 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/cell.component.html @@ -101,7 +101,8 @@ error -
+
diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 2f56abc6267..45eab41b230 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -445,8 +445,18 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT public get readonly(): boolean { return !this.editable; } + /** @hidden @internal */ + @HostBinding('attr.aria-describedby') + public get describeBy() { + let id = this.grid.id + '_' + this.column.field; + if (this.isInvalid) { + id += '_' + this.row.index + '_error'; + } + return id; + } @HostBinding('class.igx-grid__td--invalid') + @HostBinding('attr.aria-invalid') public get isInvalid() { const isRowEdit = this.grid.crudService.rowEditing; const isInvalidInEditMode = (isRowEdit && this.row.inEditMode || this.editMode) && this.formGroup?.get(this.column?.field)?.invalid; From ff2d571c73ed7154a7a68fb108f8259def831233 Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 3 Aug 2022 13:16:25 +0300 Subject: [PATCH 021/149] chore(*): Hide tooltip on active node change. --- .../src/lib/grids/cell.component.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 45eab41b230..e1e9237fff1 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -36,6 +36,7 @@ import { ISelectionNode } from './common/types'; import { IgxTooltipDirective } from '../directives/tooltip'; import { AutoPositionStrategy, HorizontalAlignment, VerticalAlignment } from '../services/public_api'; import { IgxIconComponent } from '../icon/icon.component'; +import { first } from 'rxjs/operators'; /** * Providing reference to `IgxGridCellComponent`: @@ -780,7 +781,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT if (this.errorTooltip.length > 0) { // error ocurred this.cdr.detectChanges(); - this.toggleErrorTooltip(); + this.openErrorTooltip(); } this.grid.validationStatusChange.emit( { @@ -798,9 +799,9 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT */ public errorShowing = false; - private toggleErrorTooltip() { + private openErrorTooltip() { const tooltip = this.errorTooltip.toArray()[0]; - tooltip.toggle( + tooltip.open( { target: this.errorIcon.el.nativeElement, closeOnOutsideClick: true, @@ -936,6 +937,10 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT * @internal */ public focusout = () => { + this.closeErrorTooltip(); + } + + private closeErrorTooltip() { const tooltip = this.errorTooltip.toArray()[0]; if (tooltip) { tooltip.close(); @@ -991,7 +996,10 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT if (this.isInvalid) { - this.toggleErrorTooltip(); + this.openErrorTooltip(); + this.grid.activeNodeChange.pipe(first()).subscribe(() => { + this.closeErrorTooltip(); + }); } this.selectionService.primaryButton = true; if (this.cellSelectionMode === GridSelectionMode.multiple && this.selectionService.activeElement) { From 032c5530dcfef3ab9a96ff7287bdd6fc310c1701 Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 3 Aug 2022 15:57:31 +0300 Subject: [PATCH 022/149] chore(*): Move some of the logic in the base transaction service so that validation states can be stored when bath editing is off. --- .../src/lib/grids/cell.component.ts | 6 +++-- .../src/lib/grids/common/crud.service.ts | 3 ++- .../src/lib/grids/common/pipes.ts | 5 ++-- .../services/transaction/base-transaction.ts | 23 +++++++++++++++---- .../lib/services/transaction/transaction.ts | 5 ++++ .../grid-validation.sample.component.html | 3 ++- .../grid-validation.sample.component.ts | 11 +++++++-- 7 files changed, 44 insertions(+), 12 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index e1e9237fff1..be3e804a696 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -460,12 +460,14 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT @HostBinding('attr.aria-invalid') public get isInvalid() { const isRowEdit = this.grid.crudService.rowEditing; - const isInvalidInEditMode = (isRowEdit && this.row.inEditMode || this.editMode) && this.formGroup?.get(this.column?.field)?.invalid; + const isInvalidInEditMode = (isRowEdit && this.row.inEditMode || this.editMode) && + !this.formGroup?.get(this.column?.field)?.pristine && + this.formGroup?.get(this.column?.field)?.invalid; return isInvalidInEditMode || (!!this.validity ? !this.validity.valid : false); } private get validity() { - const transactionLog = this.grid.transactions.getTransactionLog(this.row.key); + const transactionLog = this.grid.transactions.getInvalidTransactionLog(this.row.key); if (transactionLog && transactionLog.length) { const last = transactionLog[transactionLog.length - 1]; const val = last.validity.find(x => x.field === this.column.field); diff --git a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts index 0e4db89ccf5..919f6db20cf 100644 --- a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts +++ b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts @@ -15,7 +15,8 @@ export class IgxEditRow { constructor(public id: any, public index: number, public data: any, public grid: GridType) { for (const col of grid.columns) { const field = col.field; - const control = new FormControl(this.data[field], col.validators); + const control = new FormControl(this.data[field], { updateOn: 'change' }); + control.addValidators(col.validators); this.rowFormGroup.addControl(field, control); } } diff --git a/projects/igniteui-angular/src/lib/grids/common/pipes.ts b/projects/igniteui-angular/src/lib/grids/common/pipes.ts index 2505d27f663..91def176600 100644 --- a/projects/igniteui-angular/src/lib/grids/common/pipes.ts +++ b/projects/igniteui-angular/src/lib/grids/common/pipes.ts @@ -6,6 +6,7 @@ import { IgxAddRow } from './crud.service'; import { IgxSummaryOperand, IgxSummaryResult } from '../summaries/grid-summary'; import { IgxGridRow } from '../grid-public-row'; import { LayoutSampleComponent } from 'src/app/layout/layout.sample'; +import { IgxTransactionService } from '../../services/public_api'; interface GridStyleCSSProperty { [prop: string]: any; @@ -299,11 +300,11 @@ export class IgxGridTransactionStatePipe implements PipeTransform { public transform(row_id: any, field: string, rowEditable: boolean, transactions: any, _: any, __: any, ___: any) { if (rowEditable) { const rowCurrentState = transactions.getAggregatedValue(row_id, false); - if (rowCurrentState) { + if (rowCurrentState && transactions instanceof IgxTransactionService) { const value = resolveNestedPath(rowCurrentState, field); return value !== undefined && value !== null; } - } else { + } else if (transactions instanceof IgxTransactionService) { const transaction = transactions.getState(row_id); const value = resolveNestedPath(transaction?.value ?? {}, field); return transaction && transaction.value && (value || value === 0 || value === false); diff --git a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts index 25f31847855..87bed760aef 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts @@ -111,7 +111,20 @@ export class IgxBaseTransactionService i /** * @inheritdoc */ - public commit(_data: any[], _id?: any): void { } + public commit(_data: any[], _id?: any): void { + this.clear(_id); + } + + /** + * @inheritdoc + */ + public getInvalidTransactionLog(id?: any) { + let pending = [...this._pendingTransactions]; + if (id !== undefined) { + pending = pending.filter(t => t.id === id); + } + return pending.filter(x => x.validity.some(x => x.valid === false)); + } /** * @inheritdoc @@ -132,9 +145,11 @@ export class IgxBaseTransactionService i * @inheritdoc */ public endPending(_commit: boolean): void { - this._isPending = false; - this._pendingStates.clear(); - this._pendingTransactions = []; + if (_commit) { + this._isPending = false; + this._pendingStates.clear(); + this._pendingTransactions = []; + } } diff --git a/projects/igniteui-angular/src/lib/services/transaction/transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/transaction.ts index 2dffe31e178..b5204e297d9 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/transaction.ts @@ -104,6 +104,11 @@ export interface TransactionService { */ undo(): void; + /** + * Returns invalid transactions. + */ + getInvalidTransactionLog(id?: any): T[]; + /** * Applies the last undone transaction if any */ diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index 95d218f2d63..fd52cc19bf5 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -16,6 +16,7 @@

Grid without transactions

+

Grid with transactions

@@ -34,5 +35,5 @@

Grid with transactions

- +
\ No newline at end of file diff --git a/src/app/grid-validation/grid-validation.sample.component.ts b/src/app/grid-validation/grid-validation.sample.component.ts index 639fb9c029a..d83e7f5ace7 100644 --- a/src/app/grid-validation/grid-validation.sample.component.ts +++ b/src/app/grid-validation/grid-validation.sample.component.ts @@ -46,8 +46,11 @@ export class GridValidationSampleComponent { @ViewChild('gridTransactions', {read: IgxGridComponent }) public gridWithTransaction: IgxGridComponent; - public commit() { - const invalidTransactions = this.gridWithTransaction.transactions.getTransactionLog().filter(x => x.validity.some(x => x.valid === false)); + @ViewChild('gridNoTransactions', {read: IgxGridComponent }) + public gridNoTransactions: IgxGridComponent; + + public commitWithTransactions() { + const invalidTransactions = this.gridWithTransaction.transactions.getInvalidTransactionLog(); if (invalidTransactions.length > 0) { if (confirm('There are invalid values about to be submitted. Do you want to continue')) { this.gridWithTransaction.transactions.commit(this.transactionData); @@ -57,6 +60,10 @@ export class GridValidationSampleComponent { } } + public commitNoTransactions() { + this.gridNoTransactions.transactions.commit([]); + } + public cellEdit(evt) { // can cancel if there are validation errors if (!evt.isValid && !this.rowEditNoTransactions) { From 389d1af506d8a8f09b9b84b722d1f21fc7ac542d Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 3 Aug 2022 16:58:01 +0300 Subject: [PATCH 023/149] chore(*): Add grid validation trigger. --- .../src/lib/grids/cell.component.ts | 1 + .../src/lib/grids/common/crud.service.ts | 6 ++++-- .../igniteui-angular/src/lib/grids/common/enums.ts | 1 + .../src/lib/grids/common/grid.interface.ts | 3 ++- .../src/lib/grids/grid-base.directive.ts | 14 +++++++++++++- .../grid-validation.sample.component.html | 2 +- 6 files changed, 22 insertions(+), 5 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index be3e804a696..97e04670f85 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -1073,6 +1073,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT if (this.editable && editMode && !this.intRow.deleted) { if (editableCell) { + this.grid.tbody.nativeElement.focus({ preventScroll: true }); editableArgs = this.grid.crudService.updateCell(false, event); /* This check is related with the following issue #6517: diff --git a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts index 919f6db20cf..490067a0bff 100644 --- a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts +++ b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts @@ -15,7 +15,7 @@ export class IgxEditRow { constructor(public id: any, public index: number, public data: any, public grid: GridType) { for (const col of grid.columns) { const field = col.field; - const control = new FormControl(this.data[field], { updateOn: 'change' }); + const control = new FormControl(this.data[field], { updateOn: grid.validationTrigger }); control.addValidators(col.validators); this.rowFormGroup.addControl(field, control); } @@ -140,6 +140,8 @@ export class IgxCell { this.grid.transactions.getAggregatedValue(this.id.rowID, true) : this.rowData; const rowData = updatedData === null ? this.grid.gridAPI.getRowData(this.id.rowID) : updatedData; const row = this.grid.crudService.row ?? this.row; + const formControl = row.rowFormGroup.get(this.column.field); + formControl.updateValueAndValidity(); const args: IGridEditDoneEventArgs = { rowID: this.id.rowID, cellID: this.id, @@ -147,7 +149,7 @@ export class IgxCell { // the only case we use this.rowData directly, is when there is no rowEditing or transactions enabled rowData, oldValue: this.value, - isValid: row.rowFormGroup.get(this.column.field).valid, + isValid: formControl.valid, newValue: value, column: this.column, owner: this.grid, diff --git a/projects/igniteui-angular/src/lib/grids/common/enums.ts b/projects/igniteui-angular/src/lib/grids/common/enums.ts index 4b8f21ae4cd..e77a372a90e 100644 --- a/projects/igniteui-angular/src/lib/grids/common/enums.ts +++ b/projects/igniteui-angular/src/lib/grids/common/enums.ts @@ -19,6 +19,7 @@ export const GridSummaryCalculationMode = mkenum({ }); export type GridSummaryCalculationMode = (typeof GridSummaryCalculationMode)[keyof typeof GridSummaryCalculationMode]; +export type GridValidationTrigger = 'change' | 'blur' | 'submit'; export type GridKeydownTargetType = 'dataCell' | diff --git a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts index 1dda2039f39..15f66ac34ba 100644 --- a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts @@ -1,4 +1,4 @@ -import { FilterMode, GridPagingMode, GridSelectionMode, GridSummaryCalculationMode, GridSummaryPosition } from './enums'; +import { FilterMode, GridPagingMode, GridSelectionMode, GridSummaryCalculationMode, GridSummaryPosition, GridValidationTrigger } from './enums'; import { ISearchInfo, IGridCellEventArgs, IRowSelectionEventArgs, IColumnSelectionEventArgs, IGridEditEventArgs, IPinColumnCancellableEventArgs, IColumnVisibilityChangedEventArgs, IColumnVisibilityChangingEventArgs, @@ -367,6 +367,7 @@ export interface GridType extends IGridDataBindable { _baseFontSize?: number; scrollSize: number; + validationTrigger: GridValidationTrigger; pinning: IPinningConfig; expansionStates: Map; parentVirtDir: any; diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index 044d3f0a81c..207a02bfb94 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -91,7 +91,8 @@ import { FilterMode, ColumnPinningPosition, RowPinningPosition, - GridPagingMode + GridPagingMode, + GridValidationTrigger } from './common/enums'; import { IGridCellEventArgs, @@ -1700,6 +1701,17 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements return this._rowDrag && this.hasVisibleColumns; } + /** + * Gets/Sets the trigger for validators used when editing the grid. + * + * @example + * ```html + * + * ``` + */ + @Input() + public validationTrigger: GridValidationTrigger = 'change'; + public set rowDraggable(val: boolean) { this._rowDrag = val; diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index fd52cc19bf5..b30266417e3 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -2,7 +2,7 @@

Grid without transactions

Row edit - From 4312eaa577bcdb14a3613f45c8bb91605548bdb3 Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 9 Aug 2022 12:14:49 +0300 Subject: [PATCH 024/149] chore(*): Minor refactor. --- .../src/lib/grids/cell.component.ts | 20 +++++++++---------- .../grid-validation.sample.component.html | 4 ++++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 97e04670f85..a45c9a07dc4 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -37,6 +37,7 @@ import { IgxTooltipDirective } from '../directives/tooltip'; import { AutoPositionStrategy, HorizontalAlignment, VerticalAlignment } from '../services/public_api'; import { IgxIconComponent } from '../icon/icon.component'; import { first } from 'rxjs/operators'; +import { FormGroup } from '@angular/forms'; /** * Providing reference to `IgxGridCellComponent`: @@ -85,7 +86,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT public column: ColumnType; - public get formGroup() { + public get formGroup() : FormGroup { const isRowEdit = this.grid.crudService.rowEditing; const formGroup = isRowEdit ? this.grid.crudService.row?.rowFormGroup : this.grid.crudService.cell?.row.rowFormGroup; return formGroup || this.validity?.formGroup; @@ -460,18 +461,17 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT @HostBinding('attr.aria-invalid') public get isInvalid() { const isRowEdit = this.grid.crudService.rowEditing; - const isInvalidInEditMode = (isRowEdit && this.row.inEditMode || this.editMode) && - !this.formGroup?.get(this.column?.field)?.pristine && - this.formGroup?.get(this.column?.field)?.invalid; - return isInvalidInEditMode || (!!this.validity ? !this.validity.valid : false); + if (isRowEdit && this.row.inEditMode || this.editMode) { + return this.formGroup?.get(this.column?.field)?.invalid; + } else { + return !!this.validity ? !this.validity.valid : false; + } } private get validity() { - const transactionLog = this.grid.transactions.getInvalidTransactionLog(this.row.key); - if (transactionLog && transactionLog.length) { - const last = transactionLog[transactionLog.length - 1]; - const val = last.validity.find(x => x.field === this.column.field); - return val; + const state = this.grid.transactions.getState(this.row.key); + if (state && state.validity && state.validity.some(x => x.valid === false)) { + return state.validity.find(x => x.field === this.column.field); } } diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index b30266417e3..b1908dd632c 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -15,6 +15,10 @@

Grid without transactions

+ + + + From 50f81d3382c69b6d6f9b35d4f5d6b7f411f5ffe9 Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 9 Aug 2022 12:24:19 +0300 Subject: [PATCH 025/149] chore(*): fix minor issue with error message showing after cell enters/exits viewport. --- projects/igniteui-angular/src/lib/grids/cell.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index a45c9a07dc4..0bbdc8f99a0 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -778,9 +778,9 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT } - public ngAfterViewInit(){ + public ngAfterViewInit() { this.errorTooltip.changes.subscribe(() => { - if (this.errorTooltip.length > 0) { + if (this.errorTooltip.length > 0 && this.active) { // error ocurred this.cdr.detectChanges(); this.openErrorTooltip(); From 50395d7d3c40a0073702cd2800dc4d3f129df333 Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 10 Aug 2022 17:15:18 +0300 Subject: [PATCH 026/149] chore(*): Scrap submit trigger. --- projects/igniteui-angular/src/lib/grids/common/enums.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/common/enums.ts b/projects/igniteui-angular/src/lib/grids/common/enums.ts index e77a372a90e..ff2a2aeb94f 100644 --- a/projects/igniteui-angular/src/lib/grids/common/enums.ts +++ b/projects/igniteui-angular/src/lib/grids/common/enums.ts @@ -19,7 +19,7 @@ export const GridSummaryCalculationMode = mkenum({ }); export type GridSummaryCalculationMode = (typeof GridSummaryCalculationMode)[keyof typeof GridSummaryCalculationMode]; -export type GridValidationTrigger = 'change' | 'blur' | 'submit'; +export type GridValidationTrigger = 'change' | 'blur' ; export type GridKeydownTargetType = 'dataCell' | From debc88d176afc156af1fee52b823c7341a29dd63 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 11 Aug 2022 12:21:24 +0300 Subject: [PATCH 027/149] chore(*): Add the remaining column validators extending the default angular directives. --- .../src/lib/grids/cell.component.html | 21 +++- .../src/lib/grids/columns/column.module.ts | 14 ++- .../lib/grids/columns/validators.directive.ts | 102 +++++++++++++++++- .../grid-validation.sample.component.html | 11 +- 4 files changed, 135 insertions(+), 13 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.html b/projects/igniteui-angular/src/lib/grids/cell.component.html index f254abb6b91..fad7c6d7cbc 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/cell.component.html @@ -110,8 +110,25 @@ - Validation error.
- This field is required. + Field is required. +
+
+ Field must be at least {{formGroup.get(column.field).errors.minlength.requiredLength}} characters long. +
+
+ Field must be at most {{formGroup.get(column.field).errors.maxlength.requiredLength}} characters long. +
+
+ Field must be more than {{formGroup.get(column.field).errors.min.min}}. +
+
+ Field must be less than {{formGroup.get(column.field).errors.max.max}}. +
+
+ Field must contain a valid email address. +
+
+ Field must match pattern {{formGroup.get(column.field).errors.pattern.requiredPattern}}.
\ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/grids/columns/column.module.ts b/projects/igniteui-angular/src/lib/grids/columns/column.module.ts index 9c1f879f2c9..0cc490bf20b 100644 --- a/projects/igniteui-angular/src/lib/grids/columns/column.module.ts +++ b/projects/igniteui-angular/src/lib/grids/columns/column.module.ts @@ -12,11 +12,17 @@ import { IgxFilterCellTemplateDirective, IgxSummaryTemplateDirective } from './templates.directive'; -import { IgxColumnRequiredValidator } from './validators.directive'; +import { IgxColumMaxLengthValidator, IgxColumnEmailValidator, IgxColumnMaxValidator, IgxColumnMinLengthValidator, IgxColumnMinValidator, IgxColumnRequiredValidator, IgxColumPatternValidator } from './validators.directive'; @NgModule({ declarations: [ IgxColumnRequiredValidator, + IgxColumnMinValidator, + IgxColumnMaxValidator, + IgxColumnMinLengthValidator, + IgxColumMaxLengthValidator, + IgxColumnEmailValidator, + IgxColumPatternValidator, IgxFilterCellTemplateDirective, IgxSummaryTemplateDirective, IgxCellTemplateDirective, @@ -31,6 +37,12 @@ import { IgxColumnRequiredValidator } from './validators.directive'; ], exports: [ IgxColumnRequiredValidator, + IgxColumnMinValidator, + IgxColumnMaxValidator, + IgxColumnMinLengthValidator, + IgxColumMaxLengthValidator, + IgxColumnEmailValidator, + IgxColumPatternValidator, IgxFilterCellTemplateDirective, IgxSummaryTemplateDirective, IgxCellTemplateDirective, diff --git a/projects/igniteui-angular/src/lib/grids/columns/validators.directive.ts b/projects/igniteui-angular/src/lib/grids/columns/validators.directive.ts index 5704892c3eb..6afff512012 100644 --- a/projects/igniteui-angular/src/lib/grids/columns/validators.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/columns/validators.directive.ts @@ -1,5 +1,5 @@ import { Directive, forwardRef } from '@angular/core'; -import { RequiredValidator, NG_VALIDATORS, Validators, ValidationErrors } from '@angular/forms'; +import { RequiredValidator, NG_VALIDATORS, Validators, ValidationErrors, MinValidator, MaxValidator, EmailValidator, MinLengthValidator, MaxLengthValidator, PatternValidator } from '@angular/forms'; import { IgxColumnComponent } from './column.component'; @@ -7,6 +7,7 @@ import { IgxColumnComponent } from './column.component'; providers: [{ provide: NG_VALIDATORS, useExisting: IgxColumnValidator, multi: true }] }) export abstract class IgxColumnValidator extends Validators { + public value: any; constructor(private column?: IgxColumnComponent) { super(); if (column) { @@ -31,4 +32,103 @@ export class IgxColumnRequiredValidator extends RequiredValidator { super(); column.validators.push(this); } +} + +@Directive({ + selector: + 'igx-column[min]', + providers: [{ + provide: NG_VALIDATORS, + useExisting: forwardRef(() => MinValidator), + multi: true + }] +}) +export class IgxColumnMinValidator extends MinValidator { + constructor(private column: IgxColumnComponent) { + super(); + column.validators.push(this); + } +} + + +@Directive({ + selector: + 'igx-column[max]', + providers: [{ + provide: NG_VALIDATORS, + useExisting: forwardRef(() => MaxValidator), + multi: true + }] +}) +export class IgxColumnMaxValidator extends MaxValidator { + constructor(private column: IgxColumnComponent) { + super(); + column.validators.push(this); + } +} + + +@Directive({ + selector: + 'igx-column[email]', + providers: [{ + provide: NG_VALIDATORS, + useExisting: forwardRef(() => EmailValidator), + multi: true + }] +}) +export class IgxColumnEmailValidator extends EmailValidator { + constructor(private column: IgxColumnComponent) { + super(); + column.validators.push(this); + } +} + + +@Directive({ + selector: + 'igx-column[minlength]', + providers: [{ + provide: NG_VALIDATORS, + useExisting: forwardRef(() => MinLengthValidator), + multi: true + }] +}) +export class IgxColumnMinLengthValidator extends MinLengthValidator { + constructor(private column: IgxColumnComponent) { + super(); + column.validators.push(this); + } +} + +@Directive({ + selector: + 'igx-column[maxlength]', + providers: [{ + provide: NG_VALIDATORS, + useExisting: forwardRef(() => MaxLengthValidator), + multi: true + }] +}) +export class IgxColumMaxLengthValidator extends MaxLengthValidator { + constructor(private column: IgxColumnComponent) { + super(); + column.validators.push(this); + } +} + +@Directive({ + selector: + 'igx-column[pattern]', + providers: [{ + provide: NG_VALIDATORS, + useExisting: forwardRef(() => PatternValidator), + multi: true + }] +}) +export class IgxColumPatternValidator extends PatternValidator { + constructor(private column: IgxColumnComponent) { + super(); + column.validators.push(this); + } } \ No newline at end of file diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index b1908dd632c..1b053f9a5cf 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -2,18 +2,11 @@

Grid without transactions

Row edit - - -
- This name is required. -
-
- This name is forbidden. -
-
+
From 4a393c1f1482d6d4ecee1746333e31ef3703ebf4 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 11 Aug 2022 14:45:19 +0300 Subject: [PATCH 028/149] chore(*): Move default error messages as locale resources for localization. --- .../src/i18n/BG/grid-resources.ts | 9 ++++++++- .../src/i18n/CS/grid-resources.ts | 9 ++++++++- .../src/i18n/DA/grid-resources.ts | 9 ++++++++- .../src/i18n/DE/grid-resources.ts | 9 ++++++++- .../src/i18n/ES/grid-resources.ts | 9 ++++++++- .../src/i18n/FR/grid-resources.ts | 9 ++++++++- .../src/i18n/HU/grid-resources.ts | 9 ++++++++- .../src/i18n/IT/grid-resources.ts | 9 ++++++++- .../src/i18n/JA/grid-resources.ts | 9 ++++++++- .../src/i18n/KO/grid-resources.ts | 9 ++++++++- .../src/i18n/NB/grid-resources.ts | 9 ++++++++- .../src/i18n/NL/grid-resources.ts | 9 ++++++++- .../src/i18n/PL/grid-resources.ts | 9 ++++++++- .../src/i18n/PT/grid-resources.ts | 9 ++++++++- .../src/i18n/RO/grid-resources.ts | 9 ++++++++- .../src/i18n/SV/grid-resources.ts | 9 ++++++++- .../src/i18n/TR/grid-resources.ts | 9 ++++++++- .../src/i18n/ZH-HANS/grid-resources.ts | 9 ++++++++- .../src/i18n/ZH-HANT/grid-resources.ts | 9 ++++++++- .../src/lib/core/i18n/grid-resources.ts | 14 ++++++++++++++ .../src/lib/grids/cell.component.html | 14 +++++++------- .../grid-validation.sample.component.html | 2 +- 22 files changed, 174 insertions(+), 27 deletions(-) diff --git a/projects/igniteui-angular-i18n/src/i18n/BG/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/BG/grid-resources.ts index f760efbd355..e5d7ee1898d 100644 --- a/projects/igniteui-angular-i18n/src/i18n/BG/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/BG/grid-resources.ts @@ -157,7 +157,14 @@ const GridResourceStringsBG_: ExpandRequire = { igx_grid_pivot_selector_columns: 'Колони', igx_grid_pivot_selector_values: 'Стойнoсти', igx_grid_pivot_selector_panel_empty: 'Привлачи тук', - igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', + igx_grid_required_validation_error: 'Field is required.', + igx_grid_min_validation_error: 'Field must be more than {0}', + igx_grid_max_validation_error: 'Field must be less than {0}', + igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', + igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', + igx_grid_email_validation_error: 'Field must contain a valid email address.', + igx_grid_pattern_validation_error: 'Field must match pattern {0}' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/CS/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/CS/grid-resources.ts index 129dcc00215..32bc4f44a7a 100644 --- a/projects/igniteui-angular-i18n/src/i18n/CS/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/CS/grid-resources.ts @@ -157,7 +157,14 @@ const GridResourceStringsCS_: ExpandRequire = { igx_grid_pivot_selector_columns: 'Columns', igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', - igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', + igx_grid_required_validation_error: 'Field is required.', + igx_grid_min_validation_error: 'Field must be more than {0}', + igx_grid_max_validation_error: 'Field must be less than {0}', + igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', + igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', + igx_grid_email_validation_error: 'Field must contain a valid email address.', + igx_grid_pattern_validation_error: 'Field must match pattern {0}' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/DA/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/DA/grid-resources.ts index 41cdf64f7e6..31cfaaee5eb 100644 --- a/projects/igniteui-angular-i18n/src/i18n/DA/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/DA/grid-resources.ts @@ -157,7 +157,14 @@ const GridResourceStringsDA_: ExpandRequire = { igx_grid_pivot_selector_columns: 'Columns', igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', - igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', + igx_grid_required_validation_error: 'Field is required.', + igx_grid_min_validation_error: 'Field must be more than {0}', + igx_grid_max_validation_error: 'Field must be less than {0}', + igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', + igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', + igx_grid_email_validation_error: 'Field must contain a valid email address.', + igx_grid_pattern_validation_error: 'Field must match pattern {0}' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/DE/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/DE/grid-resources.ts index aeedfcc9e49..25727aba6db 100644 --- a/projects/igniteui-angular-i18n/src/i18n/DE/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/DE/grid-resources.ts @@ -157,7 +157,14 @@ const GridResourceStringsDE_: ExpandRequire = { igx_grid_pivot_selector_columns: 'Columns', igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', - igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', + igx_grid_required_validation_error: 'Field is required.', + igx_grid_min_validation_error: 'Field must be more than {0}', + igx_grid_max_validation_error: 'Field must be less than {0}', + igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', + igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', + igx_grid_email_validation_error: 'Field must contain a valid email address.', + igx_grid_pattern_validation_error: 'Field must match pattern {0}' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/ES/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/ES/grid-resources.ts index 86f78e5e2dd..f437cc1b566 100644 --- a/projects/igniteui-angular-i18n/src/i18n/ES/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/ES/grid-resources.ts @@ -157,7 +157,14 @@ const GridResourceStringsES_: ExpandRequire = { igx_grid_pivot_selector_columns: 'Columns', igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', - igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', + igx_grid_required_validation_error: 'Field is required.', + igx_grid_min_validation_error: 'Field must be more than {0}', + igx_grid_max_validation_error: 'Field must be less than {0}', + igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', + igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', + igx_grid_email_validation_error: 'Field must contain a valid email address.', + igx_grid_pattern_validation_error: 'Field must match pattern {0}' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/FR/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/FR/grid-resources.ts index 036a12a1003..7d1c5a0e9ac 100644 --- a/projects/igniteui-angular-i18n/src/i18n/FR/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/FR/grid-resources.ts @@ -157,7 +157,14 @@ const GridResourceStringsFR_: ExpandRequire = { igx_grid_pivot_selector_columns: 'Columns', igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', - igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', + igx_grid_required_validation_error: 'Field is required.', + igx_grid_min_validation_error: 'Field must be more than {0}', + igx_grid_max_validation_error: 'Field must be less than {0}', + igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', + igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', + igx_grid_email_validation_error: 'Field must contain a valid email address.', + igx_grid_pattern_validation_error: 'Field must match pattern {0}' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/HU/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/HU/grid-resources.ts index 3e358e9bddc..68af042df71 100644 --- a/projects/igniteui-angular-i18n/src/i18n/HU/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/HU/grid-resources.ts @@ -158,7 +158,14 @@ const GridResourceStringsHU_: ExpandRequire = { igx_grid_pivot_selector_columns: 'Columns', igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', - igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', + igx_grid_required_validation_error: 'Field is required.', + igx_grid_min_validation_error: 'Field must be more than {0}', + igx_grid_max_validation_error: 'Field must be less than {0}', + igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', + igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', + igx_grid_email_validation_error: 'Field must contain a valid email address.', + igx_grid_pattern_validation_error: 'Field must match pattern {0}' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/IT/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/IT/grid-resources.ts index 1c27e425c5f..455a6acbb06 100644 --- a/projects/igniteui-angular-i18n/src/i18n/IT/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/IT/grid-resources.ts @@ -157,7 +157,14 @@ const GridResourceStringsIT_: ExpandRequire = { igx_grid_pivot_selector_columns: 'Columns', igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', - igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', + igx_grid_required_validation_error: 'Field is required.', + igx_grid_min_validation_error: 'Field must be more than {0}', + igx_grid_max_validation_error: 'Field must be less than {0}', + igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', + igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', + igx_grid_email_validation_error: 'Field must contain a valid email address.', + igx_grid_pattern_validation_error: 'Field must match pattern {0}' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/JA/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/JA/grid-resources.ts index d9d581db415..180819e4d4f 100644 --- a/projects/igniteui-angular-i18n/src/i18n/JA/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/JA/grid-resources.ts @@ -157,7 +157,14 @@ const GridResourceStringsJA_: ExpandRequire = { igx_grid_pivot_selector_columns: 'Columns', igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', - igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', + igx_grid_required_validation_error: 'Field is required.', + igx_grid_min_validation_error: 'Field must be more than {0}', + igx_grid_max_validation_error: 'Field must be less than {0}', + igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', + igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', + igx_grid_email_validation_error: 'Field must contain a valid email address.', + igx_grid_pattern_validation_error: 'Field must match pattern {0}' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/KO/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/KO/grid-resources.ts index 420b0d1e057..8975194a9c7 100644 --- a/projects/igniteui-angular-i18n/src/i18n/KO/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/KO/grid-resources.ts @@ -157,7 +157,14 @@ const GridResourceStringsKO_: ExpandRequire = { igx_grid_pivot_selector_columns: 'Columns', igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', - igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', + igx_grid_required_validation_error: 'Field is required.', + igx_grid_min_validation_error: 'Field must be more than {0}', + igx_grid_max_validation_error: 'Field must be less than {0}', + igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', + igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', + igx_grid_email_validation_error: 'Field must contain a valid email address.', + igx_grid_pattern_validation_error: 'Field must match pattern {0}' }; diff --git a/projects/igniteui-angular-i18n/src/i18n/NB/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/NB/grid-resources.ts index 8c51b9f9740..510080b6067 100644 --- a/projects/igniteui-angular-i18n/src/i18n/NB/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/NB/grid-resources.ts @@ -157,7 +157,14 @@ const GridResourceStringsNB_: ExpandRequire = { igx_grid_pivot_selector_columns: 'Columns', igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', - igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', + igx_grid_required_validation_error: 'Field is required.', + igx_grid_min_validation_error: 'Field must be more than {0}', + igx_grid_max_validation_error: 'Field must be less than {0}', + igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', + igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', + igx_grid_email_validation_error: 'Field must contain a valid email address.', + igx_grid_pattern_validation_error: 'Field must match pattern {0}' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/NL/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/NL/grid-resources.ts index 9873f5d9f53..11fd0cb55ad 100644 --- a/projects/igniteui-angular-i18n/src/i18n/NL/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/NL/grid-resources.ts @@ -157,7 +157,14 @@ const GridResourceStringsNL_: ExpandRequire = { igx_grid_pivot_selector_columns: 'Columns', igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', - igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', + igx_grid_required_validation_error: 'Field is required.', + igx_grid_min_validation_error: 'Field must be more than {0}', + igx_grid_max_validation_error: 'Field must be less than {0}', + igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', + igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', + igx_grid_email_validation_error: 'Field must contain a valid email address.', + igx_grid_pattern_validation_error: 'Field must match pattern {0}' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/PL/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/PL/grid-resources.ts index 58024ad3c65..f2cf9bd038a 100644 --- a/projects/igniteui-angular-i18n/src/i18n/PL/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/PL/grid-resources.ts @@ -157,7 +157,14 @@ const GridResourceStringsPL_: ExpandRequire = { igx_grid_pivot_selector_columns: 'Columns', igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', - igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', + igx_grid_required_validation_error: 'Field is required.', + igx_grid_min_validation_error: 'Field must be more than {0}', + igx_grid_max_validation_error: 'Field must be less than {0}', + igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', + igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', + igx_grid_email_validation_error: 'Field must contain a valid email address.', + igx_grid_pattern_validation_error: 'Field must match pattern {0}' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/PT/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/PT/grid-resources.ts index 2523c2cd6a5..e0b8fe4430c 100644 --- a/projects/igniteui-angular-i18n/src/i18n/PT/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/PT/grid-resources.ts @@ -157,7 +157,14 @@ const GridResourceStringsPT_: ExpandRequire = { igx_grid_pivot_selector_columns: 'Columns', igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', - igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', + igx_grid_required_validation_error: 'Field is required.', + igx_grid_min_validation_error: 'Field must be more than {0}', + igx_grid_max_validation_error: 'Field must be less than {0}', + igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', + igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', + igx_grid_email_validation_error: 'Field must contain a valid email address.', + igx_grid_pattern_validation_error: 'Field must match pattern {0}' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/RO/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/RO/grid-resources.ts index 6af7e368e91..f0ea18aab9a 100644 --- a/projects/igniteui-angular-i18n/src/i18n/RO/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/RO/grid-resources.ts @@ -157,7 +157,14 @@ const GridResourceStringsRO_: ExpandRequire = { igx_grid_pivot_selector_columns: 'Columns', igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', - igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', + igx_grid_required_validation_error: 'Field is required.', + igx_grid_min_validation_error: 'Field must be more than {0}', + igx_grid_max_validation_error: 'Field must be less than {0}', + igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', + igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', + igx_grid_email_validation_error: 'Field must contain a valid email address.', + igx_grid_pattern_validation_error: 'Field must match pattern {0}' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/SV/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/SV/grid-resources.ts index d5dc7b18c65..574e09a4d4c 100644 --- a/projects/igniteui-angular-i18n/src/i18n/SV/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/SV/grid-resources.ts @@ -157,7 +157,14 @@ const GridResourceStringsSV_: ExpandRequire = { igx_grid_pivot_selector_columns: 'Columns', igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', - igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', + igx_grid_required_validation_error: 'Field is required.', + igx_grid_min_validation_error: 'Field must be more than {0}', + igx_grid_max_validation_error: 'Field must be less than {0}', + igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', + igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', + igx_grid_email_validation_error: 'Field must contain a valid email address.', + igx_grid_pattern_validation_error: 'Field must match pattern {0}' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/TR/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/TR/grid-resources.ts index 64bc33df683..4243dee6bae 100644 --- a/projects/igniteui-angular-i18n/src/i18n/TR/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/TR/grid-resources.ts @@ -157,7 +157,14 @@ const GridResourceStringsTR_: ExpandRequire = { igx_grid_pivot_selector_columns: 'Columns', igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', - igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', + igx_grid_required_validation_error: 'Field is required.', + igx_grid_min_validation_error: 'Field must be more than {0}', + igx_grid_max_validation_error: 'Field must be less than {0}', + igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', + igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', + igx_grid_email_validation_error: 'Field must contain a valid email address.', + igx_grid_pattern_validation_error: 'Field must match pattern {0}' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/ZH-HANS/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/ZH-HANS/grid-resources.ts index a3921250eb6..47618eb57c9 100644 --- a/projects/igniteui-angular-i18n/src/i18n/ZH-HANS/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/ZH-HANS/grid-resources.ts @@ -157,7 +157,14 @@ const GridResourceStringsZHHANS_: ExpandRequire = { igx_grid_pivot_selector_columns: 'Columns', igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', - igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', + igx_grid_required_validation_error: 'Field is required.', + igx_grid_min_validation_error: 'Field must be more than {0}', + igx_grid_max_validation_error: 'Field must be less than {0}', + igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', + igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', + igx_grid_email_validation_error: 'Field must contain a valid email address.', + igx_grid_pattern_validation_error: 'Field must match pattern {0}' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/ZH-HANT/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/ZH-HANT/grid-resources.ts index dc233d98546..05a00101b5b 100644 --- a/projects/igniteui-angular-i18n/src/i18n/ZH-HANT/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/ZH-HANT/grid-resources.ts @@ -157,7 +157,14 @@ const GridResourceStringsZHHANT_: ExpandRequire = { igx_grid_pivot_selector_columns: 'Columns', igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', - igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.' + igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', + igx_grid_required_validation_error: 'Field is required.', + igx_grid_min_validation_error: 'Field must be more than {0}', + igx_grid_max_validation_error: 'Field must be less than {0}', + igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', + igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', + igx_grid_email_validation_error: 'Field must contain a valid email address.', + igx_grid_pattern_validation_error: 'Field must match pattern {0}' }; /** diff --git a/projects/igniteui-angular/src/lib/core/i18n/grid-resources.ts b/projects/igniteui-angular/src/lib/core/i18n/grid-resources.ts index e3fce5f7bfd..c86557e9ecf 100644 --- a/projects/igniteui-angular/src/lib/core/i18n/grid-resources.ts +++ b/projects/igniteui-angular/src/lib/core/i18n/grid-resources.ts @@ -155,6 +155,13 @@ export interface IGridResourceStrings { igx_grid_pivot_selector_columns?: string; igx_grid_pivot_selector_values?: string; igx_grid_pivot_selector_panel_empty?: string; + igx_grid_required_validation_error?: string; + igx_grid_min_validation_error?: string; + igx_grid_max_validation_error?: string; + igx_grid_min_length_validation_error?: string; + igx_grid_max_length_validation_error?: string; + igx_grid_email_validation_error?: string; + igx_grid_pattern_validation_error?: string; } export const GridResourceStringsEN: IGridResourceStrings = { @@ -314,4 +321,11 @@ export const GridResourceStringsEN: IGridResourceStrings = { igx_grid_pivot_selector_columns: 'Columns', igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drop Items Here', + igx_grid_required_validation_error: 'Field is required.', + igx_grid_min_validation_error: 'Field must be more than {0}', + igx_grid_max_validation_error: 'Field must be less than {0}', + igx_grid_min_length_validation_error: 'Field length must be at least {0} characters long.', + igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', + igx_grid_email_validation_error: 'Field must contain a valid email address.', + igx_grid_pattern_validation_error: 'Field must match pattern {0}' }; diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.html b/projects/igniteui-angular/src/lib/grids/cell.component.html index fad7c6d7cbc..9953730f59c 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/cell.component.html @@ -111,24 +111,24 @@
- Field is required. + {{grid.resourceStrings.igx_grid_required_validation_error}}
- Field must be at least {{formGroup.get(column.field).errors.minlength.requiredLength}} characters long. + {{grid.resourceStrings.igx_grid_min_length_validation_error | igxStringReplace:'{0}':formGroup.get(column.field).errors.minlength.requiredLength }}
- Field must be at most {{formGroup.get(column.field).errors.maxlength.requiredLength}} characters long. + {{grid.resourceStrings.igx_grid_max_length_validation_error | igxStringReplace:'{0}':formGroup.get(column.field).errors.maxlength.requiredLength }}
- Field must be more than {{formGroup.get(column.field).errors.min.min}}. + {{grid.resourceStrings.igx_grid_min_validation_error | igxStringReplace:'{0}':formGroup.get(column.field).errors.min.min }}
- Field must be less than {{formGroup.get(column.field).errors.max.max}}. + {{grid.resourceStrings.igx_grid_max_validation_error | igxStringReplace:'{0}':formGroup.get(column.field).errors.max.max }}
- Field must contain a valid email address. + {{grid.resourceStrings.igx_grid_email_validation_error }}
- Field must match pattern {{formGroup.get(column.field).errors.pattern.requiredPattern}}. + {{grid.resourceStrings.igx_grid_pattern_validation_error | igxStringReplace:'{0}':formGroup.get(column.field).errors.pattern.requiredPattern }}
\ No newline at end of file diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index 1b053f9a5cf..0b38109580c 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -4,7 +4,7 @@

Grid without transactions

Row edit - From dcb02e57ed0f8ed7878a36b2b4c1f9a22fdd01ea Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 11 Aug 2022 16:23:21 +0300 Subject: [PATCH 029/149] chore(*): fix merge conflicts. --- projects/igniteui-angular/src/lib/grids/cell.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.html b/projects/igniteui-angular/src/lib/grids/cell.component.html index 11b26a521cc..bc75ac2fd4c 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/cell.component.html @@ -48,7 +48,7 @@ - + Date: Thu, 11 Aug 2022 16:43:40 +0300 Subject: [PATCH 030/149] chore(*): fix lint. --- .../src/lib/grids/columns/column.module.ts | 32 +++++++------- .../lib/grids/columns/validators.directive.ts | 44 +++++++++---------- .../services/transaction/base-transaction.ts | 2 +- .../grid-validation.sample.component.ts | 2 +- 4 files changed, 41 insertions(+), 39 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/columns/column.module.ts b/projects/igniteui-angular/src/lib/grids/columns/column.module.ts index 0cc490bf20b..e2d24493d7a 100644 --- a/projects/igniteui-angular/src/lib/grids/columns/column.module.ts +++ b/projects/igniteui-angular/src/lib/grids/columns/column.module.ts @@ -12,17 +12,19 @@ import { IgxFilterCellTemplateDirective, IgxSummaryTemplateDirective } from './templates.directive'; -import { IgxColumMaxLengthValidator, IgxColumnEmailValidator, IgxColumnMaxValidator, IgxColumnMinLengthValidator, IgxColumnMinValidator, IgxColumnRequiredValidator, IgxColumPatternValidator } from './validators.directive'; +import { IgxColumMaxLengthValidatorDirective, IgxColumnEmailValidatorDirective, IgxColumnMaxValidatorDirective, + IgxColumnMinLengthValidatorDirective, IgxColumnMinValidatorDirective, IgxColumnRequiredValidatorDirective, + IgxColumPatternValidatorDirective } from './validators.directive'; @NgModule({ declarations: [ - IgxColumnRequiredValidator, - IgxColumnMinValidator, - IgxColumnMaxValidator, - IgxColumnMinLengthValidator, - IgxColumMaxLengthValidator, - IgxColumnEmailValidator, - IgxColumPatternValidator, + IgxColumnRequiredValidatorDirective, + IgxColumnMinValidatorDirective, + IgxColumnMaxValidatorDirective, + IgxColumnMinLengthValidatorDirective, + IgxColumMaxLengthValidatorDirective, + IgxColumnEmailValidatorDirective, + IgxColumPatternValidatorDirective, IgxFilterCellTemplateDirective, IgxSummaryTemplateDirective, IgxCellTemplateDirective, @@ -36,13 +38,13 @@ import { IgxColumMaxLengthValidator, IgxColumnEmailValidator, IgxColumnMaxValida IgxColumnLayoutComponent ], exports: [ - IgxColumnRequiredValidator, - IgxColumnMinValidator, - IgxColumnMaxValidator, - IgxColumnMinLengthValidator, - IgxColumMaxLengthValidator, - IgxColumnEmailValidator, - IgxColumPatternValidator, + IgxColumnRequiredValidatorDirective, + IgxColumnMinValidatorDirective, + IgxColumnMaxValidatorDirective, + IgxColumnMinLengthValidatorDirective, + IgxColumMaxLengthValidatorDirective, + IgxColumnEmailValidatorDirective, + IgxColumPatternValidatorDirective, IgxFilterCellTemplateDirective, IgxSummaryTemplateDirective, IgxCellTemplateDirective, diff --git a/projects/igniteui-angular/src/lib/grids/columns/validators.directive.ts b/projects/igniteui-angular/src/lib/grids/columns/validators.directive.ts index 6afff512012..88b156f1122 100644 --- a/projects/igniteui-angular/src/lib/grids/columns/validators.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/columns/validators.directive.ts @@ -15,19 +15,19 @@ export abstract class IgxColumnValidator extends Validators { } } - abstract validate(value : any) : ValidationErrors | null; + public abstract validate(value : any) : ValidationErrors | null; } @Directive({ - selector: - 'igx-column[required]', + // eslint-disable-next-line @angular-eslint/directive-selector + selector: 'igx-column[required]', providers: [{ provide: NG_VALIDATORS, useExisting: forwardRef(() => RequiredValidator), multi: true }] }) -export class IgxColumnRequiredValidator extends RequiredValidator { +export class IgxColumnRequiredValidatorDirective extends RequiredValidator { constructor(private column: IgxColumnComponent) { super(); column.validators.push(this); @@ -35,15 +35,15 @@ export class IgxColumnRequiredValidator extends RequiredValidator { } @Directive({ - selector: - 'igx-column[min]', + // eslint-disable-next-line @angular-eslint/directive-selector + selector: 'igx-column[min]', providers: [{ provide: NG_VALIDATORS, useExisting: forwardRef(() => MinValidator), multi: true }] }) -export class IgxColumnMinValidator extends MinValidator { +export class IgxColumnMinValidatorDirective extends MinValidator { constructor(private column: IgxColumnComponent) { super(); column.validators.push(this); @@ -52,15 +52,15 @@ export class IgxColumnMinValidator extends MinValidator { @Directive({ - selector: - 'igx-column[max]', + // eslint-disable-next-line @angular-eslint/directive-selector + selector: 'igx-column[max]', providers: [{ provide: NG_VALIDATORS, useExisting: forwardRef(() => MaxValidator), multi: true }] }) -export class IgxColumnMaxValidator extends MaxValidator { +export class IgxColumnMaxValidatorDirective extends MaxValidator { constructor(private column: IgxColumnComponent) { super(); column.validators.push(this); @@ -69,15 +69,15 @@ export class IgxColumnMaxValidator extends MaxValidator { @Directive({ - selector: - 'igx-column[email]', + // eslint-disable-next-line @angular-eslint/directive-selector + selector: 'igx-column[email]', providers: [{ provide: NG_VALIDATORS, useExisting: forwardRef(() => EmailValidator), multi: true }] }) -export class IgxColumnEmailValidator extends EmailValidator { +export class IgxColumnEmailValidatorDirective extends EmailValidator { constructor(private column: IgxColumnComponent) { super(); column.validators.push(this); @@ -86,15 +86,15 @@ export class IgxColumnEmailValidator extends EmailValidator { @Directive({ - selector: - 'igx-column[minlength]', + // eslint-disable-next-line @angular-eslint/directive-selector + selector: 'igx-column[minlength]', providers: [{ provide: NG_VALIDATORS, useExisting: forwardRef(() => MinLengthValidator), multi: true }] }) -export class IgxColumnMinLengthValidator extends MinLengthValidator { +export class IgxColumnMinLengthValidatorDirective extends MinLengthValidator { constructor(private column: IgxColumnComponent) { super(); column.validators.push(this); @@ -102,15 +102,15 @@ export class IgxColumnMinLengthValidator extends MinLengthValidator { } @Directive({ - selector: - 'igx-column[maxlength]', + // eslint-disable-next-line @angular-eslint/directive-selector + selector: 'igx-column[maxlength]', providers: [{ provide: NG_VALIDATORS, useExisting: forwardRef(() => MaxLengthValidator), multi: true }] }) -export class IgxColumMaxLengthValidator extends MaxLengthValidator { +export class IgxColumMaxLengthValidatorDirective extends MaxLengthValidator { constructor(private column: IgxColumnComponent) { super(); column.validators.push(this); @@ -118,15 +118,15 @@ export class IgxColumMaxLengthValidator extends MaxLengthValidator { } @Directive({ - selector: - 'igx-column[pattern]', + // eslint-disable-next-line @angular-eslint/directive-selector + selector: 'igx-column[pattern]', providers: [{ provide: NG_VALIDATORS, useExisting: forwardRef(() => PatternValidator), multi: true }] }) -export class IgxColumPatternValidator extends PatternValidator { +export class IgxColumPatternValidatorDirective extends PatternValidator { constructor(private column: IgxColumnComponent) { super(); column.validators.push(this); diff --git a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts index 87bed760aef..fda2193abf9 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts @@ -123,7 +123,7 @@ export class IgxBaseTransactionService i if (id !== undefined) { pending = pending.filter(t => t.id === id); } - return pending.filter(x => x.validity.some(x => x.valid === false)); + return pending.filter(x => x.validity.some(y => y.valid === false)); } /** diff --git a/src/app/grid-validation/grid-validation.sample.component.ts b/src/app/grid-validation/grid-validation.sample.component.ts index d83e7f5ace7..33cfabdec7c 100644 --- a/src/app/grid-validation/grid-validation.sample.component.ts +++ b/src/app/grid-validation/grid-validation.sample.component.ts @@ -19,7 +19,7 @@ export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { @Input('appForbiddenName') public forbiddenName = ''; - validate(control: AbstractControl): ValidationErrors | null { + public validate(control: AbstractControl): ValidationErrors | null { return this.forbiddenName ? forbiddenNameValidator(new RegExp(this.forbiddenName, 'i'))(control) : null; } From 9fbbd64ae24915b2061624f1e36b6d70e060e036 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 11 Aug 2022 17:02:55 +0300 Subject: [PATCH 031/149] chore(*): Add --valid style on cell when it changes state on edit. Fix style lint. --- .../styles/components/grid/_grid-component.scss | 4 ++-- .../core/styles/components/grid/_grid-theme.scss | 5 +++++ .../src/lib/grids/cell.component.ts | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-component.scss b/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-component.scss index 354a5ed3388..a97f9a34ae8 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-component.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-component.scss @@ -235,8 +235,8 @@ @extend %grid-cell--invalid !optional; } - @include e(td, $m: invalidInline) { - @extend %grid-cell--invalidInline !optional; + @include e(td, $m: valid) { + @extend %grid-cell--valid !optional; } @include e(td, $m: column-selected) { diff --git a/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss index 63a5ab5b8ce..00f0afce351 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss @@ -1396,6 +1396,7 @@ %grid-cell--invalid { box-shadow: inset 0 0 0 rem(1px) color($palette, 'error', 500) !important; padding-right: rem(4px) !important; + > igx-icon { color: color($palette, 'error', 500); width: var(--igx-icon-size, rem(18px)); @@ -1407,6 +1408,10 @@ } } + %grid-cell--valid { + box-shadow: inset 0 0 0 rem(1px) color($palette, 'success', 500) !important; + } + %grid-cell--pinned-selected, %grid-cell--selected { color: var-get($theme, 'cell-selected-text-color'); diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 0bbdc8f99a0..b40b4619081 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -457,6 +457,10 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT return id; } + /** + * @hidden + * @internal + */ @HostBinding('class.igx-grid__td--invalid') @HostBinding('attr.aria-invalid') public get isInvalid() { @@ -468,6 +472,16 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT } } + /** + * @hidden + * @internal + */ + @HostBinding('class.igx-grid__td--valid') + public get isValidAfterEdit() { + const formControl = this.formGroup?.get(this.column?.field); + return this.editMode && !formControl.invalid && formControl.dirty; + } + private get validity() { const state = this.grid.transactions.getState(this.row.key); if (state && state.validity && state.validity.some(x => x.valid === false)) { From 638bb0f4ad806d63f7cf5f4d2b627367a1e454b8 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 11 Aug 2022 17:33:08 +0300 Subject: [PATCH 032/149] chore(*): Fix build. --- projects/igniteui-angular/src/lib/grids/common/pipes.ts | 1 - .../lib/grids/hierarchical-grid/hierarchical-grid.module.ts | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/common/pipes.ts b/projects/igniteui-angular/src/lib/grids/common/pipes.ts index 91def176600..a8d3394ae38 100644 --- a/projects/igniteui-angular/src/lib/grids/common/pipes.ts +++ b/projects/igniteui-angular/src/lib/grids/common/pipes.ts @@ -5,7 +5,6 @@ import { GridType, IGX_GRID_BASE, RowType } from './grid.interface'; import { IgxAddRow } from './crud.service'; import { IgxSummaryOperand, IgxSummaryResult } from '../summaries/grid-summary'; import { IgxGridRow } from '../grid-public-row'; -import { LayoutSampleComponent } from 'src/app/layout/layout.sample'; import { IgxTransactionService } from '../../services/public_api'; interface GridStyleCSSProperty { diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.module.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.module.ts index 38d9f90859e..391586c3533 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.module.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.module.ts @@ -5,6 +5,8 @@ import { IgxHierarchicalRowComponent } from './hierarchical-row.component'; import { IgxGridHierarchicalPipe, IgxGridHierarchicalPagingPipe } from './hierarchical-grid.pipes'; import { IgxRowIslandComponent } from './row-island.component'; import { IgxHierarchicalGridCellComponent } from './hierarchical-cell.component'; +import { IgxTooltipModule } from '../../directives/tooltip'; +import { ReactiveFormsModule } from '@angular/forms'; /** * @hidden @@ -29,6 +31,8 @@ import { IgxHierarchicalGridCellComponent } from './hierarchical-cell.component' ], imports: [ IgxGridModule, + IgxTooltipModule, + ReactiveFormsModule ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) From 804ec334655361c95ce3206f8971781de39dd1cb Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 11 Aug 2022 17:55:44 +0300 Subject: [PATCH 033/149] chore(*): Fix tests. --- projects/igniteui-angular/src/lib/grids/cell.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index b40b4619081..80f236622ed 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -479,11 +479,11 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT @HostBinding('class.igx-grid__td--valid') public get isValidAfterEdit() { const formControl = this.formGroup?.get(this.column?.field); - return this.editMode && !formControl.invalid && formControl.dirty; + return this.editMode && formControl && !formControl.invalid && formControl.dirty; } private get validity() { - const state = this.grid.transactions.getState(this.row.key); + const state = this.grid.transactions.getState(this.intRow.key); if (state && state.validity && state.validity.some(x => x.valid === false)) { return state.validity.find(x => x.field === this.column.field); } From 0a6c46a6bd45b0ca0b93cf09c7fc64c1e0375f32 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 11 Aug 2022 18:18:51 +0300 Subject: [PATCH 034/149] chore(*): Add check in case there's no form control for column. --- projects/igniteui-angular/src/lib/grids/common/crud.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts index 4b26c3cbbb2..57e7a9f9066 100644 --- a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts +++ b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts @@ -118,6 +118,7 @@ export class IgxCell { public createEditEventArgs(includeNewValue = true, event?: Event): IGridEditEventArgs { const row = this.grid.crudService.row ?? this.row; + const formControl = row.rowFormGroup.get(this.column.field); const args: IGridEditEventArgs = { rowID: this.id.rowID, cellID: this.id, @@ -126,7 +127,7 @@ export class IgxCell { cancel: false, column: this.column, owner: this.grid, - isValid: row.rowFormGroup.get(this.column.field).valid, + isValid: formControl ? formControl.valid : true, event }; if (includeNewValue) { From d8bb991ad0f5bb3bdc6325e32251eddaddabb3fa Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 11 Aug 2022 18:30:48 +0300 Subject: [PATCH 035/149] chore(*): Add check in the other event for formControl. --- projects/igniteui-angular/src/lib/grids/common/crud.service.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts index 57e7a9f9066..fada2931a56 100644 --- a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts +++ b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts @@ -142,7 +142,6 @@ export class IgxCell { const rowData = updatedData === null ? this.grid.gridAPI.getRowData(this.id.rowID) : updatedData; const row = this.grid.crudService.row ?? this.row; const formControl = row.rowFormGroup.get(this.column.field); - formControl.updateValueAndValidity(); const args: IGridEditDoneEventArgs = { rowID: this.id.rowID, cellID: this.id, @@ -150,7 +149,7 @@ export class IgxCell { // the only case we use this.rowData directly, is when there is no rowEditing or transactions enabled rowData, oldValue: this.value, - isValid: formControl.valid, + isValid: formControl ? formControl.valid : true, newValue: value, column: this.column, owner: this.grid, From 3d0909863ecf2d57a93f6634f4c9d2b0558d9207 Mon Sep 17 00:00:00 2001 From: didimmova Date: Fri, 12 Aug 2022 09:39:11 +0300 Subject: [PATCH 036/149] theme(grid): update validation styles --- .../core/styles/components/grid/_grid-theme.scss | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss index 00f0afce351..489d94d2b47 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss @@ -1393,9 +1393,10 @@ border-bottom-color: var-get($theme, 'cell-active-border-color'); } } + %grid-cell--invalid { box-shadow: inset 0 0 0 rem(1px) color($palette, 'error', 500) !important; - padding-right: rem(4px) !important; + padding-inline-end: rem(4px) !important; > igx-icon { color: color($palette, 'error', 500); @@ -1403,9 +1404,18 @@ height: var(--igx-icon-size, rem(18px)); font-size: var(--igx-icon-size, rem(18px)); } + %grid-cell-text { width: 100%; } + + .igx-input-group__bundle { + &:focus-within { + &::after { + border: none !important; + } + } + } } %grid-cell--valid { @@ -1514,6 +1524,10 @@ border: none !important; } + &.igx-grid__td--invalid { + padding-inline-end: rem(4px) !important; + } + igx-prefix, igx-suffix { padding-top: 0 !important; From a0ccaf119e3ca43f04ca619a35999da490f46dc0 Mon Sep 17 00:00:00 2001 From: didimmova Date: Mon, 15 Aug 2022 11:01:12 +0300 Subject: [PATCH 037/149] theme(grid): hide asterisk --- .../src/lib/core/styles/components/grid/_grid-theme.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss index 489d94d2b47..fda24e6214d 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss @@ -1522,6 +1522,10 @@ .igx-input-group__bundle { border: none !important; + + &::before { + content: none !important; + } } &.igx-grid__td--invalid { From 36d1683d6bac6c60acfab3542c481029a7fc15e6 Mon Sep 17 00:00:00 2001 From: MKirova Date: Mon, 15 Aug 2022 11:30:15 +0300 Subject: [PATCH 038/149] chore(*): Fix check in case another row is in edit mode and a previous invalid cell error icon is hovered. --- projects/igniteui-angular/src/lib/grids/cell.component.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 80f236622ed..2fc6c76ecbf 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -88,8 +88,12 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT public get formGroup() : FormGroup { const isRowEdit = this.grid.crudService.rowEditing; - const formGroup = isRowEdit ? this.grid.crudService.row?.rowFormGroup : this.grid.crudService.cell?.row.rowFormGroup; - return formGroup || this.validity?.formGroup; + const editRow = isRowEdit ? this.grid.crudService.row : this.grid.crudService.cell?.row; + if (editRow && editRow.id === this.intRow.key) { + return editRow.rowFormGroup; + } else { + return this.validity?.formGroup; + } } /** From 4297a870db0c5d0c21dbbde0974cd9c0b660013a Mon Sep 17 00:00:00 2001 From: MKirova Date: Mon, 15 Aug 2022 13:17:14 +0300 Subject: [PATCH 039/149] chore():Add a property in base service that determines whether to apply changes directly to data but still strore states and transactions for validation. --- .../igniteui-angular/src/lib/grids/api.service.ts | 12 ++++++++++++ .../igniteui-angular/src/lib/grids/common/pipes.ts | 5 ++--- .../src/lib/grids/tree-grid/tree-grid-api.service.ts | 3 +++ .../src/lib/services/transaction/base-transaction.ts | 8 ++++++++ .../src/lib/services/transaction/igx-transaction.ts | 8 ++++++++ .../src/lib/services/transaction/transaction.ts | 4 ++++ .../grid-validation.sample.component.html | 4 ++++ .../grid-validation.sample.component.ts | 4 +++- 8 files changed, 44 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/api.service.ts b/projects/igniteui-angular/src/lib/grids/api.service.ts index a435162f125..319510d535b 100644 --- a/projects/igniteui-angular/src/lib/grids/api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/api.service.ts @@ -53,6 +53,9 @@ export class GridBaseAPIService implements GridServiceType { data.splice(index, 1); } }); + if (grid.transactions.autoCommit) { + data = grid.data; + } } else { data = grid.data; } @@ -310,6 +313,9 @@ export class GridBaseAPIService implements GridServiceType { const transactionId = grid.primaryKey ? rowData[grid.primaryKey] : rowData; const transaction: Transaction = { id: transactionId, type: TransactionType.ADD, newValue: rowData }; grid.transactions.add(transaction); + if (grid.transactions.autoCommit) { + grid.data.push(rowData); + } } else { grid.data.push(rowData); } @@ -323,6 +329,9 @@ export class GridBaseAPIService implements GridServiceType { if (grid.transactions.enabled) { const transaction: Transaction = { id: rowID, type: TransactionType.DELETE, newValue: null }; grid.transactions.add(transaction, grid.data[index]); + if (grid.transactions.autoCommit) { + grid.data.splice(index, 1); + } } else { grid.data.splice(index, 1); } @@ -566,6 +575,9 @@ export class GridBaseAPIService implements GridServiceType { validity: validity }; grid.transactions.add(transaction, rowCurrentValue); + if (grid.transactions.autoCommit) { + mergeObjects(rowValueInDataSource, rowNewValue); + } } else { mergeObjects(rowValueInDataSource, rowNewValue); } diff --git a/projects/igniteui-angular/src/lib/grids/common/pipes.ts b/projects/igniteui-angular/src/lib/grids/common/pipes.ts index a8d3394ae38..61e876a8967 100644 --- a/projects/igniteui-angular/src/lib/grids/common/pipes.ts +++ b/projects/igniteui-angular/src/lib/grids/common/pipes.ts @@ -5,7 +5,6 @@ import { GridType, IGX_GRID_BASE, RowType } from './grid.interface'; import { IgxAddRow } from './crud.service'; import { IgxSummaryOperand, IgxSummaryResult } from '../summaries/grid-summary'; import { IgxGridRow } from '../grid-public-row'; -import { IgxTransactionService } from '../../services/public_api'; interface GridStyleCSSProperty { [prop: string]: any; @@ -299,11 +298,11 @@ export class IgxGridTransactionStatePipe implements PipeTransform { public transform(row_id: any, field: string, rowEditable: boolean, transactions: any, _: any, __: any, ___: any) { if (rowEditable) { const rowCurrentState = transactions.getAggregatedValue(row_id, false); - if (rowCurrentState && transactions instanceof IgxTransactionService) { + if (rowCurrentState && !transactions.autoCommit) { const value = resolveNestedPath(rowCurrentState, field); return value !== undefined && value !== null; } - } else if (transactions instanceof IgxTransactionService) { + } else if (!transactions.autoCommit) { const transaction = transactions.getState(row_id); const value = resolveNestedPath(transaction?.value ?? {}, field); return transaction && transaction.value && (value || value === 0 || value === false); diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts index fd7277bac99..f0f948f127d 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts @@ -296,6 +296,9 @@ export class IgxTreeGridAPIService extends GridBaseAPIService { path }; grid.transactions.add(transaction, rowCurrentValue); + if (grid.transactions.autoCommit) { + mergeObjects(rowValueInDataSource, rowNewValue); + } } else { mergeObjects(rowValueInDataSource, rowNewValue); } diff --git a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts index fda2193abf9..3de536d9471 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts @@ -38,6 +38,14 @@ export class IgxBaseTransactionService i return this._isPending; } + /** + * @inheritdoc + */ + public get autoCommit(): boolean { + // transactions are auto-commited and reflect in the data. However changes and validation states are still stored until cleared. + return true; + } + /** * @inheritdoc */ diff --git a/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts index b3a3100cc73..c606f249107 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts @@ -21,6 +21,14 @@ export class IgxTransactionService exten return this._undoStack.length > 0; } + /** + * @inheritdoc + */ + public get autoCommit(): boolean { + // transactions are stored and commited manually by the user. + return false; + } + /** * @inheritdoc */ diff --git a/projects/igniteui-angular/src/lib/services/transaction/transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/transaction.ts index b5204e297d9..8f7d553349f 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/transaction.ts @@ -58,6 +58,10 @@ export interface HierarchicalState extends State { } export interface TransactionService { + /** + * Returns whether changes are automatically commited to the data without the need to explicitly. + */ + autoCommit: boolean; /** * Returns whether transaction is enabled for this service */ diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index 0b38109580c..d7398a52a79 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -31,6 +31,10 @@

Grid with transactions

+ + + + \ No newline at end of file diff --git a/src/app/grid-validation/grid-validation.sample.component.ts b/src/app/grid-validation/grid-validation.sample.component.ts index 33cfabdec7c..e45e7263863 100644 --- a/src/app/grid-validation/grid-validation.sample.component.ts +++ b/src/app/grid-validation/grid-validation.sample.component.ts @@ -61,7 +61,9 @@ export class GridValidationSampleComponent { } public commitNoTransactions() { + console.log(this.data); this.gridNoTransactions.transactions.commit([]); + this.gridNoTransactions.markForCheck(); } public cellEdit(evt) { @@ -78,7 +80,7 @@ export class GridValidationSampleComponent { } public validationChange(evtArgs: IValidationStatus){ - console.log(evtArgs); + //console.log(evtArgs); } } From b7aaf8d6c6508e440bd3154af72f31494f453a88 Mon Sep 17 00:00:00 2001 From: MKirova Date: Mon, 15 Aug 2022 16:06:25 +0300 Subject: [PATCH 040/149] chore(*): Fix html doc and fix issues when getting id when edit mode is cell. --- projects/igniteui-angular/src/lib/grids/cell.component.ts | 3 ++- .../igniteui-angular/src/lib/grids/columns/column.component.ts | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 2fc6c76ecbf..60ce6881e17 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -89,7 +89,8 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT public get formGroup() : FormGroup { const isRowEdit = this.grid.crudService.rowEditing; const editRow = isRowEdit ? this.grid.crudService.row : this.grid.crudService.cell?.row; - if (editRow && editRow.id === this.intRow.key) { + const id = isRowEdit ? editRow?.id : editRow?.id.rowID; + if (editRow && id === this.intRow.key) { return editRow.rowFormGroup; } else { return this.validity?.formGroup; diff --git a/projects/igniteui-angular/src/lib/grids/columns/column.component.ts b/projects/igniteui-angular/src/lib/grids/columns/column.component.ts index 757f307c389..26a20a5b442 100644 --- a/projects/igniteui-angular/src/lib/grids/columns/column.component.ts +++ b/projects/igniteui-angular/src/lib/grids/columns/column.component.ts @@ -99,9 +99,6 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy * ```typescript * let validators = this.column.validators; * ``` - * ```html - * - * ``` */ public validators: Validator[] = []; From cfbef3d300a26b2d1cbba27afe4a57225c32d92b Mon Sep 17 00:00:00 2001 From: MKirova Date: Mon, 15 Aug 2022 17:27:43 +0300 Subject: [PATCH 041/149] chore(*): Update validitu per field since it gets wrong validity when editing in cell mode one cell at a time. --- .../services/transaction/base-transaction.ts | 33 ++++++++++++++++--- .../services/transaction/igx-transaction.ts | 2 +- .../grid-validation.sample.component.html | 11 ++----- .../grid-validation.sample.component.ts | 6 ++-- 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts index 3de536d9471..49fc76a7736 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts @@ -35,7 +35,7 @@ export class IgxBaseTransactionService i * @inheritdoc */ public get enabled(): boolean { - return this._isPending; + return this._isPending || this.autoCommit; } /** @@ -43,7 +43,7 @@ export class IgxBaseTransactionService i */ public get autoCommit(): boolean { // transactions are auto-commited and reflect in the data. However changes and validation states are still stored until cleared. - return true; + return true; } /** @@ -60,7 +60,7 @@ export class IgxBaseTransactionService i * @inheritdoc */ public add(transaction: T, recordRef?: any): void { - if (this._isPending) { + if (this.enabled) { this.updateState(this._pendingStates, transaction, recordRef); this._pendingTransactions.push(transaction); } @@ -119,7 +119,7 @@ export class IgxBaseTransactionService i /** * @inheritdoc */ - public commit(_data: any[], _id?: any): void { + public commit(_data: any[], _id?: any): void { this.clear(_id); } @@ -176,11 +176,34 @@ export class IgxBaseTransactionService i } else { state.value = transaction.newValue; } + this.updateValidity(state, transaction); } else { state = { value: this.cloneStrategy.clone(transaction.newValue), recordRef, type: transaction.type, validity: transaction.validity } as S; states.set(transaction.id, state); + state.validity = transaction.validity; } - state.validity = transaction.validity; + } + + /** + * Updates the validity state after update. + * + * @param state State to update value for + * @param transaction The transaction based on which to update. + */ + protected updateValidity(state, transaction) { + // update validity + const objKeys = Object.keys(state.value); + objKeys.forEach(x => { + const currentState = state.validity.find(y => y.field === x); + const newState = transaction.validity.find(y => y.field === x); + if (currentState && newState) { + // update existing + currentState.formGroup = newState.formGroup; + currentState.valid = newState.valid; + } else if (!currentState) { + state.validity = state.validity.concat(transaction.validity); + } + }); } /** diff --git a/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts index c606f249107..e30dc70131a 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts @@ -286,7 +286,7 @@ export class IgxTransactionService exten } else { state.value = transaction.newValue; } - state.validity = transaction.validity; + this.updateValidity(state, transaction); } } else { state = { value: this.cloneStrategy.clone(transaction.newValue), recordRef, type: transaction.type, validity: transaction.validity } as S; diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index d7398a52a79..84128c1b427 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -20,16 +20,9 @@

Grid with transactions

Row edit - - -
- This name is required. -
-
- This name is forbidden. -
-
+
diff --git a/src/app/grid-validation/grid-validation.sample.component.ts b/src/app/grid-validation/grid-validation.sample.component.ts index e45e7263863..c81973aa7cd 100644 --- a/src/app/grid-validation/grid-validation.sample.component.ts +++ b/src/app/grid-validation/grid-validation.sample.component.ts @@ -68,9 +68,9 @@ export class GridValidationSampleComponent { public cellEdit(evt) { // can cancel if there are validation errors - if (!evt.isValid && !this.rowEditNoTransactions) { - evt.cancel = true; - } + // if (!evt.isValid && !this.rowEditNoTransactions) { + // evt.cancel = true; + // } } public formCreateHandler(formGr: FormGroup) { From 4664d22734baaf960efb9da09756706e6d984afd Mon Sep 17 00:00:00 2001 From: MKirova Date: Mon, 15 Aug 2022 17:45:27 +0300 Subject: [PATCH 042/149] chore(*): Expose default error template. --- .../src/lib/grids/cell.component.ts | 14 ++++++++++++++ .../grid-validation.sample.component.html | 8 +++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 60ce6881e17..8f088fd8f2f 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -68,12 +68,26 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT return this.intRow.addRowUI && (this.value === undefined || this.value === null); } + /** + * @hidden + * @internal + */ @ViewChildren('error', {read: IgxTooltipDirective}) public errorTooltip: QueryList; + /** + * @hidden + * @internal + */ @ViewChild('errorIcon', { read: IgxIconComponent, static: false }) public errorIcon: IgxIconComponent; + /** + * Gets the default error template. + */ + @ViewChild('defaultError', { read: TemplateRef, static: true }) + public defaultErrorTemplate: TemplateRef; + /** * Gets the column of the cell. * ```typescript diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index 84128c1b427..4279676a42e 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -22,7 +22,13 @@

Grid with transactions

[width]="'1200px'" [height]="'800px'" > - + + + +
+ This name is forbidden. +
+
From aabd7cc41bba2e0214445ad86a94ad69585a7c3c Mon Sep 17 00:00:00 2001 From: MKirova Date: Mon, 15 Aug 2022 17:57:31 +0300 Subject: [PATCH 043/149] chore(*): Re-use formgroup from row if there's no info in the transaction service. --- projects/igniteui-angular/src/lib/grids/cell.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 8f088fd8f2f..329cd5fdd4e 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -107,7 +107,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT if (editRow && id === this.intRow.key) { return editRow.rowFormGroup; } else { - return this.validity?.formGroup; + return this.validity?.formGroup ?? editRow?.rowFormGroup; } } From 6049273228a4eafb08003f7a4e40484a83eef3a2 Mon Sep 17 00:00:00 2001 From: MKirova Date: Mon, 15 Aug 2022 18:21:18 +0300 Subject: [PATCH 044/149] chore(*): Fix issue when transaction.validity is undefined. --- .../src/lib/services/transaction/base-transaction.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts index 49fc76a7736..487666cd1e1 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts @@ -195,12 +195,12 @@ export class IgxBaseTransactionService i const objKeys = Object.keys(state.value); objKeys.forEach(x => { const currentState = state.validity.find(y => y.field === x); - const newState = transaction.validity.find(y => y.field === x); + const newState = transaction.validity?.find(y => y.field === x); if (currentState && newState) { // update existing currentState.formGroup = newState.formGroup; currentState.valid = newState.valid; - } else if (!currentState) { + } else if (!currentState && transaction.validity) { state.validity = state.validity.concat(transaction.validity); } }); From 255a8277bac3ba3727f671b3c6228ffb61f7a6ef Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 16 Aug 2022 10:36:47 +0300 Subject: [PATCH 045/149] chore(*): Clear last pending transaction once Done/Cancel button is clicked in row edit mode. Allow pending changes to be submitted in the data only after that. --- .../services/transaction/base-transaction.ts | 18 ++++++++++++------ .../services/transaction/igx-transaction.ts | 5 +---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts index 487666cd1e1..603263f67c1 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts @@ -41,10 +41,7 @@ export class IgxBaseTransactionService i /** * @inheritdoc */ - public get autoCommit(): boolean { - // transactions are auto-commited and reflect in the data. However changes and validation states are still stored until cleared. - return true; - } + public autoCommit = true; /** * @inheritdoc @@ -147,14 +144,23 @@ export class IgxBaseTransactionService i */ public startPending(): void { this._isPending = true; + this.autoCommit = false; } /** * @inheritdoc */ public endPending(_commit: boolean): void { - if (_commit) { + if (this._isPending && !_commit) { this._isPending = false; + this.autoCommit = true; + // reset last pending transaction. + const last = this._pendingTransactions.pop(); + if (last) { + this._pendingStates.delete(last.id); + } + } + if (_commit) { this._pendingStates.clear(); this._pendingTransactions = []; } @@ -194,7 +200,7 @@ export class IgxBaseTransactionService i // update validity const objKeys = Object.keys(state.value); objKeys.forEach(x => { - const currentState = state.validity.find(y => y.field === x); + const currentState = state.validity?.find(y => y.field === x); const newState = transaction.validity?.find(y => y.field === x); if (currentState && newState) { // update existing diff --git a/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts index e30dc70131a..015543fb172 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts @@ -24,10 +24,7 @@ export class IgxTransactionService exten /** * @inheritdoc */ - public get autoCommit(): boolean { - // transactions are stored and commited manually by the user. - return false; - } + public autoCommit: boolean = false; /** * @inheritdoc From de52513a98c578db1c486a50542fb83983cae9bf Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 16 Aug 2022 11:30:55 +0300 Subject: [PATCH 046/149] chore(*): Do not store add and delete transactions in case we want to directly commit them in the data. Store just updates for validation. --- projects/igniteui-angular/src/lib/grids/api.service.ts | 10 ++-------- .../src/lib/services/transaction/base-transaction.ts | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/api.service.ts b/projects/igniteui-angular/src/lib/grids/api.service.ts index 319510d535b..8c102b234c6 100644 --- a/projects/igniteui-angular/src/lib/grids/api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/api.service.ts @@ -309,13 +309,10 @@ export class GridBaseAPIService implements GridServiceType { // If there is a row in edit - > commit and close const grid = this.grid; - if (grid.transactions.enabled) { + if (grid.transactions.enabled && !grid.transactions.autoCommit) { const transactionId = grid.primaryKey ? rowData[grid.primaryKey] : rowData; const transaction: Transaction = { id: transactionId, type: TransactionType.ADD, newValue: rowData }; grid.transactions.add(transaction); - if (grid.transactions.autoCommit) { - grid.data.push(rowData); - } } else { grid.data.push(rowData); } @@ -326,12 +323,9 @@ export class GridBaseAPIService implements GridServiceType { // if there is a row in ADD or UPDATE state change it's state to DELETE const grid = this.grid; if (index !== -1) { - if (grid.transactions.enabled) { + if (grid.transactions.enabled && !grid.transactions.autoCommit) { const transaction: Transaction = { id: rowID, type: TransactionType.DELETE, newValue: null }; grid.transactions.add(transaction, grid.data[index]); - if (grid.transactions.autoCommit) { - grid.data.splice(index, 1); - } } else { grid.data.splice(index, 1); } diff --git a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts index 603263f67c1..479ab7d0d92 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts @@ -207,7 +207,7 @@ export class IgxBaseTransactionService i currentState.formGroup = newState.formGroup; currentState.valid = newState.valid; } else if (!currentState && transaction.validity) { - state.validity = state.validity.concat(transaction.validity); + state.validity = (state.validity || []).concat(transaction.validity); } }); } From 95d2e88147e407134e3794e00e29b3643436fc78 Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 16 Aug 2022 13:02:03 +0300 Subject: [PATCH 047/149] chore(*): Move validation states in a separate transaction collection so that they don't interfe with existing pending transaction logic. --- .../src/lib/grids/api.service.ts | 30 +++--- .../src/lib/grids/cell.component.ts | 2 +- .../src/lib/grids/common/pipes.ts | 4 +- .../grids/tree-grid/tree-grid-api.service.ts | 6 +- .../services/transaction/base-transaction.ts | 97 ++++++++++++------- .../services/transaction/igx-transaction.ts | 6 +- .../lib/services/transaction/transaction.ts | 8 +- 7 files changed, 87 insertions(+), 66 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/api.service.ts b/projects/igniteui-angular/src/lib/grids/api.service.ts index 8c102b234c6..bf8bb2349df 100644 --- a/projects/igniteui-angular/src/lib/grids/api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/api.service.ts @@ -53,9 +53,6 @@ export class GridBaseAPIService implements GridServiceType { data.splice(index, 1); } }); - if (grid.transactions.autoCommit) { - data = grid.data; - } } else { data = grid.data; } @@ -304,18 +301,18 @@ export class GridBaseAPIService implements GridServiceType { return this.grid.filteredData; } - public addRowToData(rowData: any, _parentID?: any) { + public addRowToData(rowData: any, _parentID?: any, validity?) { // Add row goes to transactions and if rowEditable is properly implemented, added rows will go to pending transactions // If there is a row in edit - > commit and close const grid = this.grid; - - if (grid.transactions.enabled && !grid.transactions.autoCommit) { - const transactionId = grid.primaryKey ? rowData[grid.primaryKey] : rowData; + const transactionId = grid.primaryKey ? rowData[grid.primaryKey] : rowData; + if (grid.transactions.enabled) { const transaction: Transaction = { id: transactionId, type: TransactionType.ADD, newValue: rowData }; grid.transactions.add(transaction); } else { grid.data.push(rowData); } + grid.transactions.addValidation({newValue: rowData, id: transactionId, type: TransactionType.ADD, validity: validity}); } public deleteRowFromData(rowID: any, index: number) { @@ -323,7 +320,7 @@ export class GridBaseAPIService implements GridServiceType { // if there is a row in ADD or UPDATE state change it's state to DELETE const grid = this.grid; if (index !== -1) { - if (grid.transactions.enabled && !grid.transactions.autoCommit) { + if (grid.transactions.enabled) { const transaction: Transaction = { id: rowID, type: TransactionType.DELETE, newValue: null }; grid.transactions.add(transaction, grid.data[index]); } else { @@ -561,20 +558,19 @@ export class GridBaseAPIService implements GridServiceType { * @param rowNewValue New value of the row */ protected updateData(grid, rowID, rowValueInDataSource: any, rowCurrentValue: any, rowNewValue: { [x: string]: any }, validity?) { + + const transaction: Transaction = { + id: rowID, + type: TransactionType.UPDATE, + newValue: rowNewValue, + validity: validity + }; if (grid.transactions.enabled) { - const transaction: Transaction = { - id: rowID, - type: TransactionType.UPDATE, - newValue: rowNewValue, - validity: validity - }; grid.transactions.add(transaction, rowCurrentValue); - if (grid.transactions.autoCommit) { - mergeObjects(rowValueInDataSource, rowNewValue); - } } else { mergeObjects(rowValueInDataSource, rowNewValue); } + grid.transactions.addValidation(transaction, rowCurrentValue); } diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 329cd5fdd4e..28a1e735bec 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -502,7 +502,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT } private get validity() { - const state = this.grid.transactions.getState(this.intRow.key); + const state = this.grid.transactions.getAggregatedValidation(this.intRow.key); if (state && state.validity && state.validity.some(x => x.valid === false)) { return state.validity.find(x => x.field === this.column.field); } diff --git a/projects/igniteui-angular/src/lib/grids/common/pipes.ts b/projects/igniteui-angular/src/lib/grids/common/pipes.ts index 61e876a8967..870b0de6b63 100644 --- a/projects/igniteui-angular/src/lib/grids/common/pipes.ts +++ b/projects/igniteui-angular/src/lib/grids/common/pipes.ts @@ -298,11 +298,11 @@ export class IgxGridTransactionStatePipe implements PipeTransform { public transform(row_id: any, field: string, rowEditable: boolean, transactions: any, _: any, __: any, ___: any) { if (rowEditable) { const rowCurrentState = transactions.getAggregatedValue(row_id, false); - if (rowCurrentState && !transactions.autoCommit) { + if (rowCurrentState) { const value = resolveNestedPath(rowCurrentState, field); return value !== undefined && value !== null; } - } else if (!transactions.autoCommit) { + } else { const transaction = transactions.getState(row_id); const value = resolveNestedPath(transaction?.value ?? {}, field); return transaction && transaction.value && (value || value === 0 || value === false); diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts index f0f948f127d..7b3675ec36a 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts @@ -287,7 +287,6 @@ export class IgxTreeGridAPIService extends GridBaseAPIService { rowValueInDataSource: any, rowCurrentValue: any, rowNewValue: { [x: string]: any }) { - if (grid.transactions.enabled) { const path = grid.generateRowPath(rowID); const transaction: HierarchicalTransaction = { id: rowID, @@ -295,13 +294,12 @@ export class IgxTreeGridAPIService extends GridBaseAPIService { newValue: rowNewValue, path }; + if (grid.transactions.enabled) { grid.transactions.add(transaction, rowCurrentValue); - if (grid.transactions.autoCommit) { - mergeObjects(rowValueInDataSource, rowNewValue); - } } else { mergeObjects(rowValueInDataSource, rowNewValue); } + grid.transactions.addValidation(transaction, rowCurrentValue); } private row_deleted_parent(rowID: any): boolean { diff --git a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts index 479ab7d0d92..959d1826196 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts @@ -35,14 +35,9 @@ export class IgxBaseTransactionService i * @inheritdoc */ public get enabled(): boolean { - return this._isPending || this.autoCommit; + return this._isPending; } - /** - * @inheritdoc - */ - public autoCommit = true; - /** * @inheritdoc */ @@ -51,6 +46,10 @@ export class IgxBaseTransactionService i protected _isPending = false; protected _pendingTransactions: T[] = []; protected _pendingStates: Map = new Map(); + + protected _validationTransactions: T[] = []; + protected _validationStates: Map = new Map(); + private _cloneStrategy: IDataCloneStrategy = new DefaultDataCloneStrategy(); /** @@ -92,6 +91,50 @@ export class IgxBaseTransactionService i return result; } + public addValidation(transaction: T, recordRef?: any): void { + this.updateValidationState(this._validationStates, transaction, recordRef); + this._validationTransactions.push(transaction); + } + + public updateValidationState(states: Map, transaction: T, recordRef?: any): void { + let state = states.get(transaction.id); + if (state) { + if (isObject(state.value)) { + mergeObjects(state.value, transaction.newValue); + } else { + state.value = transaction.newValue; + } + this.updateValidity(state, transaction); + } else { + state = { value: this.cloneStrategy.clone(transaction.newValue), recordRef, type: transaction.type, validity: transaction.validity } as S; + states.set(transaction.id, state); + state.validity = transaction.validity; + } + } + + /** + * @inheritdoc + */ + public getAggregatedValidationChanges(mergeChanges: boolean): T[] { + const result: T[] = []; + this._validationStates.forEach((state: S, key: any) => { + const value = mergeChanges ? this.getAggregatedValue(key, mergeChanges) : state.value; + result.push({ id: key, newValue: value, type: state.type, validity: state.validity } as T); + }); + return result; + } + + /** + * @inheritdoc + */ + public getAggregatedValidation(id: any): any { + const state = this._validationStates.get(id); + if (!state) { + return null; + } + return state; + } + /** * @inheritdoc */ @@ -124,7 +167,7 @@ export class IgxBaseTransactionService i * @inheritdoc */ public getInvalidTransactionLog(id?: any) { - let pending = [...this._pendingTransactions]; + let pending = [...this._validationTransactions]; if (id !== undefined) { pending = pending.filter(t => t.id === id); } @@ -137,6 +180,8 @@ export class IgxBaseTransactionService i public clear(_id?: any): void { this._pendingStates.clear(); this._pendingTransactions = []; + this._validationStates.clear(); + this._validationTransactions = []; } /** @@ -144,26 +189,15 @@ export class IgxBaseTransactionService i */ public startPending(): void { this._isPending = true; - this.autoCommit = false; } /** * @inheritdoc */ public endPending(_commit: boolean): void { - if (this._isPending && !_commit) { - this._isPending = false; - this.autoCommit = true; - // reset last pending transaction. - const last = this._pendingTransactions.pop(); - if (last) { - this._pendingStates.delete(last.id); - } - } - if (_commit) { - this._pendingStates.clear(); - this._pendingTransactions = []; - } + this._isPending = false; + this._pendingStates.clear(); + this._pendingTransactions = []; } @@ -182,11 +216,9 @@ export class IgxBaseTransactionService i } else { state.value = transaction.newValue; } - this.updateValidity(state, transaction); } else { - state = { value: this.cloneStrategy.clone(transaction.newValue), recordRef, type: transaction.type, validity: transaction.validity } as S; + state = { value: this.cloneStrategy.clone(transaction.newValue), recordRef, type: transaction.type } as S; states.set(transaction.id, state); - state.validity = transaction.validity; } } @@ -198,16 +230,13 @@ export class IgxBaseTransactionService i */ protected updateValidity(state, transaction) { // update validity - const objKeys = Object.keys(state.value); - objKeys.forEach(x => { - const currentState = state.validity?.find(y => y.field === x); - const newState = transaction.validity?.find(y => y.field === x); - if (currentState && newState) { - // update existing - currentState.formGroup = newState.formGroup; - currentState.valid = newState.valid; - } else if (!currentState && transaction.validity) { - state.validity = (state.validity || []).concat(transaction.validity); + transaction.validity.forEach(validity => { + const existingState = state.validity.find(x => x.field === validity.field); + if (existingState) { + existingState.valid = validity.valid; + existingState.formGroup = validity.formGroup; + } else { + state.validity.push(validity); } }); } diff --git a/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts index 015543fb172..fa2b4c12ba7 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts @@ -59,7 +59,7 @@ export class IgxTransactionService exten const result: T[] = []; this._states.forEach((state: S, key: any) => { const value = mergeChanges ? this.mergeValues(state.recordRef, state.value) : state.value; - result.push({ id: key, newValue: value, type: state.type, validity: state.validity } as T); + result.push({ id: key, newValue: value, type: state.type } as T); }); return result; } @@ -268,7 +268,6 @@ export class IgxTransactionService exten states.delete(transaction.id); } else if (state.type === TransactionType.UPDATE) { state.value = transaction.newValue; - state.validity = transaction.validity; state.type = TransactionType.DELETE; } break; @@ -283,10 +282,9 @@ export class IgxTransactionService exten } else { state.value = transaction.newValue; } - this.updateValidity(state, transaction); } } else { - state = { value: this.cloneStrategy.clone(transaction.newValue), recordRef, type: transaction.type, validity: transaction.validity } as S; + state = { value: this.cloneStrategy.clone(transaction.newValue), recordRef, type: transaction.type } as S; states.set(transaction.id, state); } diff --git a/projects/igniteui-angular/src/lib/services/transaction/transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/transaction.ts index 8f7d553349f..cd58185013c 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/transaction.ts @@ -58,10 +58,6 @@ export interface HierarchicalState extends State { } export interface TransactionService { - /** - * Returns whether changes are automatically commited to the data without the need to explicitly. - */ - autoCommit: boolean; /** * Returns whether transaction is enabled for this service */ @@ -95,6 +91,10 @@ export interface TransactionService { */ add(transaction: T, recordRef?: any): void; + addValidation(transaction: T, recordRef?: any): void; + getAggregatedValidation(id: any): T; + + /** * Returns all recorded transactions in chronological order * From b5a1e31be00c9a03d3434cdb4185683d5e2997ba Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 16 Aug 2022 13:20:49 +0300 Subject: [PATCH 048/149] chore(*): Add logic for undo, redo, clear, commit for validation state collections. --- .../src/lib/services/transaction/base-transaction.ts | 12 +++++++++--- .../src/lib/services/transaction/igx-transaction.ts | 7 +++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts index 959d1826196..bb69c6f544c 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts @@ -177,11 +177,17 @@ export class IgxBaseTransactionService i /** * @inheritdoc */ - public clear(_id?: any): void { + public clear(id?: any): void { this._pendingStates.clear(); this._pendingTransactions = []; - this._validationStates.clear(); - this._validationTransactions = []; + if (id !== undefined) { + this._validationTransactions = this._validationTransactions.filter(t => t.id !== id); + this._validationStates.delete(id); + } else { + this._validationStates.clear(); + this._validationTransactions = []; + } + } /** diff --git a/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts index fa2b4c12ba7..0a835c7c7e8 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts @@ -144,6 +144,7 @@ export class IgxTransactionService exten * @inheritdoc */ public clear(id?: any): void { + super.clear(id); if (id !== undefined) { this._transactions = this._transactions.filter(t => t.id !== id); this._states.delete(id); @@ -171,12 +172,16 @@ export class IgxTransactionService exten const lastActions: Action[] = this._undoStack.pop(); this._transactions.splice(this._transactions.length - lastActions.length); + this._validationTransactions.splice(this._validationTransactions.length - lastActions.length); this._redoStack.push(lastActions); this._states.clear(); + this._validationStates.clear(); for (const currentActions of this._undoStack) { for (const transaction of currentActions) { this.updateState(this._states, transaction.transaction, transaction.recordRef); + const validationTransaction = this._validationTransactions.find(x => x.id === transaction.transaction.id); + this.updateValidationState(this._validationStates, validationTransaction, transaction.recordRef); } } @@ -191,7 +196,9 @@ export class IgxTransactionService exten const actions: Action[] = this._redoStack.pop(); for (const action of actions) { this.updateState(this._states, action.transaction, action.recordRef); + this.updateValidationState(this._validationStates, action.transaction, action.recordRef); this._transactions.push(action.transaction); + this._validationTransactions.push(action.transaction); } this._undoStack.push(actions); From 0568e65dce958b440eeeae4d33cbd6038b120714 Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 16 Aug 2022 13:27:46 +0300 Subject: [PATCH 049/149] chore(*): Add some null checks. --- .../services/transaction/base-transaction.ts | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts index bb69c6f544c..8191aaaef9c 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts @@ -236,15 +236,20 @@ export class IgxBaseTransactionService i */ protected updateValidity(state, transaction) { // update validity - transaction.validity.forEach(validity => { - const existingState = state.validity.find(x => x.field === validity.field); - if (existingState) { - existingState.valid = validity.valid; - existingState.formGroup = validity.formGroup; - } else { - state.validity.push(validity); - } - }); + if (transaction.validity) { + transaction.validity.forEach(validity => { + const existingState = state.validity?.find(x => x.field === validity.field); + if (existingState) { + existingState.valid = validity.valid; + existingState.formGroup = validity.formGroup; + } else { + if (!state.validity) { + state.validity = []; + } + state.validity.push(validity); + } + }); + } } /** From 69e565325d8aa9d933117fb468e54752e4484e98 Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 16 Aug 2022 14:49:25 +0300 Subject: [PATCH 050/149] chore(*): Update tests with edit event arg isValid. --- .../lib/grids/grid/grid-cell-editing.spec.ts | 25 +++++++++++++------ .../lib/grids/grid/grid-row-editing.spec.ts | 8 +++++- .../lib/grids/grid/grid.nested.props.spec.ts | 12 ++++++--- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-cell-editing.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-cell-editing.spec.ts index bdef18713fb..50e4739c131 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-cell-editing.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-cell-editing.spec.ts @@ -563,7 +563,7 @@ describe('IgxGrid - Cell Editing #grid', () => { $destroyer.next(true); })); - it(`Should properly emit 'cellEditEnter' event`, () => { + fit(`Should properly emit 'cellEditEnter' event`, () => { spyOn(grid.cellEditEnter, 'emit').and.callThrough(); const cell = grid.gridAPI.get_cell_by_index(0, 'fullName'); let initialRowData = {...cell.row.data}; @@ -578,6 +578,7 @@ describe('IgxGrid - Cell Editing #grid', () => { rowData: initialRowData, oldValue: 'John Brown', cancel: false, + isValid: true, column: cell.column, owner: grid, event: jasmine.anything() as any @@ -598,6 +599,7 @@ describe('IgxGrid - Cell Editing #grid', () => { rowID: cell2.row.key, rowData: initialRowData, oldValue: 20, + isValid: true, cancel: false, column: cell2.column, owner: grid, @@ -626,6 +628,7 @@ describe('IgxGrid - Cell Editing #grid', () => { rowData: initialRowData, oldValue: 'John Brown', cancel: true, + isValid: true, column: cell.column, owner: grid, event: jasmine.anything() as any @@ -658,7 +661,7 @@ describe('IgxGrid - Cell Editing #grid', () => { expect(cell.editMode).toBeFalsy(); }); - it(`Should properly emit 'cellEditExit' event`, () => { + fit(`Should properly emit 'cellEditExit' event`, () => { spyOn(grid.cellEditExit, 'emit').and.callThrough(); let cell = grid.gridAPI.get_cell_by_index(0, 'fullName'); let initialRowData = {...cell.row.data}; @@ -676,6 +679,7 @@ describe('IgxGrid - Cell Editing #grid', () => { cellID: cell.cellID, rowData: initialRowData, newValue: 'John Brown', + isValid: true, oldValue: 'John Brown', column: cell.column, owner: grid, @@ -698,6 +702,7 @@ describe('IgxGrid - Cell Editing #grid', () => { rowData: initialRowData, newValue: 20, oldValue: 20, + isValid: true, column: cell.column, owner: grid, event: jasmine.anything() as any @@ -764,7 +769,7 @@ describe('IgxGrid - Cell Editing #grid', () => { expect(grid.cellEdit.emit).toHaveBeenCalledWith(cellArgs); }); - it(`Should be able to cancel 'cellEdit' event`, fakeAsync(() => { + fit(`Should be able to cancel 'cellEdit' event`, fakeAsync(() => { const emitSpy = spyOn(grid.cellEdit, 'emit').and.callThrough(); grid.cellEdit.subscribe((e: IGridEditEventArgs) => { e.cancel = true; @@ -796,6 +801,7 @@ describe('IgxGrid - Cell Editing #grid', () => { cancel: true, column: cell.column, owner: grid, + isValid: true, event: undefined }; expect(grid.cellEdit.emit).toHaveBeenCalledTimes(1); @@ -942,7 +948,7 @@ describe('IgxGrid - Cell Editing #grid', () => { expect(grid.cellEditDone.emit).toHaveBeenCalledWith(cellArgs); }); - it(`Should properly emit 'cellEditExit' event`, () => { + fit(`Should properly emit 'cellEditExit' event`, () => { spyOn(grid.cellEditExit, 'emit').and.callThrough(); const cell = grid.gridAPI.get_cell_by_index(0, 'fullName'); const initialRowData = {...cell.row.data}; @@ -967,7 +973,8 @@ describe('IgxGrid - Cell Editing #grid', () => { newValue: 'New Name', column: cell.column, owner: grid, - event: jasmine.anything() as any + event: jasmine.anything() as any, + isValid: true }; expect(grid.cellEditExit.emit).toHaveBeenCalledTimes(1); expect(grid.cellEditExit.emit).toHaveBeenCalledWith(cellArgs); @@ -975,7 +982,7 @@ describe('IgxGrid - Cell Editing #grid', () => { expect(cell.editMode).toBe(false); }); - it(`Should properly emit 'cellEditDone' event`, () => { + fit(`Should properly emit 'cellEditDone' event`, () => { const doneSpy = spyOn(grid.cellEditDone, 'emit').and.callThrough(); let cellArgs: IGridEditDoneEventArgs; @@ -1004,7 +1011,8 @@ describe('IgxGrid - Cell Editing #grid', () => { newValue: firstNewValue, column: cell.column, owner: grid, - event: jasmine.anything() as any + event: jasmine.anything() as any, + isValid: true }; expect(grid.cellEditDone.emit).toHaveBeenCalledTimes(1); expect(grid.cellEditDone.emit).toHaveBeenCalledWith(cellArgs); @@ -1028,7 +1036,8 @@ describe('IgxGrid - Cell Editing #grid', () => { newValue: secondNewValue, column: cell.column, owner: grid, - event: jasmine.anything() as any + event: jasmine.anything() as any, + isValid: true }; expect(grid.cellEditDone.emit).toHaveBeenCalledTimes(2); expect(grid.cellEditDone.emit).toHaveBeenCalledWith(cellArgs); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts index fae5b648fc8..d9d0e0417c4 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts @@ -153,6 +153,7 @@ describe('IgxGrid - Row Editing #grid', () => { cancel: false, column: cell.column, owner: grid, + isValid: true, event: jasmine.anything() as any }; let rowEditArgs: IGridEditEventArgs = { @@ -160,6 +161,7 @@ describe('IgxGrid - Row Editing #grid', () => { rowData: initialRowData, oldValue: row.data, cancel: false, + isValid: true, owner: grid, isAddRow: row.addRowUI, event: jasmine.anything() as any @@ -177,6 +179,7 @@ describe('IgxGrid - Row Editing #grid', () => { rowID: cell.row.key, rowData: cell.row.data, oldValue: cell.value, + isValid: true, newValue: cell.value, column: cell.column, owner: grid, @@ -193,7 +196,7 @@ describe('IgxGrid - Row Editing #grid', () => { event: jasmine.anything() as any }; - expect(grid.cellEditExit.emit).toHaveBeenCalledWith(cellEditExitArgs); + expect(grid.cellEditExit.emit).toHaveBeenCalledWith(cellEditExitArgs); expect(grid.rowEditExit.emit).toHaveBeenCalledWith(rowEditExitArgs); UIInteractions.simulateDoubleClickAndSelectEvent(cellDebug); @@ -210,6 +213,7 @@ describe('IgxGrid - Row Editing #grid', () => { rowData: Object.assign({}, row.data, { ProductName: newCellValue }), oldValue: cell.value, newValue: newCellValue, + isValid: true, column: cell.column, owner: grid, event: jasmine.anything() as any @@ -226,6 +230,7 @@ describe('IgxGrid - Row Editing #grid', () => { cancel: false, owner: grid, isAddRow: row.addRowUI, + isValid: true, event: jasmine.anything() as any }; @@ -235,6 +240,7 @@ describe('IgxGrid - Row Editing #grid', () => { rowData: updatedRowData, // with rowEditable - IgxGridRowEditingComponent oldValue: cell.value, newValue: newCellValue, + isValid: true, column: cell.column, owner: grid, event: jasmine.anything() as any diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts index 7abd84b8879..13e17fe4127 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts @@ -539,7 +539,8 @@ describe('Edit cell with data of type Array #grid', () => { cancel: false, column: cell.column, owner: grid, - event: jasmine.anything() as any + event: jasmine.anything() as any, + isValid: true }; expect(grid.cellEditEnter.emit).toHaveBeenCalledTimes(1); @@ -601,7 +602,8 @@ describe('Edit cell with data of type Array #grid', () => { cancel: false, column: cell.column, owner: grid, - event: jasmine.anything() as any + event: jasmine.anything() as any, + isValid: true }; expect(grid.cellEditEnter.emit).toHaveBeenCalledTimes(1); @@ -668,7 +670,8 @@ describe('Edit cell with data of type Array #grid', () => { owner: grid, isAddRow: row.addRowUI, cancel: false, - event: jasmine.anything() as any + event: jasmine.anything() as any, + isValid: true }; expect(grid.rowEditEnter.emit).toHaveBeenCalledTimes(1); @@ -731,7 +734,8 @@ describe('Edit cell with data of type Array #grid', () => { owner: grid, isAddRow: row.addRowUI, cancel: false, - event: jasmine.anything() as any + event: jasmine.anything() as any, + isValid: true }; expect(grid.rowEditEnter.emit).toHaveBeenCalledTimes(1); From 48996f61a770b055e75770fdb30bc662bcfc399f Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 16 Aug 2022 14:53:07 +0300 Subject: [PATCH 051/149] chore(*): Remove fit. --- .../src/lib/grids/grid/grid-cell-editing.spec.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-cell-editing.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-cell-editing.spec.ts index 50e4739c131..421360a5a2d 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-cell-editing.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-cell-editing.spec.ts @@ -563,7 +563,7 @@ describe('IgxGrid - Cell Editing #grid', () => { $destroyer.next(true); })); - fit(`Should properly emit 'cellEditEnter' event`, () => { + it(`Should properly emit 'cellEditEnter' event`, () => { spyOn(grid.cellEditEnter, 'emit').and.callThrough(); const cell = grid.gridAPI.get_cell_by_index(0, 'fullName'); let initialRowData = {...cell.row.data}; @@ -661,7 +661,7 @@ describe('IgxGrid - Cell Editing #grid', () => { expect(cell.editMode).toBeFalsy(); }); - fit(`Should properly emit 'cellEditExit' event`, () => { + it(`Should properly emit 'cellEditExit' event`, () => { spyOn(grid.cellEditExit, 'emit').and.callThrough(); let cell = grid.gridAPI.get_cell_by_index(0, 'fullName'); let initialRowData = {...cell.row.data}; @@ -769,7 +769,7 @@ describe('IgxGrid - Cell Editing #grid', () => { expect(grid.cellEdit.emit).toHaveBeenCalledWith(cellArgs); }); - fit(`Should be able to cancel 'cellEdit' event`, fakeAsync(() => { + it(`Should be able to cancel 'cellEdit' event`, fakeAsync(() => { const emitSpy = spyOn(grid.cellEdit, 'emit').and.callThrough(); grid.cellEdit.subscribe((e: IGridEditEventArgs) => { e.cancel = true; @@ -948,7 +948,7 @@ describe('IgxGrid - Cell Editing #grid', () => { expect(grid.cellEditDone.emit).toHaveBeenCalledWith(cellArgs); }); - fit(`Should properly emit 'cellEditExit' event`, () => { + it(`Should properly emit 'cellEditExit' event`, () => { spyOn(grid.cellEditExit, 'emit').and.callThrough(); const cell = grid.gridAPI.get_cell_by_index(0, 'fullName'); const initialRowData = {...cell.row.data}; @@ -982,7 +982,7 @@ describe('IgxGrid - Cell Editing #grid', () => { expect(cell.editMode).toBe(false); }); - fit(`Should properly emit 'cellEditDone' event`, () => { + it(`Should properly emit 'cellEditDone' event`, () => { const doneSpy = spyOn(grid.cellEditDone, 'emit').and.callThrough(); let cellArgs: IGridEditDoneEventArgs; From c9aa52a0721ba7be4f00d4a63ab4471f68f77dde Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 16 Aug 2022 14:57:15 +0300 Subject: [PATCH 052/149] chore(*): Add check in case transaction has no matching validation. --- .../src/lib/services/transaction/base-transaction.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts index 8191aaaef9c..d38d7e7f45b 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts @@ -97,6 +97,7 @@ export class IgxBaseTransactionService i } public updateValidationState(states: Map, transaction: T, recordRef?: any): void { + if (!transaction) return; let state = states.get(transaction.id); if (state) { if (isObject(state.value)) { From d95e83e46eabeb646c72eb127993b6ea13dae72e Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 16 Aug 2022 15:50:37 +0300 Subject: [PATCH 053/149] chore(*): update more tests. --- .../src/lib/grids/grid/grid-cell-editing.spec.ts | 9 ++++++--- .../src/lib/grids/grid/grid-row-editing.spec.ts | 3 ++- .../src/lib/grids/grid/grid.nested.props.spec.ts | 3 ++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-cell-editing.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-cell-editing.spec.ts index 421360a5a2d..75a13182083 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-cell-editing.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-cell-editing.spec.ts @@ -654,7 +654,8 @@ describe('IgxGrid - Cell Editing #grid', () => { cancel: true, column: cell.column, owner: grid, - event: jasmine.anything() as any + event: jasmine.anything() as any, + isValid: true }; expect(grid.cellEditEnter.emit).toHaveBeenCalledTimes(2); expect(grid.cellEditEnter.emit).toHaveBeenCalledWith(cellArgs); @@ -738,7 +739,8 @@ describe('IgxGrid - Cell Editing #grid', () => { cancel: false, column: cell.column, owner: grid, - event: jasmine.anything() as any + event: jasmine.anything() as any, + isValid: true }; expect(grid.cellEdit.emit).toHaveBeenCalledTimes(1); expect(grid.cellEdit.emit).toHaveBeenCalledWith(cellArgs); @@ -763,7 +765,8 @@ describe('IgxGrid - Cell Editing #grid', () => { cancel: false, column: cell.column, owner: grid, - event: jasmine.anything() as any + event: jasmine.anything() as any, + isValid: true }; expect(grid.cellEdit.emit).toHaveBeenCalledTimes(2); expect(grid.cellEdit.emit).toHaveBeenCalledWith(cellArgs); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts index d9d0e0417c4..5096cd7cb07 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts @@ -1951,7 +1951,8 @@ describe('IgxGrid - Row Editing #grid', () => { cancel: false, column: cell.column, owner: grid, - event: jasmine.anything() as any + event: jasmine.anything() as any, + isValid: true }; UIInteractions.simulateDoubleClickAndSelectEvent(cellElem); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts index 13e17fe4127..d8a8f68e5d9 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts @@ -701,6 +701,7 @@ describe('Edit cell with data of type Array #grid', () => { expect(rowArgs.newValue.locations.length).toEqual(3); expect(rowArgs.oldValue.locations.length).toEqual(3); + delete rowArgs.isValid; expect(grid.rowEditExit.emit).toHaveBeenCalledTimes(1); expect(grid.rowEditExit.emit).toHaveBeenCalledWith(rowArgs); @@ -769,7 +770,7 @@ describe('Edit cell with data of type Array #grid', () => { delete rowArgs.cancel; rowArgs.rowData = initialRowData; - + delete rowArgs.isValid; expect(grid.rowEditDone.emit).toHaveBeenCalledTimes(1); expect(grid.rowEditDone.emit).toHaveBeenCalledWith(rowArgs); From 91f638057ffa9ea7e5a4956f51346a4f0ba7845e Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 16 Aug 2022 16:13:43 +0300 Subject: [PATCH 054/149] chore(*): Blur only if needed. --- projects/igniteui-angular/src/lib/grids/cell.component.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 28a1e735bec..270623be359 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -1106,7 +1106,9 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT if (this.editable && editMode && !this.intRow.deleted) { if (editableCell) { - this.grid.tbody.nativeElement.focus({ preventScroll: true }); + if (this.grid.validationTrigger === 'blur') { + this.grid.tbody.nativeElement.focus({ preventScroll: true }); + } editableArgs = this.grid.crudService.updateCell(false, event); /* This check is related with the following issue #6517: From f8db035d8c810eeec55c4edb746f3cb8d0115447 Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 16 Aug 2022 16:50:11 +0300 Subject: [PATCH 055/149] chore(*): Fix check. --- projects/igniteui-angular/src/lib/grids/cell.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 270623be359..2d4c4ca9fef 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -484,7 +484,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT @HostBinding('attr.aria-invalid') public get isInvalid() { const isRowEdit = this.grid.crudService.rowEditing; - if (isRowEdit && this.row.inEditMode || this.editMode) { + if ((isRowEdit && (this.row && this.row.inEditMode) || this.editMode)) { return this.formGroup?.get(this.column?.field)?.invalid; } else { return !!this.validity ? !this.validity.valid : false; From 3d1a092a412079945ee8a30efb836692c4d5a1dc Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 16 Aug 2022 17:08:25 +0300 Subject: [PATCH 056/149] chore(*): Use the row's inEditMode. --- projects/igniteui-angular/src/lib/grids/cell.component.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 2d4c4ca9fef..d79e5496bee 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -483,8 +483,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT @HostBinding('class.igx-grid__td--invalid') @HostBinding('attr.aria-invalid') public get isInvalid() { - const isRowEdit = this.grid.crudService.rowEditing; - if ((isRowEdit && (this.row && this.row.inEditMode) || this.editMode)) { + if (this.intRow.inEditMode || this.editMode) { return this.formGroup?.get(this.column?.field)?.invalid; } else { return !!this.validity ? !this.validity.valid : false; From eb574d65c95838ef98e40d341f0ef3c657ed625a Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 16 Aug 2022 17:56:41 +0300 Subject: [PATCH 057/149] chore(*): Update more tests. --- .../src/lib/grids/common/crud.service.ts | 2 +- .../lib/grids/grid/grid-row-editing.spec.ts | 23 ++++++++++++++----- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts index fada2931a56..948e580bbfe 100644 --- a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts +++ b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts @@ -15,7 +15,7 @@ export class IgxEditRow { constructor(public id: any, public index: number, public data: any, public grid: GridType) { for (const col of grid.columns) { const field = col.field; - const control = new FormControl(this.data[field], { updateOn: grid.validationTrigger }); + const control = new FormControl(this.data ? this.data[field] : {}, { updateOn: grid.validationTrigger }); control.addValidators(col.validators); this.rowFormGroup.addControl(field, control); } diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts index 5096cd7cb07..b2f920a6a1e 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts @@ -1723,6 +1723,7 @@ describe('IgxGrid - Row Editing #grid', () => { cancel: false, owner: grid, isAddRow: false, + isValid: true, event: jasmine.anything() as any }); }); @@ -1762,7 +1763,8 @@ describe('IgxGrid - Row Editing #grid', () => { cancel: true, owner: grid, isAddRow: false, - event: jasmine.anything() as any + event: jasmine.anything() as any, + isValid: true }); // Enter cell edit mode again @@ -1786,7 +1788,8 @@ describe('IgxGrid - Row Editing #grid', () => { cancel: true, owner: grid, isAddRow: false, - event: jasmine.anything() as any + event: jasmine.anything() as any, + isValid: true }); }); @@ -1841,7 +1844,8 @@ describe('IgxGrid - Row Editing #grid', () => { cancel: false, owner: grid, isAddRow: false, - event: jasmine.anything() as any + event: jasmine.anything() as any, + isValid: true }); }); @@ -1871,7 +1875,8 @@ describe('IgxGrid - Row Editing #grid', () => { cancel: true, owner: grid, isAddRow: false, - event: jasmine.anything() as any + event: jasmine.anything() as any, + isValid: true }); }); @@ -2155,7 +2160,8 @@ describe('IgxGrid - Row Editing #grid', () => { newValue: newCellValue, column: cell.column, owner: grid, - event: jasmine.anything() as any + event: jasmine.anything() as any, + isValid: true }; const rowDoneArgs: IGridEditDoneEventArgs = { @@ -2782,7 +2788,12 @@ describe('IgxGrid - Row Editing #grid', () => { expect(trans.add).toHaveBeenCalled(); expect(trans.add).toHaveBeenCalledTimes(1); - expect(trans.add).toHaveBeenCalledWith({ id: 3, type: 'update', newValue: { ProductName: 'Updated Cell' }}, grid.data[2]); + expect(trans.add).toHaveBeenCalledWith({ + id: 3, + type: 'update', + newValue: { ProductName: 'Updated Cell' }, + validity: trans.getTransactionLog()[0].validity + }, grid.data[2]); expect(grid.data.length).toBe(10); }); From 5f5ebb6c322d9a93cabaa5d4ac103c738dd23c58 Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 16 Aug 2022 18:38:29 +0300 Subject: [PATCH 058/149] chore(*): Update test and also update another flickering test. --- .../lib/grids/grid/grid-row-editing.spec.ts | 29 +++++++++++-------- .../src/lib/grids/grid/grid.search.spec.ts | 6 ++-- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts index b2f920a6a1e..3d9469cff6f 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts @@ -126,7 +126,7 @@ describe('IgxGrid - Row Editing #grid', () => { it('Emit all events with proper arguments', () => { const row = grid.gridAPI.get_row_by_index(2); - const initialRowData = {...cell.row.data}; + const initialRowData = { ...cell.row.data }; const newCellValue = 'Aaaaa'; const updatedRowData = Object.assign({}, row.data, { ProductName: newCellValue }); @@ -196,7 +196,7 @@ describe('IgxGrid - Row Editing #grid', () => { event: jasmine.anything() as any }; - expect(grid.cellEditExit.emit).toHaveBeenCalledWith(cellEditExitArgs); + expect(grid.cellEditExit.emit).toHaveBeenCalledWith(cellEditExitArgs); expect(grid.rowEditExit.emit).toHaveBeenCalledWith(rowEditExitArgs); UIInteractions.simulateDoubleClickAndSelectEvent(cellDebug); @@ -499,7 +499,7 @@ describe('IgxGrid - Row Editing #grid', () => { fix.detectChanges(); await wait(DEBOUNCETIME); - targetCell = grid.gridAPI.get_cell_by_index(0, 'Test'); + targetCell = grid.gridAPI.get_cell_by_index(0, 'Test'); UIInteractions.simulateClickAndSelectEvent(targetCell); fix.detectChanges(); @@ -1548,7 +1548,7 @@ describe('IgxGrid - Row Editing #grid', () => { cell = grid.getCellByColumn(0, 'ProductName'); cellElem = grid.gridAPI.get_cell_by_index(0, 'ProductName'); initialRow = grid.getRowByIndex(0); - initialData = {...initialRow.data}; + initialData = { ...initialRow.data }; fix.componentInstance.pinnedFlag = true; fix.detectChanges(); })); @@ -1690,7 +1690,7 @@ describe('IgxGrid - Row Editing #grid', () => { const doneButtonElement = GridFunctions.getRowEditingDoneButton(fix); doneButtonElement.click(); - const rowData = Object.assign({}, cell.row.data, {ProductName: 'New Name'}); + const rowData = Object.assign({}, cell.row.data, { ProductName: 'New Name' }); expect(!!grid.gridAPI.crudService.rowInEditMode).toEqual(true); expect(grid.gridAPI.crudService.cellInEditMode).toEqual(false); expect(cell.row.data).not.toEqual(rowData); @@ -2766,7 +2766,7 @@ describe('IgxGrid - Row Editing #grid', () => { expect(trans.add).toHaveBeenCalled(); expect(trans.add).toHaveBeenCalledTimes(1); - expect(trans.add).toHaveBeenCalledWith({ id: 100, type: 'add', newValue: addRowData}); + expect(trans.add).toHaveBeenCalledWith({ id: 100, type: 'add', newValue: addRowData }); expect(grid.data.length).toBe(10); }); @@ -2788,12 +2788,12 @@ describe('IgxGrid - Row Editing #grid', () => { expect(trans.add).toHaveBeenCalled(); expect(trans.add).toHaveBeenCalledTimes(1); - expect(trans.add).toHaveBeenCalledWith({ + expect(trans.add).toHaveBeenCalledWith({ id: 3, - type: 'update', - newValue: { ProductName: 'Updated Cell' }, - validity: trans.getTransactionLog()[0].validity - }, grid.data[2]); + type: 'update', + newValue: { ProductName: 'Updated Cell' }, + validity: trans.getTransactionLog()[0].validity + }, grid.data[2]); expect(grid.data.length).toBe(10); }); @@ -2813,7 +2813,12 @@ describe('IgxGrid - Row Editing #grid', () => { expect(trans.add).toHaveBeenCalled(); expect(trans.add).toHaveBeenCalledTimes(1); - expect(trans.add).toHaveBeenCalledWith({ id: 3, type: 'update', newValue: updateRowData }, oldRowData); + expect(trans.add).toHaveBeenCalledWith({ + id: 3, + type: 'update', + newValue: updateRowData, + validity: trans.getTransactionLog()[0].validity + }, oldRowData); expect(grid.data[2]).toBe(oldRowData); }); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.search.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.search.spec.ts index 4794cdab2b3..099a8a8759e 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.search.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.search.spec.ts @@ -1111,7 +1111,7 @@ describe('IgxGrid - search API #grid - ', () => { }); fix.detectChanges(); grid.findNext('Casey'); - await wait(30); + await wait(100); fix.detectChanges(); let row = grid.gridAPI.get_row_by_index(17); let spans = row.nativeElement.querySelectorAll(HIGHLIGHT_CSS_CLASS); @@ -1120,7 +1120,7 @@ describe('IgxGrid - search API #grid - ', () => { grid.toggleAllGroupRows(); fix.detectChanges(); (grid as any).scrollTo(0, 0); - await wait(); + await wait(100); fix.detectChanges(); grid.toggleGroup(grid.groupsRecords[0]); fix.detectChanges(); @@ -1128,7 +1128,7 @@ describe('IgxGrid - search API #grid - ', () => { fix.detectChanges(); grid.findNext('Casey'); - await wait(); + await wait(100); fix.detectChanges(); row = grid.gridAPI.get_row_by_index(11); spans = row.nativeElement.querySelectorAll(HIGHLIGHT_CSS_CLASS); From f0a158610b4123e51be11dd64d6da615520a83eb Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 17 Aug 2022 11:20:46 +0300 Subject: [PATCH 059/149] chore(*): Add api docs for new methods in the base transaction service. --- .../src/lib/grids/cell.component.ts | 2 +- .../services/transaction/base-transaction.ts | 41 +++++++++++-------- .../lib/services/transaction/transaction.ts | 22 +++++++++- 3 files changed, 45 insertions(+), 20 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index d79e5496bee..745037c1f71 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -501,7 +501,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT } private get validity() { - const state = this.grid.transactions.getAggregatedValidation(this.intRow.key); + const state = this.grid.transactions.getAggregatedValidationState(this.intRow.key); if (state && state.validity && state.validity.some(x => x.valid === false)) { return state.validity.find(x => x.field === this.column.field); } diff --git a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts index d38d7e7f45b..cafb81f67d2 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts @@ -96,7 +96,15 @@ export class IgxBaseTransactionService i this._validationTransactions.push(transaction); } - public updateValidationState(states: Map, transaction: T, recordRef?: any): void { + + /** + * Updates the provided validation states collection according to passed transaction and recordRef + * + * @param states States collection to apply the update to + * @param transaction Transaction to apply to the current state + * @param recordRef Reference to the value of the record in data source, if any, where transaction should be applied + */ + protected updateValidationState(states: Map, transaction: T, recordRef?: any): void { if (!transaction) return; let state = states.get(transaction.id); if (state) { @@ -116,25 +124,24 @@ export class IgxBaseTransactionService i /** * @inheritdoc */ - public getAggregatedValidationChanges(mergeChanges: boolean): T[] { + public getAggregatedValidationChanges(): T[] { const result: T[] = []; this._validationStates.forEach((state: S, key: any) => { - const value = mergeChanges ? this.getAggregatedValue(key, mergeChanges) : state.value; - result.push({ id: key, newValue: value, type: state.type, validity: state.validity } as T); + result.push({ id: key, newValue: state.value, type: state.type, validity: state.validity } as T); }); return result; } - /** - * @inheritdoc - */ - public getAggregatedValidation(id: any): any { - const state = this._validationStates.get(id); - if (!state) { - return null; - } - return state; + /** + * @inheritdoc + */ + public getAggregatedValidationState(id: any): any { + const state = this._validationStates.get(id); + if (!state) { + return null; } + return state; + } /** * @inheritdoc @@ -168,11 +175,11 @@ export class IgxBaseTransactionService i * @inheritdoc */ public getInvalidTransactionLog(id?: any) { - let pending = [...this._validationTransactions]; + let validationTransactions = [...this._validationTransactions]; if (id !== undefined) { - pending = pending.filter(t => t.id === id); + validationTransactions = validationTransactions.filter(t => t.id === id); } - return pending.filter(x => x.validity.some(y => y.valid === false)); + return validationTransactions.filter(x => x.validity.some(y => y.valid === false)); } /** @@ -188,7 +195,7 @@ export class IgxBaseTransactionService i this._validationStates.clear(); this._validationTransactions = []; } - + } /** diff --git a/projects/igniteui-angular/src/lib/services/transaction/transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/transaction.ts index cd58185013c..a40d2bd72ea 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/transaction.ts @@ -91,9 +91,27 @@ export interface TransactionService { */ add(transaction: T, recordRef?: any): void; + /** + * Adds provided transaction with validation status. + * + * @param transaction Transaction to be added + * @param recordRef Reference to the value of the record in the data source related to the changed item + */ addValidation(transaction: T, recordRef?: any): void; - getAggregatedValidation(id: any): T; + /** + * Returns the validation state of the record with provided id + * + * @param id The id of the record + * @returns State of the record if any + */ + getAggregatedValidationState(id: any): T; + + /** + * Returns aggregated validation changes from all transactions + * @returns The states with the validation status. + */ + getAggregatedValidationChanges(): T[]; /** * Returns all recorded transactions in chronological order @@ -108,7 +126,7 @@ export interface TransactionService { */ undo(): void; - /** + /** * Returns invalid transactions. */ getInvalidTransactionLog(id?: any): T[]; From dd7f8176d990050b5ed84fb94ceee9d9bee7177f Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 17 Aug 2022 11:23:17 +0300 Subject: [PATCH 060/149] chore(*): add missed inheritdoc tag. --- .../src/lib/services/transaction/base-transaction.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts index cafb81f67d2..b182a525d77 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts @@ -91,6 +91,9 @@ export class IgxBaseTransactionService i return result; } + /** + * @inheritdoc + */ public addValidation(transaction: T, recordRef?: any): void { this.updateValidationState(this._validationStates, transaction, recordRef); this._validationTransactions.push(transaction); From b9e5eb8ccc2e7853c51e98f904391fbff68c986b Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 17 Aug 2022 12:50:25 +0300 Subject: [PATCH 061/149] chore(*): Fix warning for ngModel and formConrol's integration. Use the recommended way for changing values by subscribing the formControl valueChanges instead of binding via ngModel. --- .../src/lib/grids/cell.component.html | 30 +++++++++---------- .../src/lib/grids/cell.component.ts | 15 +++++++++- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.html b/projects/igniteui-angular/src/lib/grids/cell.component.html index bc75ac2fd4c..dde06d79fae 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/cell.component.html @@ -47,13 +47,13 @@ }}
- - + + @@ -61,13 +61,13 @@ - + + [disableRipple]="true" [formControl]="formControl"> + [formControl]="formControl"> - + {{ currencyCodeSymbol }} - + {{ currencyCodeSymbol }} - + {{ editValue | percent:column.pipeArgs.digitsInfo:grid.locale }} diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 745037c1f71..c08916ce568 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -36,7 +36,7 @@ import { ISelectionNode } from './common/types'; import { IgxTooltipDirective } from '../directives/tooltip'; import { AutoPositionStrategy, HorizontalAlignment, VerticalAlignment } from '../services/public_api'; import { IgxIconComponent } from '../icon/icon.component'; -import { first } from 'rxjs/operators'; +import { first, takeWhile } from 'rxjs/operators'; import { FormGroup } from '@angular/forms'; /** @@ -500,6 +500,13 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT return this.editMode && formControl && !formControl.invalid && formControl.dirty; } + /** + * Gets the formControl responsible for value changes and validation for this cell. + */ + public get formControl(){ + return this.formGroup?.get(this.column.field); + } + private get validity() { const state = this.grid.transactions.getAggregatedValidationState(this.intRow.key); if (state && state.validity && state.validity.some(x => x.valid === false)) { @@ -868,6 +875,12 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT * @internal */ public ngOnChanges(changes: SimpleChanges): void { + if (changes.editMode && changes.editMode.currentValue && this.formControl) { + // while in edit mode subscribe to value changes on the current form control and set to editValue + this.formControl.valueChanges.pipe(takeWhile(x => this.editMode)).subscribe(value => { + this.editValue = value; + }); + } if (changes.value && !changes.value.firstChange) { if (this.highlight) { this.highlight.lastSearchInfo.searchedText = this.grid.lastSearchInfo.searchText; From 34460703cf1cdc3f76c5e87a7241a5ba0ef6a2cb Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 17 Aug 2022 13:00:09 +0300 Subject: [PATCH 062/149] chore(*): Fix types. --- projects/igniteui-angular/src/lib/grids/cell.component.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index c08916ce568..aec023b7a91 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -37,7 +37,7 @@ import { IgxTooltipDirective } from '../directives/tooltip'; import { AutoPositionStrategy, HorizontalAlignment, VerticalAlignment } from '../services/public_api'; import { IgxIconComponent } from '../icon/icon.component'; import { first, takeWhile } from 'rxjs/operators'; -import { FormGroup } from '@angular/forms'; +import { FormControl, FormGroup } from '@angular/forms'; /** * Providing reference to `IgxGridCellComponent`: @@ -503,8 +503,8 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT /** * Gets the formControl responsible for value changes and validation for this cell. */ - public get formControl(){ - return this.formGroup?.get(this.column.field); + public get formControl(): FormControl { + return this.formGroup?.get(this.column.field) as FormControl; } private get validity() { From ce2ad87ffa9e01886c02773a3d3bd147bdf0e4aa Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 17 Aug 2022 13:33:01 +0300 Subject: [PATCH 063/149] chore(*): Return default form control in case formGroup is not populated yet. --- projects/igniteui-angular/src/lib/grids/cell.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index aec023b7a91..0a60bead22d 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -504,7 +504,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT * Gets the formControl responsible for value changes and validation for this cell. */ public get formControl(): FormControl { - return this.formGroup?.get(this.column.field) as FormControl; + return (this.formGroup?.get(this.column.field) || new FormControl(this.column.field)) as FormControl; } private get validity() { From 5a20acedb502bc75da5232d36da133126c86e1e3 Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 17 Aug 2022 15:47:39 +0300 Subject: [PATCH 064/149] chore(*): Add note in changelog for grid validation. --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ee1866a57b..02eda76292b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,14 @@ All notable changes for each version of this project will be documented in this ``` + - Added reactive forms style validation for grid editing. This extends the [Angular's reactive forms](https://angular.io/guide/form-validation#validating-input-in-reactive-forms) validation functionality + You can configure it in 2 ways: + 1. Via template-driven configuration on the `igx-column` of the grid. + 2. Via reactive forms using the FormGroup exposed via the `onFormGroupCreate` event of the grid. + + Edited cells wil enter an invalid state when validation fails, will be marked with `igx-grid__td--invalid` class and will display an error icon with additional error message. Cell will remain in that state until the value is edited to a valid value or the related transaction in the transaction service are commited or cleared. + Additional Apis have been exposed on the base transaction service to get transactions and states related to validity. + ## 14.0.0 From c5fae4fc0909e1b82a322df220fa87d147fc670f Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 17 Aug 2022 17:52:35 +0300 Subject: [PATCH 065/149] chore(*): Expose formControl in the public cell api since otherwise the edit template won't recogize it. --- .../igniteui-angular/src/lib/grids/grid-public-cell.ts | 9 +++++++++ .../grid-validation.sample.component.html | 5 ++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid-public-cell.ts b/projects/igniteui-angular/src/lib/grids/grid-public-cell.ts index 1319fe4d48e..cda1797f344 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-public-cell.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-public-cell.ts @@ -1,6 +1,7 @@ import { CellType, ColumnType, GridType, RowType } from './common/grid.interface'; import { ISelectionNode } from './common/types'; import { resolveNestedPath } from '../core/utils'; +import { FormControl } from '@angular/forms'; export class IgxGridCell implements CellType { @@ -74,6 +75,14 @@ export class IgxGridCell implements CellType { } } + /** + * Gets the formControl responsible for value changes and validation for this cell. + */ + public get formControl(): FormControl { + const editRow = this.grid.crudService.row || this.grid.crudService.cell.row; + return editRow.rowFormGroup.get(this.column.field); + } + /** * Sets the current edit value while a cell is in edit mode. * Only for cell editing mode. diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index 4279676a42e..aab295412b9 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -6,7 +6,10 @@

Grid without transactions

[width]="'1200px'" [height]="'800px'"> - + From cc9b03e6304bc76ba03dd01884c1946490c3bfdb Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 18 Aug 2022 17:10:45 +0300 Subject: [PATCH 066/149] chore(*): Add aria-errormessage to cell and to accessible inputs. --- .../src/lib/grids/cell.component.html | 10 +++---- .../src/lib/grids/cell.component.ts | 26 ++++++++++--------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.html b/projects/igniteui-angular/src/lib/grids/cell.component.html index dde06d79fae..ec5a1f2505c 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/cell.component.html @@ -51,7 +51,7 @@ - @@ -90,21 +90,21 @@
- {{ currencyCodeSymbol }} - {{ currencyCodeSymbol }} - {{ editValue | percent:column.pipeArgs.digitsInfo:grid.locale }} diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 0a60bead22d..507323246d8 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -72,7 +72,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT * @hidden * @internal */ - @ViewChildren('error', {read: IgxTooltipDirective}) + @ViewChildren('error', { read: IgxTooltipDirective }) public errorTooltip: QueryList; /** @@ -100,7 +100,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT public column: ColumnType; - public get formGroup() : FormGroup { + public get formGroup(): FormGroup { const isRowEdit = this.grid.crudService.rowEditing; const editRow = isRowEdit ? this.grid.crudService.row : this.grid.crudService.cell?.row; const id = isRowEdit ? editRow?.id : editRow?.id.rowID; @@ -469,11 +469,13 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT /** @hidden @internal */ @HostBinding('attr.aria-describedby') public get describeBy() { - let id = this.grid.id + '_' + this.column.field; - if (this.isInvalid) { - id += '_' + this.row.index + '_error'; - } - return id; + return this.grid.id + '_' + this.column.field; + } + + /** @hidden @internal */ + @HostBinding('attr.aria-errormessage') + public get ariaErrorMessage() { + return this.grid.id + '_' + this.column.field + '_' + this.row.index + '_error'; } /** @@ -486,7 +488,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT if (this.intRow.inEditMode || this.editMode) { return this.formGroup?.get(this.column?.field)?.invalid; } else { - return !!this.validity ? !this.validity.valid : false; + return !!this.validity ? !this.validity.valid : false; } } @@ -510,7 +512,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT private get validity() { const state = this.grid.transactions.getAggregatedValidationState(this.intRow.key); if (state && state.validity && state.validity.some(x => x.valid === false)) { - return state.validity.find(x => x.field === this.column.field); + return state.validity.find(x => x.field === this.column.field); } } @@ -814,7 +816,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT cssProps: {} /* don't disable user-select, etc */ } as HammerOptions); } - + } public ngAfterViewInit() { @@ -846,7 +848,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT { target: this.errorIcon.el.nativeElement, closeOnOutsideClick: true, - excludeFromOutsideClick: [ this.nativeElement ], + excludeFromOutsideClick: [this.nativeElement], closeOnEscape: false, outlet: this.grid.outlet, modal: false, @@ -984,7 +986,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT * @internal */ public focusout = () => { - this.closeErrorTooltip(); + this.closeErrorTooltip(); } private closeErrorTooltip() { From 4c8b7d71de91f72fdcfc48e0430e2be104e4e918 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 18 Aug 2022 17:29:21 +0300 Subject: [PATCH 067/149] chore(*): Change default messages to match the ones from the igGrid. --- .../src/i18n/BG/grid-resources.ts | 14 +++++++------- .../src/i18n/CS/grid-resources.ts | 14 +++++++------- .../src/i18n/DA/grid-resources.ts | 14 +++++++------- .../src/i18n/DE/grid-resources.ts | 14 +++++++------- .../src/i18n/ES/grid-resources.ts | 14 +++++++------- .../src/i18n/FR/grid-resources.ts | 14 +++++++------- .../src/i18n/HU/grid-resources.ts | 14 +++++++------- .../src/i18n/IT/grid-resources.ts | 14 +++++++------- .../src/i18n/JA/grid-resources.ts | 14 +++++++------- .../src/i18n/KO/grid-resources.ts | 14 +++++++------- .../src/i18n/NB/grid-resources.ts | 14 +++++++------- .../src/i18n/NL/grid-resources.ts | 14 +++++++------- .../src/i18n/PL/grid-resources.ts | 14 +++++++------- .../src/i18n/PT/grid-resources.ts | 14 +++++++------- .../src/i18n/RO/grid-resources.ts | 14 +++++++------- .../src/i18n/SV/grid-resources.ts | 14 +++++++------- .../src/i18n/TR/grid-resources.ts | 14 +++++++------- .../src/i18n/ZH-HANS/grid-resources.ts | 14 +++++++------- .../src/i18n/ZH-HANT/grid-resources.ts | 14 +++++++------- .../src/lib/core/i18n/grid-resources.ts | 14 +++++++------- .../src/lib/grids/cell.component.html | 2 +- 21 files changed, 141 insertions(+), 141 deletions(-) diff --git a/projects/igniteui-angular-i18n/src/i18n/BG/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/BG/grid-resources.ts index e5d7ee1898d..41697ec395e 100644 --- a/projects/igniteui-angular-i18n/src/i18n/BG/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/BG/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsBG_: ExpandRequire = { igx_grid_pivot_selector_values: 'Стойнoсти', igx_grid_pivot_selector_panel_empty: 'Привлачи тук', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'Field is required.', - igx_grid_min_validation_error: 'Field must be more than {0}', - igx_grid_max_validation_error: 'Field must be less than {0}', - igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', - igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', - igx_grid_email_validation_error: 'Field must contain a valid email address.', - igx_grid_pattern_validation_error: 'Field must match pattern {0}' + igx_grid_required_validation_error: 'This field is required', + igx_grid_min_validation_error: 'A value of at least {0} should be entered', + igx_grid_max_validation_error: 'A value no more than {0} should be entered', + igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', + igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', + igx_grid_email_validation_error: 'A valid email address should be entered', + igx_grid_pattern_validation_error: 'Entry does not match the required pattern' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/CS/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/CS/grid-resources.ts index 32bc4f44a7a..f5b934e33d2 100644 --- a/projects/igniteui-angular-i18n/src/i18n/CS/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/CS/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsCS_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'Field is required.', - igx_grid_min_validation_error: 'Field must be more than {0}', - igx_grid_max_validation_error: 'Field must be less than {0}', - igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', - igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', - igx_grid_email_validation_error: 'Field must contain a valid email address.', - igx_grid_pattern_validation_error: 'Field must match pattern {0}' + igx_grid_required_validation_error: 'This field is required', + igx_grid_min_validation_error: 'A value of at least {0} should be entered', + igx_grid_max_validation_error: 'A value no more than {0} should be entered', + igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', + igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', + igx_grid_email_validation_error: 'A valid email address should be entered', + igx_grid_pattern_validation_error: 'Entry does not match the required pattern' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/DA/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/DA/grid-resources.ts index 31cfaaee5eb..7edc467d7f0 100644 --- a/projects/igniteui-angular-i18n/src/i18n/DA/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/DA/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsDA_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'Field is required.', - igx_grid_min_validation_error: 'Field must be more than {0}', - igx_grid_max_validation_error: 'Field must be less than {0}', - igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', - igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', - igx_grid_email_validation_error: 'Field must contain a valid email address.', - igx_grid_pattern_validation_error: 'Field must match pattern {0}' + igx_grid_required_validation_error: 'This field is required', + igx_grid_min_validation_error: 'A value of at least {0} should be entered', + igx_grid_max_validation_error: 'A value no more than {0} should be entered', + igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', + igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', + igx_grid_email_validation_error: 'A valid email address should be entered', + igx_grid_pattern_validation_error: 'Entry does not match the required pattern' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/DE/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/DE/grid-resources.ts index 25727aba6db..fabeaefa8f7 100644 --- a/projects/igniteui-angular-i18n/src/i18n/DE/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/DE/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsDE_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'Field is required.', - igx_grid_min_validation_error: 'Field must be more than {0}', - igx_grid_max_validation_error: 'Field must be less than {0}', - igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', - igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', - igx_grid_email_validation_error: 'Field must contain a valid email address.', - igx_grid_pattern_validation_error: 'Field must match pattern {0}' + igx_grid_required_validation_error: 'This field is required', + igx_grid_min_validation_error: 'A value of at least {0} should be entered', + igx_grid_max_validation_error: 'A value no more than {0} should be entered', + igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', + igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', + igx_grid_email_validation_error: 'A valid email address should be entered', + igx_grid_pattern_validation_error: 'Entry does not match the required pattern' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/ES/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/ES/grid-resources.ts index f437cc1b566..4fa9d0e758b 100644 --- a/projects/igniteui-angular-i18n/src/i18n/ES/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/ES/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsES_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'Field is required.', - igx_grid_min_validation_error: 'Field must be more than {0}', - igx_grid_max_validation_error: 'Field must be less than {0}', - igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', - igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', - igx_grid_email_validation_error: 'Field must contain a valid email address.', - igx_grid_pattern_validation_error: 'Field must match pattern {0}' + igx_grid_required_validation_error: 'This field is required', + igx_grid_min_validation_error: 'A value of at least {0} should be entered', + igx_grid_max_validation_error: 'A value no more than {0} should be entered', + igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', + igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', + igx_grid_email_validation_error: 'A valid email address should be entered', + igx_grid_pattern_validation_error: 'Entry does not match the required pattern' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/FR/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/FR/grid-resources.ts index 7d1c5a0e9ac..a5370f437e1 100644 --- a/projects/igniteui-angular-i18n/src/i18n/FR/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/FR/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsFR_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'Field is required.', - igx_grid_min_validation_error: 'Field must be more than {0}', - igx_grid_max_validation_error: 'Field must be less than {0}', - igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', - igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', - igx_grid_email_validation_error: 'Field must contain a valid email address.', - igx_grid_pattern_validation_error: 'Field must match pattern {0}' + igx_grid_required_validation_error: 'This field is required', + igx_grid_min_validation_error: 'A value of at least {0} should be entered', + igx_grid_max_validation_error: 'A value no more than {0} should be entered', + igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', + igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', + igx_grid_email_validation_error: 'A valid email address should be entered', + igx_grid_pattern_validation_error: 'Entry does not match the required pattern' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/HU/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/HU/grid-resources.ts index 68af042df71..639012d0272 100644 --- a/projects/igniteui-angular-i18n/src/i18n/HU/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/HU/grid-resources.ts @@ -159,13 +159,13 @@ const GridResourceStringsHU_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'Field is required.', - igx_grid_min_validation_error: 'Field must be more than {0}', - igx_grid_max_validation_error: 'Field must be less than {0}', - igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', - igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', - igx_grid_email_validation_error: 'Field must contain a valid email address.', - igx_grid_pattern_validation_error: 'Field must match pattern {0}' + igx_grid_required_validation_error: 'This field is required', + igx_grid_min_validation_error: 'A value of at least {0} should be entered', + igx_grid_max_validation_error: 'A value no more than {0} should be entered', + igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', + igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', + igx_grid_email_validation_error: 'A valid email address should be entered', + igx_grid_pattern_validation_error: 'Entry does not match the required pattern' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/IT/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/IT/grid-resources.ts index 455a6acbb06..9f8ae4362b8 100644 --- a/projects/igniteui-angular-i18n/src/i18n/IT/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/IT/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsIT_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'Field is required.', - igx_grid_min_validation_error: 'Field must be more than {0}', - igx_grid_max_validation_error: 'Field must be less than {0}', - igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', - igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', - igx_grid_email_validation_error: 'Field must contain a valid email address.', - igx_grid_pattern_validation_error: 'Field must match pattern {0}' + igx_grid_required_validation_error: 'This field is required', + igx_grid_min_validation_error: 'A value of at least {0} should be entered', + igx_grid_max_validation_error: 'A value no more than {0} should be entered', + igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', + igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', + igx_grid_email_validation_error: 'A valid email address should be entered', + igx_grid_pattern_validation_error: 'Entry does not match the required pattern' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/JA/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/JA/grid-resources.ts index 180819e4d4f..42c7def3dc8 100644 --- a/projects/igniteui-angular-i18n/src/i18n/JA/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/JA/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsJA_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'Field is required.', - igx_grid_min_validation_error: 'Field must be more than {0}', - igx_grid_max_validation_error: 'Field must be less than {0}', - igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', - igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', - igx_grid_email_validation_error: 'Field must contain a valid email address.', - igx_grid_pattern_validation_error: 'Field must match pattern {0}' + igx_grid_required_validation_error: 'This field is required', + igx_grid_min_validation_error: 'A value of at least {0} should be entered', + igx_grid_max_validation_error: 'A value no more than {0} should be entered', + igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', + igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', + igx_grid_email_validation_error: 'A valid email address should be entered', + igx_grid_pattern_validation_error: 'Entry does not match the required pattern' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/KO/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/KO/grid-resources.ts index 8975194a9c7..7b1d979c601 100644 --- a/projects/igniteui-angular-i18n/src/i18n/KO/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/KO/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsKO_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'Field is required.', - igx_grid_min_validation_error: 'Field must be more than {0}', - igx_grid_max_validation_error: 'Field must be less than {0}', - igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', - igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', - igx_grid_email_validation_error: 'Field must contain a valid email address.', - igx_grid_pattern_validation_error: 'Field must match pattern {0}' + igx_grid_required_validation_error: 'This field is required', + igx_grid_min_validation_error: 'A value of at least {0} should be entered', + igx_grid_max_validation_error: 'A value no more than {0} should be entered', + igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', + igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', + igx_grid_email_validation_error: 'A valid email address should be entered', + igx_grid_pattern_validation_error: 'Entry does not match the required pattern' }; diff --git a/projects/igniteui-angular-i18n/src/i18n/NB/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/NB/grid-resources.ts index 510080b6067..89e76aa158e 100644 --- a/projects/igniteui-angular-i18n/src/i18n/NB/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/NB/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsNB_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'Field is required.', - igx_grid_min_validation_error: 'Field must be more than {0}', - igx_grid_max_validation_error: 'Field must be less than {0}', - igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', - igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', - igx_grid_email_validation_error: 'Field must contain a valid email address.', - igx_grid_pattern_validation_error: 'Field must match pattern {0}' + igx_grid_required_validation_error: 'This field is required', + igx_grid_min_validation_error: 'A value of at least {0} should be entered', + igx_grid_max_validation_error: 'A value no more than {0} should be entered', + igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', + igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', + igx_grid_email_validation_error: 'A valid email address should be entered', + igx_grid_pattern_validation_error: 'Entry does not match the required pattern' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/NL/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/NL/grid-resources.ts index 11fd0cb55ad..774b5bebad9 100644 --- a/projects/igniteui-angular-i18n/src/i18n/NL/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/NL/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsNL_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'Field is required.', - igx_grid_min_validation_error: 'Field must be more than {0}', - igx_grid_max_validation_error: 'Field must be less than {0}', - igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', - igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', - igx_grid_email_validation_error: 'Field must contain a valid email address.', - igx_grid_pattern_validation_error: 'Field must match pattern {0}' + igx_grid_required_validation_error: 'This field is required', + igx_grid_min_validation_error: 'A value of at least {0} should be entered', + igx_grid_max_validation_error: 'A value no more than {0} should be entered', + igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', + igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', + igx_grid_email_validation_error: 'A valid email address should be entered', + igx_grid_pattern_validation_error: 'Entry does not match the required pattern' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/PL/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/PL/grid-resources.ts index f2cf9bd038a..f64db242a5a 100644 --- a/projects/igniteui-angular-i18n/src/i18n/PL/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/PL/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsPL_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'Field is required.', - igx_grid_min_validation_error: 'Field must be more than {0}', - igx_grid_max_validation_error: 'Field must be less than {0}', - igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', - igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', - igx_grid_email_validation_error: 'Field must contain a valid email address.', - igx_grid_pattern_validation_error: 'Field must match pattern {0}' + igx_grid_required_validation_error: 'This field is required', + igx_grid_min_validation_error: 'A value of at least {0} should be entered', + igx_grid_max_validation_error: 'A value no more than {0} should be entered', + igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', + igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', + igx_grid_email_validation_error: 'A valid email address should be entered', + igx_grid_pattern_validation_error: 'Entry does not match the required pattern' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/PT/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/PT/grid-resources.ts index e0b8fe4430c..a20db7de344 100644 --- a/projects/igniteui-angular-i18n/src/i18n/PT/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/PT/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsPT_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'Field is required.', - igx_grid_min_validation_error: 'Field must be more than {0}', - igx_grid_max_validation_error: 'Field must be less than {0}', - igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', - igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', - igx_grid_email_validation_error: 'Field must contain a valid email address.', - igx_grid_pattern_validation_error: 'Field must match pattern {0}' + igx_grid_required_validation_error: 'This field is required', + igx_grid_min_validation_error: 'A value of at least {0} should be entered', + igx_grid_max_validation_error: 'A value no more than {0} should be entered', + igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', + igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', + igx_grid_email_validation_error: 'A valid email address should be entered', + igx_grid_pattern_validation_error: 'Entry does not match the required pattern' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/RO/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/RO/grid-resources.ts index f0ea18aab9a..39e809cae16 100644 --- a/projects/igniteui-angular-i18n/src/i18n/RO/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/RO/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsRO_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'Field is required.', - igx_grid_min_validation_error: 'Field must be more than {0}', - igx_grid_max_validation_error: 'Field must be less than {0}', - igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', - igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', - igx_grid_email_validation_error: 'Field must contain a valid email address.', - igx_grid_pattern_validation_error: 'Field must match pattern {0}' + igx_grid_required_validation_error: 'This field is required', + igx_grid_min_validation_error: 'A value of at least {0} should be entered', + igx_grid_max_validation_error: 'A value no more than {0} should be entered', + igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', + igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', + igx_grid_email_validation_error: 'A valid email address should be entered', + igx_grid_pattern_validation_error: 'Entry does not match the required pattern' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/SV/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/SV/grid-resources.ts index 574e09a4d4c..bb8f8242781 100644 --- a/projects/igniteui-angular-i18n/src/i18n/SV/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/SV/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsSV_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'Field is required.', - igx_grid_min_validation_error: 'Field must be more than {0}', - igx_grid_max_validation_error: 'Field must be less than {0}', - igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', - igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', - igx_grid_email_validation_error: 'Field must contain a valid email address.', - igx_grid_pattern_validation_error: 'Field must match pattern {0}' + igx_grid_required_validation_error: 'This field is required', + igx_grid_min_validation_error: 'A value of at least {0} should be entered', + igx_grid_max_validation_error: 'A value no more than {0} should be entered', + igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', + igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', + igx_grid_email_validation_error: 'A valid email address should be entered', + igx_grid_pattern_validation_error: 'Entry does not match the required pattern' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/TR/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/TR/grid-resources.ts index 4243dee6bae..5f3a51cb421 100644 --- a/projects/igniteui-angular-i18n/src/i18n/TR/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/TR/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsTR_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'Field is required.', - igx_grid_min_validation_error: 'Field must be more than {0}', - igx_grid_max_validation_error: 'Field must be less than {0}', - igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', - igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', - igx_grid_email_validation_error: 'Field must contain a valid email address.', - igx_grid_pattern_validation_error: 'Field must match pattern {0}' + igx_grid_required_validation_error: 'This field is required', + igx_grid_min_validation_error: 'A value of at least {0} should be entered', + igx_grid_max_validation_error: 'A value no more than {0} should be entered', + igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', + igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', + igx_grid_email_validation_error: 'A valid email address should be entered', + igx_grid_pattern_validation_error: 'Entry does not match the required pattern' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/ZH-HANS/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/ZH-HANS/grid-resources.ts index 47618eb57c9..eefb0f1ab53 100644 --- a/projects/igniteui-angular-i18n/src/i18n/ZH-HANS/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/ZH-HANS/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsZHHANS_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'Field is required.', - igx_grid_min_validation_error: 'Field must be more than {0}', - igx_grid_max_validation_error: 'Field must be less than {0}', - igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', - igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', - igx_grid_email_validation_error: 'Field must contain a valid email address.', - igx_grid_pattern_validation_error: 'Field must match pattern {0}' + igx_grid_required_validation_error: 'This field is required', + igx_grid_min_validation_error: 'A value of at least {0} should be entered', + igx_grid_max_validation_error: 'A value no more than {0} should be entered', + igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', + igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', + igx_grid_email_validation_error: 'A valid email address should be entered', + igx_grid_pattern_validation_error: 'Entry does not match the required pattern' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/ZH-HANT/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/ZH-HANT/grid-resources.ts index 05a00101b5b..65dba53ac85 100644 --- a/projects/igniteui-angular-i18n/src/i18n/ZH-HANT/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/ZH-HANT/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsZHHANT_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'Field is required.', - igx_grid_min_validation_error: 'Field must be more than {0}', - igx_grid_max_validation_error: 'Field must be less than {0}', - igx_grid_min_length_validation_error: 'Field must be at least {0} characters long.', - igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', - igx_grid_email_validation_error: 'Field must contain a valid email address.', - igx_grid_pattern_validation_error: 'Field must match pattern {0}' + igx_grid_required_validation_error: 'This field is required', + igx_grid_min_validation_error: 'A value of at least {0} should be entered', + igx_grid_max_validation_error: 'A value no more than {0} should be entered', + igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', + igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', + igx_grid_email_validation_error: 'A valid email address should be entered', + igx_grid_pattern_validation_error: 'Entry does not match the required pattern' }; /** diff --git a/projects/igniteui-angular/src/lib/core/i18n/grid-resources.ts b/projects/igniteui-angular/src/lib/core/i18n/grid-resources.ts index c86557e9ecf..1c186f17b89 100644 --- a/projects/igniteui-angular/src/lib/core/i18n/grid-resources.ts +++ b/projects/igniteui-angular/src/lib/core/i18n/grid-resources.ts @@ -321,11 +321,11 @@ export const GridResourceStringsEN: IGridResourceStrings = { igx_grid_pivot_selector_columns: 'Columns', igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drop Items Here', - igx_grid_required_validation_error: 'Field is required.', - igx_grid_min_validation_error: 'Field must be more than {0}', - igx_grid_max_validation_error: 'Field must be less than {0}', - igx_grid_min_length_validation_error: 'Field length must be at least {0} characters long.', - igx_grid_max_length_validation_error: 'Field must be at most {0} characters long.', - igx_grid_email_validation_error: 'Field must contain a valid email address.', - igx_grid_pattern_validation_error: 'Field must match pattern {0}' + igx_grid_required_validation_error: 'This field is required', + igx_grid_min_validation_error: 'A value of at least {0} should be entered', + igx_grid_max_validation_error: 'A value no more than {0} should be entered', + igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', + igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', + igx_grid_email_validation_error: 'A valid email address should be entered', + igx_grid_pattern_validation_error: 'Entry does not match the required pattern' }; diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.html b/projects/igniteui-angular/src/lib/grids/cell.component.html index ec5a1f2505c..c2a24b2c581 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/cell.component.html @@ -144,6 +144,6 @@ {{grid.resourceStrings.igx_grid_email_validation_error }}
- {{grid.resourceStrings.igx_grid_pattern_validation_error | igxStringReplace:'{0}':formGroup.get(column.field).errors.pattern.requiredPattern }} + {{grid.resourceStrings.igx_grid_pattern_validation_error}}
\ No newline at end of file From 20c51352d07c58962b52ebfafa704efb4aa0b8b6 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 18 Aug 2022 18:20:38 +0300 Subject: [PATCH 068/149] chore(*): Fix getter. --- projects/igniteui-angular/src/lib/grids/cell.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 507323246d8..171e24ee53d 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -475,7 +475,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT /** @hidden @internal */ @HostBinding('attr.aria-errormessage') public get ariaErrorMessage() { - return this.grid.id + '_' + this.column.field + '_' + this.row.index + '_error'; + return this.grid.id + '_' + this.column.field + '_' + this.intRow.index + '_error'; } /** From 0b256a79713cff49be2dc5b57205eb774956b37c Mon Sep 17 00:00:00 2001 From: didimmova Date: Fri, 19 Aug 2022 10:06:36 +0300 Subject: [PATCH 069/149] Merge branch 'master' into mkirova/grid-reactive-forms --- .../date-picker/date-picker.component.spec.ts | 23 +++++ .../lib/date-picker/date-picker.component.ts | 1 + .../date-range-picker.component.spec.ts | 89 +++++++++++++++++-- .../date-range-picker.component.ts | 4 +- .../time-picker/time-picker.component.spec.ts | 23 +++++ .../lib/time-picker/time-picker.component.ts | 3 +- 6 files changed, 134 insertions(+), 9 deletions(-) diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.component.spec.ts b/projects/igniteui-angular/src/lib/date-picker/date-picker.component.spec.ts index a10be5d34c3..f62ac4a8d0d 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.component.spec.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.component.spec.ts @@ -334,6 +334,20 @@ describe('IgxDatePicker', () => { expect(datePicker.locale).toEqual('en-US'); expect(datePicker.weekStart).toEqual(WEEKDAYS.FRIDAY); })); + + it('should set initial validity state when the form group is disabled', () => { + fixture = TestBed.createComponent(IgxDatePickerReactiveFormComponent); + fixture.detectChanges(); + datePicker = fixture.componentInstance.datePicker; + + (fixture.componentInstance as IgxDatePickerReactiveFormComponent).markAsTouched(); + fixture.detectChanges(); + expect((datePicker as any).inputDirective.valid).toBe(IgxInputState.INVALID); + + (fixture.componentInstance as IgxDatePickerReactiveFormComponent).disableForm(); + fixture.detectChanges(); + expect((datePicker as any).inputDirective.valid).toBe(IgxInputState.INITIAL); + }); }); describe('Projected elements', () => { @@ -1382,4 +1396,13 @@ export class IgxDatePickerReactiveFormComponent { this.form.get('date').setValidators(Validators.required); this.form.get('date').updateValueAndValidity(); } + + public markAsTouched() { + this.form.get('date').markAsTouched(); + this.form.get('date').updateValueAndValidity(); + } + + public disableForm() { + this.form.disable(); + } } diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.component.ts b/projects/igniteui-angular/src/lib/date-picker/date-picker.component.ts index e4cd838d698..40c2d8eda98 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.component.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.component.ts @@ -816,6 +816,7 @@ export class IgxDatePickerComponent extends PickerBaseDirective implements Contr } private onStatusChanged = () => { + this.disabled = this._ngControl.disabled; this.updateValidity(); this.inputGroup.isRequired = this.required; }; diff --git a/projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.spec.ts b/projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.spec.ts index 5930f817f2d..f9e79497bbb 100644 --- a/projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.spec.ts +++ b/projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.spec.ts @@ -1,9 +1,9 @@ import { ComponentFixture, TestBed, fakeAsync, tick, waitForAsync, flush } from '@angular/core/testing'; import { Component, OnInit, ViewChild, DebugElement, ChangeDetectionStrategy } from '@angular/core'; -import { IgxInputGroupModule } from '../input-group/public_api'; +import { IgxInputGroupModule, IgxInputState } from '../input-group/public_api'; import { PickerInteractionMode } from '../date-common/types'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { FormsModule, UntypedFormControl } from '@angular/forms'; +import { FormsModule, ReactiveFormsModule, UntypedFormBuilder, UntypedFormControl, Validators } from '@angular/forms'; import { IgxDateRangePickerModule } from './date-range-picker.module'; import { By } from '@angular/platform-browser'; import { ControlsFunction } from '../test-utils/controls-functions.spec'; @@ -339,7 +339,8 @@ describe('IgxDateRangePicker', () => { declarations: [ DateRangeTestComponent, DateRangeDefaultComponent, - DateRangeDisabledComponent + DateRangeDisabledComponent, + DateRangeReactiveFormComponent ], imports: [ CommonModule, @@ -350,7 +351,8 @@ describe('IgxDateRangePicker', () => { FormsModule, NoopAnimationsModule, IgxPickersCommonModule, - IgxCalendarContainerModule + IgxCalendarContainerModule, + ReactiveFormsModule ] }) .compileComponents(); @@ -774,6 +776,20 @@ describe('IgxDateRangePicker', () => { expect((dateRange as any).calendar.selectedDates.length).toBeGreaterThan(0); })); + + it('should set initial validity state when the form group is disabled', () => { + const fix = TestBed.createComponent(DateRangeReactiveFormComponent); + fix.detectChanges(); + const dateRangePicker = fix.componentInstance.dateRange; + + fix.componentInstance.markAsTouched(); + fix.detectChanges(); + expect(dateRangePicker.inputDirective.valid).toBe(IgxInputState.INVALID); + + fix.componentInstance.disableForm(); + fix.detectChanges(); + expect(dateRangePicker.inputDirective.valid).toBe(IgxInputState.INITIAL); + }); }); describe('Two Inputs', () => { @@ -787,7 +803,8 @@ describe('IgxDateRangePicker', () => { DateRangeTwoInputsTestComponent, DateRangeTwoInputsNgModelTestComponent, DateRangeDisabledComponent, - DateRangeTwoInputsDisabledComponent + DateRangeTwoInputsDisabledComponent, + DateRangeReactiveFormComponent ], imports: [ CommonModule, @@ -798,7 +815,8 @@ describe('IgxDateRangePicker', () => { IgxInputGroupModule, FormsModule, NoopAnimationsModule, - IgxIconModule + IgxIconModule, + ReactiveFormsModule ] }) .compileComponents(); @@ -1013,6 +1031,22 @@ describe('IgxDateRangePicker', () => { expect((rangePicker as any).calendar.selectedDates.length).toBe(7); flush(); })); + + it('should set initial validity state when the form group is disabled', () => { + const fix = TestBed.createComponent(DateRangeReactiveFormComponent); + fix.detectChanges(); + const dateRangePicker = fix.componentInstance.dateRangeWithTwoInputs; + + fix.componentInstance.markAsTouched(); + fix.detectChanges(); + expect(dateRangePicker.projectedInputs.first.inputDirective.valid).toBe(IgxInputState.INVALID); + expect(dateRangePicker.projectedInputs.last.inputDirective.valid).toBe(IgxInputState.INVALID); + + fix.componentInstance.disableForm(); + fix.detectChanges(); + expect(dateRangePicker.projectedInputs.first.inputDirective.valid).toBe(IgxInputState.INITIAL); + expect(dateRangePicker.projectedInputs.last.inputDirective.valid).toBe(IgxInputState.INITIAL); + }); }); describe('Keyboard navigation', () => { @@ -1566,3 +1600,46 @@ export class DateRangeDisabledComponent extends DateRangeTestComponent { changeDetection: ChangeDetectionStrategy.OnPush }) export class DateRangeTwoInputsDisabledComponent extends DateRangeDisabledComponent { } + +@Component({ + template: ` +
+ + + + + + + + + + + +
` +}) +export class DateRangeReactiveFormComponent { + @ViewChild('range', {read: IgxDateRangePickerComponent}) public dateRange: IgxDateRangePickerComponent; + @ViewChild('twoInputs', {read: IgxDateRangePickerComponent}) public dateRangeWithTwoInputs: IgxDateRangePickerComponent; + + public form = this.fb.group({ + range: ['', Validators.required], + twoInputs: ['', Validators.required] + }); + + constructor(private fb: UntypedFormBuilder) { } + + public markAsTouched() { + if (!this.form.valid) { + for (const key in this.form.controls) { + if (this.form.controls[key]) { + this.form.controls[key].markAsTouched(); + this.form.controls[key].updateValueAndValidity(); + } + } + } + } + + public disableForm() { + this.form.disable(); + } +} diff --git a/projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.ts b/projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.ts index 30fd0e4dcb0..855d0cd232b 100644 --- a/projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.ts +++ b/projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.ts @@ -650,13 +650,13 @@ export class IgxDateRangePickerComponent extends PickerBaseDirective protected onStatusChanged = () => { if (this.inputGroup) { - this.inputDirective.valid = this.isTouchedOrDirty + this.inputDirective.valid = this.isTouchedOrDirty && !this._ngControl.disabled ? this.getInputState(this.inputGroup.isFocused) : IgxInputState.INITIAL; } else if (this.hasProjectedInputs) { this.projectedInputs .forEach(i => { - i.inputDirective.valid = this.isTouchedOrDirty + i.inputDirective.valid = this.isTouchedOrDirty && !this._ngControl.disabled ? this.getInputState(i.isFocused) : IgxInputState.INITIAL;; }); diff --git a/projects/igniteui-angular/src/lib/time-picker/time-picker.component.spec.ts b/projects/igniteui-angular/src/lib/time-picker/time-picker.component.spec.ts index e9a9bd9c670..7699b23a4c0 100644 --- a/projects/igniteui-angular/src/lib/time-picker/time-picker.component.spec.ts +++ b/projects/igniteui-angular/src/lib/time-picker/time-picker.component.spec.ts @@ -1714,6 +1714,20 @@ describe('IgxTimePicker', () => { expect(inputGroupRequiredClass).not.toBeNull(); expect(asterisk).toBe('"*"'); }); + + it('should set initial validity state when the form group is disabled', () => { + fixture = TestBed.createComponent(IgxTimePickerReactiveFormComponent); + fixture.detectChanges(); + timePicker = fixture.componentInstance.timePicker; + + (fixture.componentInstance as IgxTimePickerReactiveFormComponent).markAsTouched(); + fixture.detectChanges(); + expect((timePicker as any).inputDirective.valid).toBe(IgxInputState.INVALID); + + (fixture.componentInstance as IgxTimePickerReactiveFormComponent).disableForm(); + fixture.detectChanges(); + expect((timePicker as any).inputDirective.valid).toBe(IgxInputState.INITIAL); + }); }); }); }); @@ -1802,4 +1816,13 @@ export class IgxTimePickerReactiveFormComponent { this.form.get('time').setValidators(Validators.required); this.form.get('time').updateValueAndValidity(); } + + public markAsTouched() { + this.form.get('time').markAsTouched(); + this.form.get('time').updateValueAndValidity(); + } + + public disableForm() { + this.form.disable(); + } } diff --git a/projects/igniteui-angular/src/lib/time-picker/time-picker.component.ts b/projects/igniteui-angular/src/lib/time-picker/time-picker.component.ts index 09642dc5799..449352aa632 100644 --- a/projects/igniteui-angular/src/lib/time-picker/time-picker.component.ts +++ b/projects/igniteui-angular/src/lib/time-picker/time-picker.component.ts @@ -1066,7 +1066,8 @@ export class IgxTimePickerComponent extends PickerBaseDirective protected onStatusChanged() { if ((this._ngControl.control.touched || this._ngControl.control.dirty) && - (this._ngControl.control.validator || this._ngControl.control.asyncValidator)) { + (this._ngControl.control.validator || this._ngControl.control.asyncValidator) && + !this._ngControl.disabled) { if (this._inputGroup.isFocused) { this.inputDirective.valid = this._ngControl.valid ? IgxInputState.VALID : IgxInputState.INVALID; } else { From f6b6d886e9aefa365c62f882476644a707302664 Mon Sep 17 00:00:00 2001 From: didimmova Date: Fri, 19 Aug 2022 10:56:49 +0300 Subject: [PATCH 070/149] theme(grid): update grid theme --- .../lib/core/styles/components/grid/_grid-theme.scss | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss index fda24e6214d..efe859a660c 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss @@ -1202,6 +1202,12 @@ caret-color: var-get($theme, 'edit-mode-color') !important; } + %form-group-bundle--fluent--focus { + &::after { + border: none !important; + } + } + %form-group-border { background: var-get($theme, 'edit-mode-color') !important; } @@ -1395,7 +1401,6 @@ } %grid-cell--invalid { - box-shadow: inset 0 0 0 rem(1px) color($palette, 'error', 500) !important; padding-inline-end: rem(4px) !important; > igx-icon { @@ -1419,7 +1424,7 @@ } %grid-cell--valid { - box-shadow: inset 0 0 0 rem(1px) color($palette, 'success', 500) !important; + box-shadow: inset 0 0 0 rem(2px) color($palette, 'success', 500) !important; } %grid-cell--pinned-selected, @@ -1529,6 +1534,7 @@ } &.igx-grid__td--invalid { + box-shadow: inset 0 0 0 rem(2px) color($palette, 'error', 500) !important; padding-inline-end: rem(4px) !important; } From 341659ac3fd88bfa3439897460a9077d513a331a Mon Sep 17 00:00:00 2001 From: MKirova Date: Fri, 19 Aug 2022 11:20:38 +0300 Subject: [PATCH 071/149] chore(*): Resize and reposition error tooltip overlay when form control status changes. --- .../src/lib/grids/cell.component.ts | 14 ++++++++++++++ .../src/lib/grids/common/grid.interface.ts | 1 + .../src/lib/grids/grid-base.directive.ts | 10 ++++++++++ 3 files changed, 25 insertions(+) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 171e24ee53d..de6ad3cce91 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -82,6 +82,13 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT @ViewChild('errorIcon', { read: IgxIconComponent, static: false }) public errorIcon: IgxIconComponent; + /** + * @hidden + * @internal + */ + @ViewChild('error', { read: ElementRef, static: false }) + public errorDiv: ElementRef; + /** * Gets the default error template. */ @@ -882,6 +889,13 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT this.formControl.valueChanges.pipe(takeWhile(x => this.editMode)).subscribe(value => { this.editValue = value; }); + this.formControl.statusChanges.pipe(takeWhile(x => this.editMode)).subscribe(status => { + if (status === 'INVALID' && this.errorTooltip.length > 0) { + this.cdr.detectChanges(); + const tooltip = this.errorTooltip.toArray()[0]; + this.grid.resizeAndRepositionOverlayById(tooltip.overlayId, this.errorDiv.nativeElement.offsetWidth); + } + }); } if (changes.value && !changes.value.firstChange) { if (this.highlight) { diff --git a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts index 80fff4e1569..2b7738b2336 100644 --- a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts @@ -606,6 +606,7 @@ export interface GridType extends IGridDataBindable { resetColumnCollections(): void; triggerPipes(): void; repositionRowEditingOverlay(row: RowType): void; + resizeAndRepositionOverlayById(overlayId: string, newSize: number): void; closeRowEditingOverlay(): void; reflow(): void; diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index cccf735ac96..5388188d874 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -5927,6 +5927,16 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements } } + /** + * @hidden @internal + */ + public resizeAndRepositionOverlayById(overlayId: string, newSize: number) { + const overlay = this.overlayService.getOverlayById(overlayId); + overlay.initialSize.width = newSize; + overlay.elementRef.nativeElement.parentElement.style.width = newSize + 'px'; + this.overlayService.reposition(overlayId); + } + /** * @hidden @internal */ From 5ee3d2a934992917eef848028cd73e08fb8bc0c6 Mon Sep 17 00:00:00 2001 From: MKirova Date: Fri, 19 Aug 2022 14:02:38 +0300 Subject: [PATCH 072/149] chore(*): Apply review comments to changelog section. --- CHANGELOG.md | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8be80d83f0..57eb2cca8b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,11 +73,27 @@ All notable changes for each version of this project will be documented in this - Added reactive forms style validation for grid editing. This extends the [Angular's reactive forms](https://angular.io/guide/form-validation#validating-input-in-reactive-forms) validation functionality You can configure it in 2 ways: - 1. Via template-driven configuration on the `igx-column` of the grid. - 2. Via reactive forms using the FormGroup exposed via the `onFormGroupCreate` event of the grid. + 1. Via template-driven configuration on the `igx-column` of the grid: + ```html + + ``` + 2. Via reactive forms using the FormGroup exposed via the `onFormGroupCreate` event of the grid: + + ```html + + ``` + + ```ts + public formCreateHandler(formGr: FormGroup) { + // add a validator + const prodName = formGr.get('UserName'); + prodName.addValidators(forbiddenNameValidator(/bob/i)) + } + ``` - Edited cells wil enter an invalid state when validation fails, will be marked with `igx-grid__td--invalid` class and will display an error icon with additional error message. Cell will remain in that state until the value is edited to a valid value or the related transaction in the transaction service are commited or cleared. - Additional Apis have been exposed on the base transaction service to get transactions and states related to validity. + Edited cells wil enter an invalid state when validation fails and will be show an error icon and message. Cell will remain invalid until the value is edited to a valid value or the related transaction in the transaction service are commited or cleared. + + You can refer to the documentation for more details: https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/validation ### General From 365e9d9728424b4b7189b4df449ea6bfb7a00b04 Mon Sep 17 00:00:00 2001 From: MKirova Date: Fri, 19 Aug 2022 17:46:00 +0300 Subject: [PATCH 073/149] chore(*): Start moving validation related apis in separate service. --- .../src/lib/grids/api.service.ts | 28 ++--- .../src/lib/grids/cell.component.ts | 22 +--- .../src/lib/grids/common/crud.service.ts | 17 +-- .../src/lib/grids/common/grid.interface.ts | 2 + .../src/lib/grids/grid-base.directive.ts | 3 + .../lib/grids/grid/grid-validation.service.ts | 45 ++++++++ .../src/lib/grids/grid/grid.component.ts | 2 + .../hierarchical-grid-base.directive.ts | 3 + .../hierarchical-grid.component.ts | 2 + .../hierarchical-grid/row-island.component.ts | 3 + .../grids/pivot-grid/pivot-grid.component.ts | 4 + .../grids/tree-grid/tree-grid-api.service.ts | 1 - .../grids/tree-grid/tree-grid.component.ts | 5 +- .../services/transaction/base-transaction.ts | 103 +----------------- .../services/transaction/igx-transaction.ts | 6 - .../lib/services/transaction/transaction.ts | 31 +----- .../grid-validation.sample.component.ts | 16 +-- 17 files changed, 92 insertions(+), 201 deletions(-) create mode 100644 projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts diff --git a/projects/igniteui-angular/src/lib/grids/api.service.ts b/projects/igniteui-angular/src/lib/grids/api.service.ts index bf8bb2349df..b1771c0cb58 100644 --- a/projects/igniteui-angular/src/lib/grids/api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/api.service.ts @@ -11,6 +11,7 @@ import { IgxColumnMovingService } from './moving/moving.service'; import { IGroupingExpression } from '../data-operations/grouping-expression.interface'; import { ISortingExpression, SortingDirection } from '../data-operations/sorting-strategy'; import { FilterUtil } from '../data-operations/filtering-strategy'; +import { IgxGridValidationService } from './grid/grid-validation.service'; /** * @hidden @@ -145,12 +146,7 @@ export class GridBaseAPIService implements GridServiceType { this.grid.summaryService.clearSummaryCache(args); const data = this.getRowData(cell.id.rowID); - const validity: IFieldValid[] = [{ - field: cell.column.field, - valid: args.isValid, - formGroup: cell.row.rowFormGroup - }]; - this.updateData(this.grid, cell.id.rowID, data, cell.rowData, reverseMapper(cell.column.field, args.newValue), validity); + this.updateData(this.grid, cell.id.rowID, data, cell.rowData, reverseMapper(cell.column.field, args.newValue)); if (this.grid.primaryKey === cell.column.field) { if (this.grid.selectionService.isRowSelected(cell.id.rowID)) { this.grid.selectionService.deselectRow(cell.id.rowID); @@ -201,16 +197,8 @@ export class GridBaseAPIService implements GridServiceType { if (hasSummarized) { grid.summaryService.removeSummaries(args.rowID); } - const validityArray: IFieldValid[] = []; - Object.keys(row.rowFormGroup.controls).forEach(key => { - validityArray.push({ - field: key, - valid: row.rowFormGroup.controls[key].valid, - formGroup: row.rowFormGroup - }); - }); - this.updateData(grid, row.id, data[index], args.oldValue, args.newValue, validityArray); + this.updateData(grid, row.id, data[index], args.oldValue, args.newValue); const newId = grid.primaryKey ? args.newValue[grid.primaryKey] : args.newValue; if (selected) { grid.selectionService.deselectRow(row.id); @@ -312,7 +300,6 @@ export class GridBaseAPIService implements GridServiceType { } else { grid.data.push(rowData); } - grid.transactions.addValidation({newValue: rowData, id: transactionId, type: TransactionType.ADD, validity: validity}); } public deleteRowFromData(rowID: any, index: number) { @@ -557,20 +544,21 @@ export class GridBaseAPIService implements GridServiceType { * @param rowCurrentValue Current value of the row as it is with applied previous transactions * @param rowNewValue New value of the row */ - protected updateData(grid, rowID, rowValueInDataSource: any, rowCurrentValue: any, rowNewValue: { [x: string]: any }, validity?) { + protected updateData(grid, rowID, rowValueInDataSource: any, rowCurrentValue: any, rowNewValue: { [x: string]: any }) { const transaction: Transaction = { id: rowID, type: TransactionType.UPDATE, - newValue: rowNewValue, - validity: validity + newValue: rowNewValue }; if (grid.transactions.enabled) { grid.transactions.add(transaction, rowCurrentValue); } else { mergeObjects(rowValueInDataSource, rowNewValue); } - grid.transactions.addValidation(transaction, rowCurrentValue); + const formGroup = this.grid.validationService.getFormGroup(rowID); + const validation = grid.validationService as IgxGridValidationService; + validation.addRecordState(rowID, formGroup) } diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index de6ad3cce91..4a01b649090 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -108,14 +108,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT public get formGroup(): FormGroup { - const isRowEdit = this.grid.crudService.rowEditing; - const editRow = isRowEdit ? this.grid.crudService.row : this.grid.crudService.cell?.row; - const id = isRowEdit ? editRow?.id : editRow?.id.rowID; - if (editRow && id === this.intRow.key) { - return editRow.rowFormGroup; - } else { - return this.validity?.formGroup ?? editRow?.rowFormGroup; - } + return this.grid.validationService.getFormGroup(this.intRow.key) || new FormGroup({}); } /** @@ -492,11 +485,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT @HostBinding('class.igx-grid__td--invalid') @HostBinding('attr.aria-invalid') public get isInvalid() { - if (this.intRow.inEditMode || this.editMode) { - return this.formGroup?.get(this.column?.field)?.invalid; - } else { - return !!this.validity ? !this.validity.valid : false; - } + return this.formGroup?.get(this.column?.field)?.invalid; } /** @@ -516,13 +505,6 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT return (this.formGroup?.get(this.column.field) || new FormControl(this.column.field)) as FormControl; } - private get validity() { - const state = this.grid.transactions.getAggregatedValidationState(this.intRow.key); - if (state && state.validity && state.validity.some(x => x.valid === false)) { - return state.validity.find(x => x.field === this.column.field); - } - } - public get gridRowSpan(): number { return this.column.gridRowSpan; } diff --git a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts index 948e580bbfe..e987e1bad79 100644 --- a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts +++ b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts @@ -13,12 +13,7 @@ export class IgxEditRow { public rowFormGroup = new FormGroup({}); constructor(public id: any, public index: number, public data: any, public grid: GridType) { - for (const col of grid.columns) { - const field = col.field; - const control = new FormControl(this.data ? this.data[field] : {}, { updateOn: grid.validationTrigger }); - control.addValidators(col.validators); - this.rowFormGroup.addControl(field, control); - } + this.rowFormGroup = this.grid.validationService.createFormGroupForRecord(id, data); } public createEditEventArgs(includeNewValue = true, event?: Event): IGridEditEventArgs { @@ -95,7 +90,6 @@ export interface IgxAddRowParent { export class IgxCell { public primaryKey: any; public state: any; - public row: IgxEditRow; constructor( public id, @@ -105,7 +99,7 @@ export class IgxCell { public editValue: any, public rowData: any, public grid: GridType) { - this.row = new IgxEditRow(id, rowIndex, rowData, grid); + this.grid.validationService.createFormGroupForRecord(id.rowID, rowData); } public castToNumber(value: any): any { @@ -117,8 +111,7 @@ export class IgxCell { } public createEditEventArgs(includeNewValue = true, event?: Event): IGridEditEventArgs { - const row = this.grid.crudService.row ?? this.row; - const formControl = row.rowFormGroup.get(this.column.field); + const formControl = this.grid.validationService.getFormControl(this.id.rowID, this.column.field); const args: IGridEditEventArgs = { rowID: this.id.rowID, cellID: this.id, @@ -140,8 +133,7 @@ export class IgxCell { const updatedData = this.grid.transactions.enabled ? this.grid.transactions.getAggregatedValue(this.id.rowID, true) : this.rowData; const rowData = updatedData === null ? this.grid.gridAPI.getRowData(this.id.rowID) : updatedData; - const row = this.grid.crudService.row ?? this.row; - const formControl = row.rowFormGroup.get(this.column.field); + const formControl = this.grid.validationService.getFormControl(this.id.rowID, this.column.field); const args: IGridEditDoneEventArgs = { rowID: this.id.rowID, cellID: this.id, @@ -568,7 +560,6 @@ export class IgxGridCRUDService extends IgxRowAddCrudState { } else { this.createCell(cell); - this.grid.onFormGroupCreate.emit(this.cell.row.rowFormGroup); this.beginCellEdit(event); } } diff --git a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts index 2b7738b2336..098c773d7f9 100644 --- a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts @@ -37,6 +37,7 @@ import { IPinningConfig } from '../grid.common'; import { IDimensionsChange, IPivotConfiguration, IPivotDimension, IPivotKeys, IPivotValue, IValuesChange, PivotDimensionType } from '../pivot-grid/pivot-grid.interface'; import { IDataCloneStrategy } from '../../data-operations/data-clone-strategy'; import { FormGroup } from '@angular/forms'; +import { IgxGridValidationService } from '../grid/grid-validation.service'; export const IGX_GRID_BASE = new InjectionToken('IgxGridBaseToken'); export const IGX_GRID_SERVICE_BASE = new InjectionToken('IgxGridServiceBaseToken'); @@ -399,6 +400,7 @@ export interface GridType extends IGridDataBindable { filteredSortedData: any[]; dataWithAddedInTransactionRows: any[]; transactions: TransactionService; + validationService: IgxGridValidationService; defaultSummaryHeight: number; summaryRowHeight: number; rowEditingOverlay: IgxToggleDirective; diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index 5388188d874..fe552d5d2b2 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -155,6 +155,7 @@ import { IgxGridFilteringRowComponent } from './filtering/base/grid-filtering-ro import { DefaultDataCloneStrategy, IDataCloneStrategy } from '../data-operations/data-clone-strategy'; import { IgxGridCellComponent } from './cell.component'; import { FormGroup } from '@angular/forms'; +import { IgxGridValidationService } from './grid/grid-validation.service'; let FAKE_ROW_ID = -1; const DEFAULT_ITEMS_PER_PAGE = 15; @@ -3047,6 +3048,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements } constructor( + public validationService: IgxGridValidationService, public selectionService: IgxGridSelectionService, public colResizingService: IgxColumnResizingService, @Inject(IGX_GRID_SERVICE_BASE) public gridAPI: GridServiceType, @@ -3254,6 +3256,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements this.gridAPI.grid = this as any; this.crudService.grid = this as any; this.selectionService.grid = this as any; + this.validationService.grid = this as any; this.navigation.grid = this as any; this.filteringService.grid = this as any; this.summaryService.grid = this as any; diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts new file mode 100644 index 00000000000..d453d582703 --- /dev/null +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@angular/core'; +import { FormControl, FormGroup } from '@angular/forms'; +import { GridType } from '../common/grid.interface'; + +@Injectable() +export class IgxGridValidationService { + public grid: GridType; + private _validityStates = new Map(); + + public createFormGroupForRecord(rowId, data) { + let formGroup = this._validityStates.get(rowId); + if (!formGroup) { + formGroup = new FormGroup({}); + for (const col of this.grid.columns) { + const field = col.field; + const control = new FormControl(data ? data[field] : undefined, { updateOn: this.grid.validationTrigger }); + control.addValidators(col.validators); + formGroup.addControl(field, control); + } + this.grid.onFormGroupCreate.emit(formGroup); + this.addRecordState(rowId, formGroup); + } + return formGroup; + } + + public getFormGroup(id: any) { + return this._validityStates.get(id); + } + + public getFormControl(rowId: any, columnKey: string) { + const formControl = this.getFormGroup(rowId); + return formControl.get(columnKey); + } + + public addRecordState(rowId: any, form: FormGroup ) { + this._validityStates.set(rowId, form); + } + + public getInvalidStates() { + //todo + this._validityStates.forEach(x => { + }); + } + +} \ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.component.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.component.ts index a9002ecc3b8..6770d5e0faf 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.component.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.component.ts @@ -30,6 +30,7 @@ import { IgxGridGroupByAreaComponent } from '../grouping/grid-group-by-area.comp import { IgxGridCell } from '../grid-public-cell'; import { ISortingExpression } from '../../data-operations/sorting-strategy'; import { IGridGroupingStrategy } from '../common/strategy'; +import { IgxGridValidationService } from './grid-validation.service'; let NEXT_ID = 0; @@ -66,6 +67,7 @@ export interface IGroupingDoneEventArgs extends IBaseEventArgs { IgxGridNavigationService, IgxGridSummaryService, IgxGridSelectionService, + IgxGridValidationService, { provide: IGX_GRID_SERVICE_BASE, useClass: IgxGridAPIService }, { provide: IGX_GRID_BASE, useExisting: IgxGridComponent }, IgxFilteringService, diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid-base.directive.ts index 3fcdb593aa1..ae9cca02bca 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid-base.directive.ts @@ -40,6 +40,7 @@ import { IgxTransactionService } from '../../services/transaction/igx-transactio import { IgxOverlayService } from '../../services/overlay/overlay'; import { State, Transaction, TransactionService } from '../../services/transaction/transaction'; import { IgxGridTransaction } from '../common/types'; +import { IgxGridValidationService } from '../grid/grid-validation.service'; export const hierarchicalTransactionServiceFactory = () => new IgxTransactionService(); @@ -148,6 +149,7 @@ export abstract class IgxHierarchicalGridBaseDirective extends IgxGridBaseDirect public abstract expandChildren: boolean; constructor( + public validationService: IgxGridValidationService, public selectionService: IgxGridSelectionService, public colResizingService: IgxColumnResizingService, @Inject(IGX_GRID_SERVICE_BASE) public gridAPI: IgxHierarchicalGridAPIService, @@ -171,6 +173,7 @@ export abstract class IgxHierarchicalGridBaseDirective extends IgxGridBaseDirect protected platform: PlatformUtil, @Optional() @Inject(IgxGridTransaction) protected _diTransactions?: TransactionService) { super( + validationService, selectionService, colResizingService, gridAPI, diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts index 0978da4dc62..fd3b4366fc0 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts @@ -43,6 +43,7 @@ import { IgxGridComponent } from '../grid/grid.component'; import { IgxOverlayOutletDirective } from '../../directives/toggle/toggle.directive'; import { IgxColumnResizingService } from '../resizing/resizing.service'; import { IgxGridExcelStyleFilteringComponent } from '../filtering/excel-style/grid.excel-style-filtering.component'; +import { IgxGridValidationService } from '../grid/grid-validation.service'; let NEXT_ID = 0; @@ -234,6 +235,7 @@ export class IgxChildGridRowComponent implements AfterViewInit, OnInit { templateUrl: 'hierarchical-grid.component.html', providers: [ IgxGridCRUDService, + IgxGridValidationService, IgxGridSelectionService, { provide: IGX_GRID_SERVICE_BASE, useClass: IgxHierarchicalGridAPIService }, { provide: IGX_GRID_BASE, useExisting: IgxHierarchicalGridComponent }, diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/row-island.component.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/row-island.component.ts index 9a38c0ba1fa..c068a23e769 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/row-island.component.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/row-island.component.ts @@ -47,6 +47,7 @@ import { IgxActionStripComponent } from '../../action-strip/action-strip.compone import { IgxPaginatorDirective } from '../../paginator/paginator-interfaces'; import { IgxFlatTransactionFactory } from '../../services/transaction/transaction-factory.service'; import { IGridCreatedEventArgs } from './events'; +import { IgxGridValidationService } from '../grid/grid-validation.service'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -217,6 +218,7 @@ export class IgxRowIslandComponent extends IgxHierarchicalGridBaseDirective } constructor( + public validationService: IgxGridValidationService, public selectionService: IgxGridSelectionService, public colResizingService: IgxColumnResizingService, @Inject(IGX_GRID_SERVICE_BASE) gridAPI: IgxHierarchicalGridAPIService, @@ -240,6 +242,7 @@ export class IgxRowIslandComponent extends IgxHierarchicalGridBaseDirective @Inject(LOCALE_ID) localeId: string, protected platform: PlatformUtil) { super( + validationService, selectionService, colResizingService, gridAPI, diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.ts index b35b1e0c8d3..f470555e3a8 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.ts @@ -74,6 +74,7 @@ import { PivotSortUtil } from './pivot-sort-util'; import { FilterUtil, IFilteringStrategy } from '../../data-operations/filtering-strategy'; import { IgxPivotValueChipTemplateDirective } from './pivot-grid.directives'; import { IFilteringOperation } from '../../data-operations/filtering-condition'; +import { IgxGridValidationService } from '../grid/grid-validation.service'; let NEXT_ID = 0; const MINIMUM_COLUMN_WIDTH = 200; @@ -102,6 +103,7 @@ const MINIMUM_COLUMN_WIDTH_SUPER_COMPACT = 104; templateUrl: 'pivot-grid.component.html', providers: [ IgxGridCRUDService, + IgxGridValidationService, IgxGridSummaryService, IgxGridSelectionService, GridBaseAPIService, @@ -911,6 +913,7 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni } constructor( + public validationService: IgxGridValidationService, public selectionService: IgxGridSelectionService, public colResizingService: IgxPivotColumnResizingService, gridAPI: GridBaseAPIService, @@ -934,6 +937,7 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni protected platform: PlatformUtil, @Optional() @Inject(IgxGridTransaction) protected _diTransactions?: TransactionService) { super( + validationService, selectionService, colResizingService, gridAPI, diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts index 7b3675ec36a..0dae7c38559 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts @@ -299,7 +299,6 @@ export class IgxTreeGridAPIService extends GridBaseAPIService { } else { mergeObjects(rowValueInDataSource, rowNewValue); } - grid.transactions.addValidation(transaction, rowCurrentValue); } private row_deleted_parent(rowID: any): boolean { diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts index c1850d681b8..de0a4ba2c5e 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts @@ -59,6 +59,7 @@ import { HierarchicalTransactionService } from '../../services/transaction/hiera import { IgxOverlayService } from '../../services/overlay/overlay'; import { IgxGridTransaction } from '../common/types'; import { TreeGridFilteringStrategy } from './tree-grid.filtering.strategy'; +import { IgxGridValidationService } from '../grid/grid-validation.service'; let NEXT_ID = 0; @@ -84,6 +85,7 @@ let NEXT_ID = 0; templateUrl: 'tree-grid.component.html', providers: [ IgxGridCRUDService, + IgxGridValidationService, IgxGridSummaryService, IgxGridNavigationService, { provide: IgxGridSelectionService, useClass: IgxTreeGridSelectionService }, @@ -391,6 +393,7 @@ export class IgxTreeGridComponent extends IgxGridBaseDirective implements GridTy // } constructor( + public validationService: IgxGridValidationService, public selectionService: IgxGridSelectionService, public colResizingService: IgxColumnResizingService, @Inject(IGX_GRID_SERVICE_BASE) public gridAPI: GridServiceType, @@ -416,7 +419,7 @@ export class IgxTreeGridComponent extends IgxGridBaseDirective implements GridTy @Optional() @Inject(IgxGridTransaction) protected _diTransactions?: HierarchicalTransactionService, ) { - super(selectionService, colResizingService, gridAPI, transactionFactory, + super(validationService, selectionService, colResizingService, gridAPI, transactionFactory, _elementRef, _zone, document, cdr, resolver, differs, viewRef, appRef, moduleRef, injector, navigation, filteringService, overlayService, summaryService, _displayDensityOptions, localeId, platform); } diff --git a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts index b182a525d77..597d751b471 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts @@ -47,9 +47,6 @@ export class IgxBaseTransactionService i protected _pendingTransactions: T[] = []; protected _pendingStates: Map = new Map(); - protected _validationTransactions: T[] = []; - protected _validationStates: Map = new Map(); - private _cloneStrategy: IDataCloneStrategy = new DefaultDataCloneStrategy(); /** @@ -86,66 +83,11 @@ export class IgxBaseTransactionService i const result: T[] = []; this._pendingStates.forEach((state: S, key: any) => { const value = mergeChanges ? this.getAggregatedValue(key, mergeChanges) : state.value; - result.push({ id: key, newValue: value, type: state.type, validity: state.validity } as T); + result.push({ id: key, newValue: value, type: state.type } as T); }); return result; } - /** - * @inheritdoc - */ - public addValidation(transaction: T, recordRef?: any): void { - this.updateValidationState(this._validationStates, transaction, recordRef); - this._validationTransactions.push(transaction); - } - - - /** - * Updates the provided validation states collection according to passed transaction and recordRef - * - * @param states States collection to apply the update to - * @param transaction Transaction to apply to the current state - * @param recordRef Reference to the value of the record in data source, if any, where transaction should be applied - */ - protected updateValidationState(states: Map, transaction: T, recordRef?: any): void { - if (!transaction) return; - let state = states.get(transaction.id); - if (state) { - if (isObject(state.value)) { - mergeObjects(state.value, transaction.newValue); - } else { - state.value = transaction.newValue; - } - this.updateValidity(state, transaction); - } else { - state = { value: this.cloneStrategy.clone(transaction.newValue), recordRef, type: transaction.type, validity: transaction.validity } as S; - states.set(transaction.id, state); - state.validity = transaction.validity; - } - } - - /** - * @inheritdoc - */ - public getAggregatedValidationChanges(): T[] { - const result: T[] = []; - this._validationStates.forEach((state: S, key: any) => { - result.push({ id: key, newValue: state.value, type: state.type, validity: state.validity } as T); - }); - return result; - } - - /** - * @inheritdoc - */ - public getAggregatedValidationState(id: any): any { - const state = this._validationStates.get(id); - if (!state) { - return null; - } - return state; - } - /** * @inheritdoc */ @@ -174,31 +116,12 @@ export class IgxBaseTransactionService i this.clear(_id); } - /** - * @inheritdoc - */ - public getInvalidTransactionLog(id?: any) { - let validationTransactions = [...this._validationTransactions]; - if (id !== undefined) { - validationTransactions = validationTransactions.filter(t => t.id === id); - } - return validationTransactions.filter(x => x.validity.some(y => y.valid === false)); - } - /** * @inheritdoc */ public clear(id?: any): void { this._pendingStates.clear(); this._pendingTransactions = []; - if (id !== undefined) { - this._validationTransactions = this._validationTransactions.filter(t => t.id !== id); - this._validationStates.delete(id); - } else { - this._validationStates.clear(); - this._validationTransactions = []; - } - } /** @@ -239,30 +162,6 @@ export class IgxBaseTransactionService i } } - /** - * Updates the validity state after update. - * - * @param state State to update value for - * @param transaction The transaction based on which to update. - */ - protected updateValidity(state, transaction) { - // update validity - if (transaction.validity) { - transaction.validity.forEach(validity => { - const existingState = state.validity?.find(x => x.field === validity.field); - if (existingState) { - existingState.valid = validity.valid; - existingState.formGroup = validity.formGroup; - } else { - if (!state.validity) { - state.validity = []; - } - state.validity.push(validity); - } - }); - } - } - /** * Updates the recordRef of the provided state with all the changes in the state. Accepts primitive and object value types * diff --git a/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts index 0a835c7c7e8..7788fa4ce1d 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts @@ -172,16 +172,12 @@ export class IgxTransactionService exten const lastActions: Action[] = this._undoStack.pop(); this._transactions.splice(this._transactions.length - lastActions.length); - this._validationTransactions.splice(this._validationTransactions.length - lastActions.length); this._redoStack.push(lastActions); this._states.clear(); - this._validationStates.clear(); for (const currentActions of this._undoStack) { for (const transaction of currentActions) { this.updateState(this._states, transaction.transaction, transaction.recordRef); - const validationTransaction = this._validationTransactions.find(x => x.id === transaction.transaction.id); - this.updateValidationState(this._validationStates, validationTransaction, transaction.recordRef); } } @@ -196,9 +192,7 @@ export class IgxTransactionService exten const actions: Action[] = this._redoStack.pop(); for (const action of actions) { this.updateState(this._states, action.transaction, action.recordRef); - this.updateValidationState(this._validationStates, action.transaction, action.recordRef); this._transactions.push(action.transaction); - this._validationTransactions.push(action.transaction); } this._undoStack.push(actions); diff --git a/projects/igniteui-angular/src/lib/services/transaction/transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/transaction.ts index a40d2bd72ea..941fd07ee09 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/transaction.ts @@ -21,7 +21,6 @@ export interface Transaction { id: any; type: TransactionType; newValue: any; - validity?: IFieldValid[]; } /** @@ -36,7 +35,6 @@ export interface State { value: any; recordRef: any; type: TransactionType; - validity?: IFieldValid[]; } export interface Action { @@ -91,29 +89,7 @@ export interface TransactionService { */ add(transaction: T, recordRef?: any): void; - /** - * Adds provided transaction with validation status. - * - * @param transaction Transaction to be added - * @param recordRef Reference to the value of the record in the data source related to the changed item - */ - addValidation(transaction: T, recordRef?: any): void; - - /** - * Returns the validation state of the record with provided id - * - * @param id The id of the record - * @returns State of the record if any - */ - getAggregatedValidationState(id: any): T; - - /** - * Returns aggregated validation changes from all transactions - * @returns The states with the validation status. - */ - getAggregatedValidationChanges(): T[]; - - /** + /** * Returns all recorded transactions in chronological order * * @param id Optional record id to get transactions for @@ -126,11 +102,6 @@ export interface TransactionService { */ undo(): void; - /** - * Returns invalid transactions. - */ - getInvalidTransactionLog(id?: any): T[]; - /** * Applies the last undone transaction if any */ diff --git a/src/app/grid-validation/grid-validation.sample.component.ts b/src/app/grid-validation/grid-validation.sample.component.ts index c81973aa7cd..1a7d964ed49 100644 --- a/src/app/grid-validation/grid-validation.sample.component.ts +++ b/src/app/grid-validation/grid-validation.sample.component.ts @@ -50,14 +50,14 @@ export class GridValidationSampleComponent { public gridNoTransactions: IgxGridComponent; public commitWithTransactions() { - const invalidTransactions = this.gridWithTransaction.transactions.getInvalidTransactionLog(); - if (invalidTransactions.length > 0) { - if (confirm('There are invalid values about to be submitted. Do you want to continue')) { - this.gridWithTransaction.transactions.commit(this.transactionData); - } - } else { - this.gridWithTransaction.transactions.commit(this.transactionData); - } + // const invalidTransactions = this.gridWithTransaction.transactions.getInvalidTransactionLog(); + // if (invalidTransactions.length > 0) { + // if (confirm('There are invalid values about to be submitted. Do you want to continue')) { + // this.gridWithTransaction.transactions.commit(this.transactionData); + // } + // } else { + // this.gridWithTransaction.transactions.commit(this.transactionData); + // } } public commitNoTransactions() { From 0d0cac397641ffc863618aff3ddbbdb578dccf8f Mon Sep 17 00:00:00 2001 From: MKirova Date: Mon, 22 Aug 2022 13:57:58 +0300 Subject: [PATCH 074/149] chore(*): Update apis and sample. --- .../src/lib/grids/api.service.ts | 7 +- .../src/lib/grids/cell.component.ts | 4 +- .../src/lib/grids/common/crud.service.ts | 8 +-- .../src/lib/grids/common/grid.interface.ts | 16 ++++- .../src/lib/grids/grid-base.directive.ts | 10 ++- .../lib/grids/grid/grid-validation.service.ts | 66 +++++++++++++++++-- .../grid-validation.sample.component.html | 4 +- .../grid-validation.sample.component.ts | 30 +++++---- 8 files changed, 111 insertions(+), 34 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/api.service.ts b/projects/igniteui-angular/src/lib/grids/api.service.ts index b1771c0cb58..273e88672fe 100644 --- a/projects/igniteui-angular/src/lib/grids/api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/api.service.ts @@ -545,7 +545,6 @@ export class GridBaseAPIService implements GridServiceType { * @param rowNewValue New value of the row */ protected updateData(grid, rowID, rowValueInDataSource: any, rowCurrentValue: any, rowNewValue: { [x: string]: any }) { - const transaction: Transaction = { id: rowID, type: TransactionType.UPDATE, @@ -556,9 +555,9 @@ export class GridBaseAPIService implements GridServiceType { } else { mergeObjects(rowValueInDataSource, rowNewValue); } - const formGroup = this.grid.validationService.getFormGroup(rowID); - const validation = grid.validationService as IgxGridValidationService; - validation.addRecordState(rowID, formGroup) + const formGroup = this.grid.validation.getFormGroup(rowID); + const validation = grid.validation as IgxGridValidationService; + validation.add(rowID, formGroup) } diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 4a01b649090..9c1a32a9041 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -108,7 +108,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT public get formGroup(): FormGroup { - return this.grid.validationService.getFormGroup(this.intRow.key) || new FormGroup({}); + return this.grid.validation.getFormGroup(this.intRow.key) || new FormGroup({}); } /** @@ -485,7 +485,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT @HostBinding('class.igx-grid__td--invalid') @HostBinding('attr.aria-invalid') public get isInvalid() { - return this.formGroup?.get(this.column?.field)?.invalid; + return !this.intRow.deleted && this.formGroup?.get(this.column?.field)?.invalid; } /** diff --git a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts index e987e1bad79..9112e374961 100644 --- a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts +++ b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts @@ -13,7 +13,7 @@ export class IgxEditRow { public rowFormGroup = new FormGroup({}); constructor(public id: any, public index: number, public data: any, public grid: GridType) { - this.rowFormGroup = this.grid.validationService.createFormGroupForRecord(id, data); + this.rowFormGroup = this.grid.validation.create(id, data); } public createEditEventArgs(includeNewValue = true, event?: Event): IGridEditEventArgs { @@ -99,7 +99,7 @@ export class IgxCell { public editValue: any, public rowData: any, public grid: GridType) { - this.grid.validationService.createFormGroupForRecord(id.rowID, rowData); + this.grid.validation.create(id.rowID, rowData); } public castToNumber(value: any): any { @@ -111,7 +111,7 @@ export class IgxCell { } public createEditEventArgs(includeNewValue = true, event?: Event): IGridEditEventArgs { - const formControl = this.grid.validationService.getFormControl(this.id.rowID, this.column.field); + const formControl = this.grid.validation.getFormControl(this.id.rowID, this.column.field); const args: IGridEditEventArgs = { rowID: this.id.rowID, cellID: this.id, @@ -133,7 +133,7 @@ export class IgxCell { const updatedData = this.grid.transactions.enabled ? this.grid.transactions.getAggregatedValue(this.id.rowID, true) : this.rowData; const rowData = updatedData === null ? this.grid.gridAPI.getRowData(this.id.rowID) : updatedData; - const formControl = this.grid.validationService.getFormControl(this.id.rowID, this.column.field); + const formControl = this.grid.validation.getFormControl(this.id.rowID, this.column.field); const args: IGridEditDoneEventArgs = { rowID: this.id.rowID, cellID: this.id, diff --git a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts index 098c773d7f9..efc70a9d25c 100644 --- a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts @@ -36,7 +36,7 @@ import { OverlaySettings } from '../../services/overlay/utilities'; import { IPinningConfig } from '../grid.common'; import { IDimensionsChange, IPivotConfiguration, IPivotDimension, IPivotKeys, IPivotValue, IValuesChange, PivotDimensionType } from '../pivot-grid/pivot-grid.interface'; import { IDataCloneStrategy } from '../../data-operations/data-clone-strategy'; -import { FormGroup } from '@angular/forms'; +import { FormGroup, ValidationErrors } from '@angular/forms'; import { IgxGridValidationService } from '../grid/grid-validation.service'; export const IGX_GRID_BASE = new InjectionToken('IgxGridBaseToken'); @@ -220,6 +220,18 @@ Valid, Invalid } +export interface IRecordValidationState { + id: any; + valid: boolean; + state: IFieldValidationState[]; +} + +export interface IFieldValidationState { + field: string, + valid: boolean, + errors: ValidationErrors +} + export interface GridServiceType { grid: GridType; @@ -400,7 +412,7 @@ export interface GridType extends IGridDataBindable { filteredSortedData: any[]; dataWithAddedInTransactionRows: any[]; transactions: TransactionService; - validationService: IgxGridValidationService; + validation: IgxGridValidationService; defaultSummaryHeight: number; summaryRowHeight: number; rowEditingOverlay: IgxToggleDirective; diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index fe552d5d2b2..051dadeefe5 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -3048,7 +3048,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements } constructor( - public validationService: IgxGridValidationService, + public validation: IgxGridValidationService, public selectionService: IgxGridSelectionService, public colResizingService: IgxColumnResizingService, @Inject(IGX_GRID_SERVICE_BASE) public gridAPI: GridServiceType, @@ -3256,7 +3256,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements this.gridAPI.grid = this as any; this.crudService.grid = this as any; this.selectionService.grid = this as any; - this.validationService.grid = this as any; + this.validation.grid = this as any; this.navigation.grid = this as any; this.filteringService.grid = this as any; this.summaryService.grid = this as any; @@ -6203,6 +6203,12 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements this.summaryService.clearSummaryCache(); this.pipeTrigger++; this.notifyChanges(); + if (event.origin === TransactionEventOrigin.REDO || event.origin === TransactionEventOrigin.UNDO) { + event.actions.forEach(x => { + const value = this.transactions.getAggregatedValue(x.transaction.id, true); + this.validation.update(x.transaction.id, value ?? x.recordRef); + }); + } }; protected writeToData(rowIndex: number, value: any) { diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index d453d582703..2026737c589 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -1,13 +1,17 @@ import { Injectable } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; -import { GridType } from '../common/grid.interface'; +import { GridType, IFieldValidationState, IRecordValidationState } from '../common/grid.interface'; @Injectable() export class IgxGridValidationService { public grid: GridType; private _validityStates = new Map(); - public createFormGroupForRecord(rowId, data) { + /** + * @hidden + * @internal + */ + public create(rowId, data) { let formGroup = this._validityStates.get(rowId); if (!formGroup) { formGroup = new FormGroup({}); @@ -18,28 +22,76 @@ export class IgxGridValidationService { formGroup.addControl(field, control); } this.grid.onFormGroupCreate.emit(formGroup); - this.addRecordState(rowId, formGroup); + this.add(rowId, formGroup); } return formGroup; } + /** + * @hidden + * @internal + */ public getFormGroup(id: any) { return this._validityStates.get(id); } + /** + * @hidden + * @internal + */ public getFormControl(rowId: any, columnKey: string) { const formControl = this.getFormGroup(rowId); return formControl.get(columnKey); } - public addRecordState(rowId: any, form: FormGroup ) { + /** + * @hidden + * @internal + */ + public add(rowId: any, form: FormGroup ) { this._validityStates.set(rowId, form); } - public getInvalidStates() { - //todo - this._validityStates.forEach(x => { + public getValidity() : IRecordValidationState[] { + const states: IRecordValidationState[] = []; + this._validityStates.forEach((formGroup, key) => { + const state: IFieldValidationState[] = []; + for (const col of this.grid.columns) { + const control = formGroup.get(col.field); + if (control) { + state.push({field: col.field, valid: control.valid, errors: control.errors }) + } + } + states.push({id: key, valid: formGroup.valid, state: state }); }); + return states; + } + + public getInvalid(): IRecordValidationState[] { + const validity = this.getValidity(); + return validity.filter(x => !x.valid); + } + + /** + * @hidden + * @internal + */ + public update(rowId:any, rowData: any) { + const rowGroup = this.getFormGroup(rowId); + for (const col of this.grid.columns) { + const control = rowGroup.get(col.field); + if (control) { + control.setValue(rowData[col.field]); + } + } + } + + public clear(rowId?: any) { + if (rowId) { + this._validityStates.delete(rowId); + } else { + this._validityStates.clear(); + } } } \ No newline at end of file diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index aab295412b9..e0e722cac14 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -16,7 +16,7 @@

Grid without transactions

- +

Grid with transactions

@@ -39,4 +39,6 @@

Grid with transactions

+ +
\ No newline at end of file diff --git a/src/app/grid-validation/grid-validation.sample.component.ts b/src/app/grid-validation/grid-validation.sample.component.ts index 1a7d964ed49..1ec4374d7f1 100644 --- a/src/app/grid-validation/grid-validation.sample.component.ts +++ b/src/app/grid-validation/grid-validation.sample.component.ts @@ -50,20 +50,26 @@ export class GridValidationSampleComponent { public gridNoTransactions: IgxGridComponent; public commitWithTransactions() { - // const invalidTransactions = this.gridWithTransaction.transactions.getInvalidTransactionLog(); - // if (invalidTransactions.length > 0) { - // if (confirm('There are invalid values about to be submitted. Do you want to continue')) { - // this.gridWithTransaction.transactions.commit(this.transactionData); - // } - // } else { - // this.gridWithTransaction.transactions.commit(this.transactionData); - // } + const invalid = this.gridWithTransaction.validation.getInvalid(); + if (invalid.length > 0) { + if (confirm('There are invalid values about to be submitted. Do you want to continue')) { + this.gridWithTransaction.transactions.commit(this.transactionData); + } + } else { + this.gridWithTransaction.transactions.commit(this.transactionData); + } + } + + public undo() { + this.gridWithTransaction.transactions.undo(); + } + + public redo() { + this.gridWithTransaction.transactions.redo(); } - public commitNoTransactions() { - console.log(this.data); - this.gridNoTransactions.transactions.commit([]); - this.gridNoTransactions.markForCheck(); + public clearValidity() { + this.gridNoTransactions.validation.clear(); } public cellEdit(evt) { From 411011f5e6cf1ab893f0c9a0133bf84b0eb30227 Mon Sep 17 00:00:00 2001 From: MKirova Date: Mon, 22 Aug 2022 14:05:55 +0300 Subject: [PATCH 075/149] chore(*): Update api docs. --- .../lib/grids/grid/grid-validation.service.ts | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index 2026737c589..cb0490eba95 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -5,7 +5,7 @@ import { GridType, IFieldValidationState, IRecordValidationState } from '../comm @Injectable() export class IgxGridValidationService { public grid: GridType; - private _validityStates = new Map(); + private _validityStates = new Map(); /** * @hidden @@ -48,25 +48,31 @@ export class IgxGridValidationService { * @hidden * @internal */ - public add(rowId: any, form: FormGroup ) { + public add(rowId: any, form: FormGroup) { this._validityStates.set(rowId, form); } - public getValidity() : IRecordValidationState[] { + /** Returns validity states for records. + * @returns Array os records states. + */ + public getValidity(): IRecordValidationState[] { const states: IRecordValidationState[] = []; this._validityStates.forEach((formGroup, key) => { const state: IFieldValidationState[] = []; for (const col of this.grid.columns) { const control = formGroup.get(col.field); if (control) { - state.push({field: col.field, valid: control.valid, errors: control.errors }) + state.push({ field: col.field, valid: control.valid, errors: control.errors }) } } - states.push({id: key, valid: formGroup.valid, state: state }); + states.push({ id: key, valid: formGroup.valid, state: state }); }); return states; } + /** Returns all invalid record states. + * @returns Array of IRecordValidationState. + */ public getInvalid(): IRecordValidationState[] { const validity = this.getValidity(); return validity.filter(x => !x.valid); @@ -76,7 +82,7 @@ export class IgxGridValidationService { * @hidden * @internal */ - public update(rowId:any, rowData: any) { + public update(rowId: any, rowData: any) { const rowGroup = this.getFormGroup(rowId); for (const col of this.grid.columns) { const control = rowGroup.get(col.field); @@ -86,6 +92,10 @@ export class IgxGridValidationService { } } + /** Clears validity state by id or clears all states if no id is passed. + * @param id The id of the record for which to clear state. + * @returns Array of IRecordValidationState. + */ public clear(rowId?: any) { if (rowId) { this._validityStates.delete(rowId); From 0c36b939d6b57d775ece7512911bb075d240e876 Mon Sep 17 00:00:00 2001 From: MKirova Date: Mon, 22 Aug 2022 14:25:17 +0300 Subject: [PATCH 076/149] chore(*): Small renames suggested from review. --- CHANGELOG.md | 4 ++-- .../igniteui-angular/src/lib/grids/common/crud.service.ts | 7 +++---- projects/igniteui-angular/src/lib/grids/common/events.ts | 2 +- .../src/lib/grids/common/grid.interface.ts | 2 +- .../igniteui-angular/src/lib/grids/grid-base.directive.ts | 2 +- .../src/lib/grids/grid/grid-validation.service.ts | 2 +- .../grid-validation/grid-validation.sample.component.html | 2 +- 7 files changed, 10 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57eb2cca8b2..df018db324b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,10 +77,10 @@ All notable changes for each version of this project will be documented in this ```html ``` - 2. Via reactive forms using the FormGroup exposed via the `onFormGroupCreate` event of the grid: + 2. Via reactive forms using the FormGroup exposed via the `formGroupCreated` event of the grid: ```html - + ``` ```ts diff --git a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts index 9112e374961..adeadef4fd9 100644 --- a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts +++ b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts @@ -24,7 +24,7 @@ export class IgxEditRow { cancel: false, owner: this.grid, isAddRow: false, - isValid: this.rowFormGroup.valid, + invalid: this.rowFormGroup.invalid, event }; if (includeNewValue) { @@ -120,7 +120,7 @@ export class IgxCell { cancel: false, column: this.column, owner: this.grid, - isValid: formControl ? formControl.valid : true, + invalid: formControl ? formControl.invalid : false, event }; if (includeNewValue) { @@ -141,7 +141,7 @@ export class IgxCell { // the only case we use this.rowData directly, is when there is no rowEditing or transactions enabled rowData, oldValue: this.value, - isValid: formControl ? formControl.valid : true, + invalid: formControl ? formControl.invalid : false, newValue: value, column: this.column, owner: this.grid, @@ -297,7 +297,6 @@ export class IgxRowCrudState extends IgxCellCrudState { if (!this.row || !(this.row.getClassName() === IgxEditRow.name)) { if (!this.row) { this.createRow(this.cell); - this.grid.onFormGroupCreate.emit(this.row.rowFormGroup); } const rowArgs = this.row.createEditEventArgs(false, event); diff --git a/projects/igniteui-angular/src/lib/grids/common/events.ts b/projects/igniteui-angular/src/lib/grids/common/events.ts index c025915f276..a30e473d738 100644 --- a/projects/igniteui-angular/src/lib/grids/common/events.ts +++ b/projects/igniteui-angular/src/lib/grids/common/events.ts @@ -31,7 +31,7 @@ export interface IGridEditDoneEventArgs extends IBaseEventArgs { column?: ColumnType; owner?: GridType; isAddRow?: boolean; - isValid?: boolean; + invalid?: boolean; } export interface IGridEditEventArgs extends CancelableEventArgs, IGridEditDoneEventArgs { diff --git a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts index efc70a9d25c..69651231a8b 100644 --- a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts @@ -518,7 +518,7 @@ export interface GridType extends IGridDataBindable { rowDragStart: EventEmitter; rowDragEnd: EventEmitter; rowToggle: EventEmitter; - onFormGroupCreate: EventEmitter; + formGroupCreated: EventEmitter; validationStatusChange: EventEmitter; toolbarExporting: EventEmitter; diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index 051dadeefe5..c71bc32521b 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -505,7 +505,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements public cellClick = new EventEmitter(); @Output() - public onFormGroupCreate = new EventEmitter(); + public formGroupCreated = new EventEmitter(); @Output() public validationStatusChange = new EventEmitter(); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index cb0490eba95..09c30d35943 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -21,7 +21,7 @@ export class IgxGridValidationService { control.addValidators(col.validators); formGroup.addControl(field, control); } - this.grid.onFormGroupCreate.emit(formGroup); + this.grid.formGroupCreated.emit(formGroup); this.add(rowId, formGroup); } return formGroup; diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index e0e722cac14..57f73c17861 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -2,7 +2,7 @@

Grid without transactions

Row edit - From 3f6c11270a9e1887893ed861a29799b8f731b844 Mon Sep 17 00:00:00 2001 From: MKirova Date: Mon, 22 Aug 2022 14:43:20 +0300 Subject: [PATCH 077/149] chore(*): Update tests. --- .../lib/grids/grid/grid-cell-editing.spec.ts | 24 ++++++++--------- .../lib/grids/grid/grid-row-editing.spec.ts | 26 +++++++++---------- .../lib/grids/grid/grid.nested.props.spec.ts | 12 ++++----- .../grid-cellEditing.component.ts | 4 +-- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-cell-editing.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-cell-editing.spec.ts index 75a13182083..cc656afb080 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-cell-editing.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-cell-editing.spec.ts @@ -578,7 +578,7 @@ describe('IgxGrid - Cell Editing #grid', () => { rowData: initialRowData, oldValue: 'John Brown', cancel: false, - isValid: true, + invalid: false, column: cell.column, owner: grid, event: jasmine.anything() as any @@ -599,7 +599,7 @@ describe('IgxGrid - Cell Editing #grid', () => { rowID: cell2.row.key, rowData: initialRowData, oldValue: 20, - isValid: true, + invalid: false, cancel: false, column: cell2.column, owner: grid, @@ -628,7 +628,7 @@ describe('IgxGrid - Cell Editing #grid', () => { rowData: initialRowData, oldValue: 'John Brown', cancel: true, - isValid: true, + invalid: false, column: cell.column, owner: grid, event: jasmine.anything() as any @@ -655,7 +655,7 @@ describe('IgxGrid - Cell Editing #grid', () => { column: cell.column, owner: grid, event: jasmine.anything() as any, - isValid: true + invalid: false }; expect(grid.cellEditEnter.emit).toHaveBeenCalledTimes(2); expect(grid.cellEditEnter.emit).toHaveBeenCalledWith(cellArgs); @@ -680,7 +680,7 @@ describe('IgxGrid - Cell Editing #grid', () => { cellID: cell.cellID, rowData: initialRowData, newValue: 'John Brown', - isValid: true, + invalid: false, oldValue: 'John Brown', column: cell.column, owner: grid, @@ -703,7 +703,7 @@ describe('IgxGrid - Cell Editing #grid', () => { rowData: initialRowData, newValue: 20, oldValue: 20, - isValid: true, + invalid: false, column: cell.column, owner: grid, event: jasmine.anything() as any @@ -740,7 +740,7 @@ describe('IgxGrid - Cell Editing #grid', () => { column: cell.column, owner: grid, event: jasmine.anything() as any, - isValid: true + invalid: false }; expect(grid.cellEdit.emit).toHaveBeenCalledTimes(1); expect(grid.cellEdit.emit).toHaveBeenCalledWith(cellArgs); @@ -766,7 +766,7 @@ describe('IgxGrid - Cell Editing #grid', () => { column: cell.column, owner: grid, event: jasmine.anything() as any, - isValid: true + invalid: false }; expect(grid.cellEdit.emit).toHaveBeenCalledTimes(2); expect(grid.cellEdit.emit).toHaveBeenCalledWith(cellArgs); @@ -804,7 +804,7 @@ describe('IgxGrid - Cell Editing #grid', () => { cancel: true, column: cell.column, owner: grid, - isValid: true, + invalid: false, event: undefined }; expect(grid.cellEdit.emit).toHaveBeenCalledTimes(1); @@ -977,7 +977,7 @@ describe('IgxGrid - Cell Editing #grid', () => { column: cell.column, owner: grid, event: jasmine.anything() as any, - isValid: true + invalid: false }; expect(grid.cellEditExit.emit).toHaveBeenCalledTimes(1); expect(grid.cellEditExit.emit).toHaveBeenCalledWith(cellArgs); @@ -1015,7 +1015,7 @@ describe('IgxGrid - Cell Editing #grid', () => { column: cell.column, owner: grid, event: jasmine.anything() as any, - isValid: true + invalid: false }; expect(grid.cellEditDone.emit).toHaveBeenCalledTimes(1); expect(grid.cellEditDone.emit).toHaveBeenCalledWith(cellArgs); @@ -1040,7 +1040,7 @@ describe('IgxGrid - Cell Editing #grid', () => { column: cell.column, owner: grid, event: jasmine.anything() as any, - isValid: true + invalid: false }; expect(grid.cellEditDone.emit).toHaveBeenCalledTimes(2); expect(grid.cellEditDone.emit).toHaveBeenCalledWith(cellArgs); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts index 3d9469cff6f..1204eb9fe0f 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts @@ -153,7 +153,7 @@ describe('IgxGrid - Row Editing #grid', () => { cancel: false, column: cell.column, owner: grid, - isValid: true, + invalid: false, event: jasmine.anything() as any }; let rowEditArgs: IGridEditEventArgs = { @@ -161,7 +161,7 @@ describe('IgxGrid - Row Editing #grid', () => { rowData: initialRowData, oldValue: row.data, cancel: false, - isValid: true, + invalid: false, owner: grid, isAddRow: row.addRowUI, event: jasmine.anything() as any @@ -179,7 +179,7 @@ describe('IgxGrid - Row Editing #grid', () => { rowID: cell.row.key, rowData: cell.row.data, oldValue: cell.value, - isValid: true, + invalid: false, newValue: cell.value, column: cell.column, owner: grid, @@ -213,7 +213,7 @@ describe('IgxGrid - Row Editing #grid', () => { rowData: Object.assign({}, row.data, { ProductName: newCellValue }), oldValue: cell.value, newValue: newCellValue, - isValid: true, + invalid: false, column: cell.column, owner: grid, event: jasmine.anything() as any @@ -230,7 +230,7 @@ describe('IgxGrid - Row Editing #grid', () => { cancel: false, owner: grid, isAddRow: row.addRowUI, - isValid: true, + invalid: false, event: jasmine.anything() as any }; @@ -240,7 +240,7 @@ describe('IgxGrid - Row Editing #grid', () => { rowData: updatedRowData, // with rowEditable - IgxGridRowEditingComponent oldValue: cell.value, newValue: newCellValue, - isValid: true, + invalid: false, column: cell.column, owner: grid, event: jasmine.anything() as any @@ -1723,7 +1723,7 @@ describe('IgxGrid - Row Editing #grid', () => { cancel: false, owner: grid, isAddRow: false, - isValid: true, + invalid: false, event: jasmine.anything() as any }); }); @@ -1764,7 +1764,7 @@ describe('IgxGrid - Row Editing #grid', () => { owner: grid, isAddRow: false, event: jasmine.anything() as any, - isValid: true + invalid: false }); // Enter cell edit mode again @@ -1789,7 +1789,7 @@ describe('IgxGrid - Row Editing #grid', () => { owner: grid, isAddRow: false, event: jasmine.anything() as any, - isValid: true + invalid: false }); }); @@ -1845,7 +1845,7 @@ describe('IgxGrid - Row Editing #grid', () => { owner: grid, isAddRow: false, event: jasmine.anything() as any, - isValid: true + invalid: false }); }); @@ -1876,7 +1876,7 @@ describe('IgxGrid - Row Editing #grid', () => { owner: grid, isAddRow: false, event: jasmine.anything() as any, - isValid: true + invalid: false }); }); @@ -1957,7 +1957,7 @@ describe('IgxGrid - Row Editing #grid', () => { column: cell.column, owner: grid, event: jasmine.anything() as any, - isValid: true + invalid: false }; UIInteractions.simulateDoubleClickAndSelectEvent(cellElem); @@ -2161,7 +2161,7 @@ describe('IgxGrid - Row Editing #grid', () => { column: cell.column, owner: grid, event: jasmine.anything() as any, - isValid: true + invalid: false }; const rowDoneArgs: IGridEditDoneEventArgs = { diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts index d8a8f68e5d9..1e8911807f9 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts @@ -540,7 +540,7 @@ describe('Edit cell with data of type Array #grid', () => { column: cell.column, owner: grid, event: jasmine.anything() as any, - isValid: true + invalid: false }; expect(grid.cellEditEnter.emit).toHaveBeenCalledTimes(1); @@ -603,7 +603,7 @@ describe('Edit cell with data of type Array #grid', () => { column: cell.column, owner: grid, event: jasmine.anything() as any, - isValid: true + invalid: false }; expect(grid.cellEditEnter.emit).toHaveBeenCalledTimes(1); @@ -671,7 +671,7 @@ describe('Edit cell with data of type Array #grid', () => { isAddRow: row.addRowUI, cancel: false, event: jasmine.anything() as any, - isValid: true + invalid: false }; expect(grid.rowEditEnter.emit).toHaveBeenCalledTimes(1); @@ -701,7 +701,7 @@ describe('Edit cell with data of type Array #grid', () => { expect(rowArgs.newValue.locations.length).toEqual(3); expect(rowArgs.oldValue.locations.length).toEqual(3); - delete rowArgs.isValid; + delete rowArgs.invalid; expect(grid.rowEditExit.emit).toHaveBeenCalledTimes(1); expect(grid.rowEditExit.emit).toHaveBeenCalledWith(rowArgs); @@ -736,7 +736,7 @@ describe('Edit cell with data of type Array #grid', () => { isAddRow: row.addRowUI, cancel: false, event: jasmine.anything() as any, - isValid: true + invalid: false }; expect(grid.rowEditEnter.emit).toHaveBeenCalledTimes(1); @@ -770,7 +770,7 @@ describe('Edit cell with data of type Array #grid', () => { delete rowArgs.cancel; rowArgs.rowData = initialRowData; - delete rowArgs.isValid; + delete rowArgs.invalid; expect(grid.rowEditDone.emit).toHaveBeenCalledTimes(1); expect(grid.rowEditDone.emit).toHaveBeenCalledWith(rowArgs); diff --git a/src/app/grid-cellEditing/grid-cellEditing.component.ts b/src/app/grid-cellEditing/grid-cellEditing.component.ts index 987212fe50f..d99fcae083c 100644 --- a/src/app/grid-cellEditing/grid-cellEditing.component.ts +++ b/src/app/grid-cellEditing/grid-cellEditing.component.ts @@ -56,13 +56,13 @@ export class GridCellEditingComponent { public cellEdit(evt) { - if (!evt.isValid) { + if (evt.invalid) { evt.cancel = true; } } public rowEdit(evt) { - if (!evt.isValid) { + if (evt.invalid) { evt.cancel = true; } } From 60e3680fc080690b8349f6d5d278a4ea572887ef Mon Sep 17 00:00:00 2001 From: MKirova Date: Mon, 22 Aug 2022 18:02:35 +0300 Subject: [PATCH 078/149] chore(*): Remove igxColumnValidator. Use DI on column constructor instead. --- .../src/lib/grids/columns/column.component.ts | 9 ++- .../src/lib/grids/columns/column.module.ts | 14 ++-- .../lib/grids/columns/validators.directive.ts | 69 +++---------------- .../grid-cellEditing.component.ts | 2 +- .../grid-validation.sample.component.ts | 6 +- 5 files changed, 30 insertions(+), 70 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/columns/column.component.ts b/projects/igniteui-angular/src/lib/grids/columns/column.component.ts index 26a20a5b442..7e7ae19a7c3 100644 --- a/projects/igniteui-angular/src/lib/grids/columns/column.component.ts +++ b/projects/igniteui-angular/src/lib/grids/columns/column.component.ts @@ -13,6 +13,8 @@ import { EventEmitter, OnDestroy, Inject, + Optional, + Self, } from '@angular/core'; import { notifyChanges } from '../watch-changes'; import { WatchColumnChanges } from '../watch-changes'; @@ -52,7 +54,7 @@ import { DropPosition } from '../moving/moving.service'; import { IColumnVisibilityChangingEventArgs, IPinColumnCancellableEventArgs, IPinColumnEventArgs } from '../common/events'; import { isConstructor, PlatformUtil } from '../../core/utils'; import { IgxGridCell } from '../grid-public-cell'; -import { Validator } from '@angular/forms'; +import { NG_VALIDATORS, Validator } from '@angular/forms'; const DEFAULT_DATE_FORMAT = 'mediumDate'; const DEFAULT_TIME_FORMAT = 'mediumTime'; @@ -1737,9 +1739,12 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy constructor( @Inject(IGX_GRID_BASE) public grid: GridType, + @Optional() @Self() @Inject(NG_VALIDATORS) private _validators: Validator[], public cdr: ChangeDetectorRef, protected platform: PlatformUtil, - ) { } + ) { + this.validators = _validators; + } /** * @hidden diff --git a/projects/igniteui-angular/src/lib/grids/columns/column.module.ts b/projects/igniteui-angular/src/lib/grids/columns/column.module.ts index e2d24493d7a..c62a7ac9e95 100644 --- a/projects/igniteui-angular/src/lib/grids/columns/column.module.ts +++ b/projects/igniteui-angular/src/lib/grids/columns/column.module.ts @@ -12,9 +12,11 @@ import { IgxFilterCellTemplateDirective, IgxSummaryTemplateDirective } from './templates.directive'; -import { IgxColumMaxLengthValidatorDirective, IgxColumnEmailValidatorDirective, IgxColumnMaxValidatorDirective, - IgxColumnMinLengthValidatorDirective, IgxColumnMinValidatorDirective, IgxColumnRequiredValidatorDirective, - IgxColumPatternValidatorDirective } from './validators.directive'; +import { + IgxColumMaxLengthValidatorDirective, IgxColumnEmailValidatorDirective, IgxColumnMaxValidatorDirective, + IgxColumnMinLengthValidatorDirective, IgxColumnMinValidatorDirective, IgxColumnRequiredValidatorDirective, + IgxColumPatternValidatorDirective +} from './validators.directive'; @NgModule({ declarations: [ @@ -22,7 +24,7 @@ import { IgxColumMaxLengthValidatorDirective, IgxColumnEmailValidatorDirective, IgxColumnMinValidatorDirective, IgxColumnMaxValidatorDirective, IgxColumnMinLengthValidatorDirective, - IgxColumMaxLengthValidatorDirective, + IgxColumMaxLengthValidatorDirective, IgxColumnEmailValidatorDirective, IgxColumPatternValidatorDirective, IgxFilterCellTemplateDirective, @@ -42,7 +44,7 @@ import { IgxColumMaxLengthValidatorDirective, IgxColumnEmailValidatorDirective, IgxColumnMinValidatorDirective, IgxColumnMaxValidatorDirective, IgxColumnMinLengthValidatorDirective, - IgxColumMaxLengthValidatorDirective, + IgxColumMaxLengthValidatorDirective, IgxColumnEmailValidatorDirective, IgxColumPatternValidatorDirective, IgxFilterCellTemplateDirective, @@ -58,4 +60,4 @@ import { IgxColumMaxLengthValidatorDirective, IgxColumnEmailValidatorDirective, IgxColumnLayoutComponent ] }) -export class IgxGridColumnModule {} +export class IgxGridColumnModule { } diff --git a/projects/igniteui-angular/src/lib/grids/columns/validators.directive.ts b/projects/igniteui-angular/src/lib/grids/columns/validators.directive.ts index 88b156f1122..6e02412e688 100644 --- a/projects/igniteui-angular/src/lib/grids/columns/validators.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/columns/validators.directive.ts @@ -3,35 +3,16 @@ import { RequiredValidator, NG_VALIDATORS, Validators, ValidationErrors, MinVali import { IgxColumnComponent } from './column.component'; -@Directive({ - providers: [{ provide: NG_VALIDATORS, useExisting: IgxColumnValidator, multi: true }] -}) -export abstract class IgxColumnValidator extends Validators { - public value: any; - constructor(private column?: IgxColumnComponent) { - super(); - if (column) { - column.validators.push(this); - } - } - - public abstract validate(value : any) : ValidationErrors | null; -} - @Directive({ // eslint-disable-next-line @angular-eslint/directive-selector selector: 'igx-column[required]', providers: [{ provide: NG_VALIDATORS, - useExisting: forwardRef(() => RequiredValidator), + useExisting: IgxColumnRequiredValidatorDirective, multi: true }] }) export class IgxColumnRequiredValidatorDirective extends RequiredValidator { - constructor(private column: IgxColumnComponent) { - super(); - column.validators.push(this); - } } @Directive({ @@ -39,16 +20,11 @@ export class IgxColumnRequiredValidatorDirective extends RequiredValidator { selector: 'igx-column[min]', providers: [{ provide: NG_VALIDATORS, - useExisting: forwardRef(() => MinValidator), + useExisting: IgxColumnMinValidatorDirective, multi: true }] }) -export class IgxColumnMinValidatorDirective extends MinValidator { - constructor(private column: IgxColumnComponent) { - super(); - column.validators.push(this); - } -} +export class IgxColumnMinValidatorDirective extends MinValidator { } @Directive({ @@ -56,16 +32,11 @@ export class IgxColumnMinValidatorDirective extends MinValidator { selector: 'igx-column[max]', providers: [{ provide: NG_VALIDATORS, - useExisting: forwardRef(() => MaxValidator), + useExisting: IgxColumnMaxValidatorDirective, multi: true }] }) -export class IgxColumnMaxValidatorDirective extends MaxValidator { - constructor(private column: IgxColumnComponent) { - super(); - column.validators.push(this); - } -} +export class IgxColumnMaxValidatorDirective extends MaxValidator { } @Directive({ @@ -73,16 +44,11 @@ export class IgxColumnMaxValidatorDirective extends MaxValidator { selector: 'igx-column[email]', providers: [{ provide: NG_VALIDATORS, - useExisting: forwardRef(() => EmailValidator), + useExisting: IgxColumnEmailValidatorDirective, multi: true }] }) -export class IgxColumnEmailValidatorDirective extends EmailValidator { - constructor(private column: IgxColumnComponent) { - super(); - column.validators.push(this); - } -} +export class IgxColumnEmailValidatorDirective extends EmailValidator { } @Directive({ @@ -90,31 +56,22 @@ export class IgxColumnEmailValidatorDirective extends EmailValidator { selector: 'igx-column[minlength]', providers: [{ provide: NG_VALIDATORS, - useExisting: forwardRef(() => MinLengthValidator), + useExisting: IgxColumnMinLengthValidatorDirective, multi: true }] }) -export class IgxColumnMinLengthValidatorDirective extends MinLengthValidator { - constructor(private column: IgxColumnComponent) { - super(); - column.validators.push(this); - } -} +export class IgxColumnMinLengthValidatorDirective extends MinLengthValidator { } @Directive({ // eslint-disable-next-line @angular-eslint/directive-selector selector: 'igx-column[maxlength]', providers: [{ provide: NG_VALIDATORS, - useExisting: forwardRef(() => MaxLengthValidator), + useExisting: IgxColumMaxLengthValidatorDirective, multi: true }] }) export class IgxColumMaxLengthValidatorDirective extends MaxLengthValidator { - constructor(private column: IgxColumnComponent) { - super(); - column.validators.push(this); - } } @Directive({ @@ -122,13 +79,9 @@ export class IgxColumMaxLengthValidatorDirective extends MaxLengthValidator { selector: 'igx-column[pattern]', providers: [{ provide: NG_VALIDATORS, - useExisting: forwardRef(() => PatternValidator), + useExisting: IgxColumPatternValidatorDirective, multi: true }] }) export class IgxColumPatternValidatorDirective extends PatternValidator { - constructor(private column: IgxColumnComponent) { - super(); - column.validators.push(this); - } } \ No newline at end of file diff --git a/src/app/grid-cellEditing/grid-cellEditing.component.ts b/src/app/grid-cellEditing/grid-cellEditing.component.ts index d99fcae083c..80dd9e5f565 100644 --- a/src/app/grid-cellEditing/grid-cellEditing.component.ts +++ b/src/app/grid-cellEditing/grid-cellEditing.component.ts @@ -2,7 +2,7 @@ import { Component, Directive, Input, ViewChild } from '@angular/core'; import { data, dataWithoutPK } from '../shared/data'; import { - IgxGridComponent, GridSelectionMode, IgxDateSummaryOperand, IgxSummaryResult, DisplayDensity, IgxColumnComponent, IgxColumnValidator + IgxGridComponent, GridSelectionMode, IgxDateSummaryOperand, IgxSummaryResult, DisplayDensity, IgxColumnComponent } from 'igniteui-angular'; import { AbstractControl, FormGroup, NG_VALIDATORS, ValidationErrors, Validator, ValidatorFn, Validators } from '@angular/forms'; diff --git a/src/app/grid-validation/grid-validation.sample.component.ts b/src/app/grid-validation/grid-validation.sample.component.ts index 1ec4374d7f1..12150030663 100644 --- a/src/app/grid-validation/grid-validation.sample.component.ts +++ b/src/app/grid-validation/grid-validation.sample.component.ts @@ -1,8 +1,8 @@ import { Component, Directive, ViewChild, Input } from '@angular/core'; import { data } from '../shared/data'; -import { IgxColumnValidator, IgxGridComponent, IgxTransactionService, IValidationStatus } from 'igniteui-angular'; -import { AbstractControl, FormGroup, NG_VALIDATORS, ValidationErrors, ValidatorFn } from '@angular/forms'; +import { IgxGridComponent, IgxTransactionService, IValidationStatus } from 'igniteui-angular'; +import { AbstractControl, FormGroup, NG_VALIDATORS, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { return (control: AbstractControl): ValidationErrors | null => { @@ -15,7 +15,7 @@ export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { selector: '[appForbiddenName]', providers: [{provide: NG_VALIDATORS, useExisting: ForbiddenValidatorDirective, multi: true}] }) - export class ForbiddenValidatorDirective extends IgxColumnValidator { + export class ForbiddenValidatorDirective extends Validators { @Input('appForbiddenName') public forbiddenName = ''; From 656a7ddc169a5eee00c48ca784a8f98b8f8e208d Mon Sep 17 00:00:00 2001 From: MKirova Date: Mon, 22 Aug 2022 18:43:11 +0300 Subject: [PATCH 079/149] chore(*): Fix test error. --- .../src/lib/grids/grid/grid-validation.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index 09c30d35943..4520fd4365c 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -85,7 +85,7 @@ export class IgxGridValidationService { public update(rowId: any, rowData: any) { const rowGroup = this.getFormGroup(rowId); for (const col of this.grid.columns) { - const control = rowGroup.get(col.field); + const control = rowGroup?.get(col.field); if (control) { control.setValue(rowData[col.field]); } From 4ea770fc2f7141263bdbace249d6bf71b4c22ebc Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 23 Aug 2022 10:53:30 +0300 Subject: [PATCH 080/149] chore(*): Recreate form groups when entering edit mode for cell/row. Update value in case editing exits with commit: false. --- .../src/lib/grids/common/crud.service.ts | 4 ++++ .../lib/grids/grid/grid-validation.service.ts | 19 ++++++++----------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts index adeadef4fd9..20100bd1981 100644 --- a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts +++ b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts @@ -636,6 +636,10 @@ export class IgxGridCRUDService extends IgxRowAddCrudState { return args.cancel; } } else { + const id = this.row ? this.row.id : this.cell.id.rowID; + const value = this.grid.transactions.getAggregatedValue(id, true); + const originalData = this.row ? this.row.data : this.cell.rowData; + this.grid.validation.update(id, value ?? originalData); this.exitCellEdit(event); } diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index 4520fd4365c..03afed4055b 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -12,18 +12,15 @@ export class IgxGridValidationService { * @internal */ public create(rowId, data) { - let formGroup = this._validityStates.get(rowId); - if (!formGroup) { - formGroup = new FormGroup({}); - for (const col of this.grid.columns) { - const field = col.field; - const control = new FormControl(data ? data[field] : undefined, { updateOn: this.grid.validationTrigger }); - control.addValidators(col.validators); - formGroup.addControl(field, control); - } - this.grid.formGroupCreated.emit(formGroup); - this.add(rowId, formGroup); + let formGroup = new FormGroup({}); + for (const col of this.grid.columns) { + const field = col.field; + const control = new FormControl(data ? data[field] : undefined, { updateOn: this.grid.validationTrigger }); + control.addValidators(col.validators); + formGroup.addControl(field, control); } + this.grid.formGroupCreated.emit(formGroup); + this.add(rowId, formGroup); return formGroup; } From e53e080be4e9968dfbe308de8772c2af880be3cb Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 23 Aug 2022 11:11:22 +0300 Subject: [PATCH 081/149] chore(*): Apply some of the review comments. --- projects/igniteui-angular/src/lib/grids/api.service.ts | 3 +-- projects/igniteui-angular/src/lib/grids/cell.component.ts | 2 +- .../igniteui-angular/src/lib/grids/grid-base.directive.ts | 8 ++++---- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/api.service.ts b/projects/igniteui-angular/src/lib/grids/api.service.ts index 273e88672fe..1e15d5b467a 100644 --- a/projects/igniteui-angular/src/lib/grids/api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/api.service.ts @@ -555,9 +555,8 @@ export class GridBaseAPIService implements GridServiceType { } else { mergeObjects(rowValueInDataSource, rowNewValue); } - const formGroup = this.grid.validation.getFormGroup(rowID); const validation = grid.validation as IgxGridValidationService; - validation.add(rowID, formGroup) + validation.update(rowID, rowNewValue); } diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 9c1a32a9041..0320645455e 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -108,7 +108,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT public get formGroup(): FormGroup { - return this.grid.validation.getFormGroup(this.intRow.key) || new FormGroup({}); + return this.grid.validation.getFormGroup(this.intRow.key); } /** diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index c71bc32521b..44281fae17c 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -6199,16 +6199,16 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements } } } - this.selectionService.clearHeaderCBState(); - this.summaryService.clearSummaryCache(); - this.pipeTrigger++; - this.notifyChanges(); if (event.origin === TransactionEventOrigin.REDO || event.origin === TransactionEventOrigin.UNDO) { event.actions.forEach(x => { const value = this.transactions.getAggregatedValue(x.transaction.id, true); this.validation.update(x.transaction.id, value ?? x.recordRef); }); } + this.selectionService.clearHeaderCBState(); + this.summaryService.clearSummaryCache(); + this.pipeTrigger++; + this.notifyChanges(); }; protected writeToData(rowIndex: number, value: any) { From 0610befcab67fc6ab4872112eae2aa02d01b51c5 Mon Sep 17 00:00:00 2001 From: Martin Dragnev Date: Tue, 23 Aug 2022 15:49:23 +0300 Subject: [PATCH 082/149] test(validator): Add all tests from the validator spec --- .../src/lib/grids/cell.component.ts | 8 +- .../lib/grids/grid/grid-validation.spec.ts | 314 ++++++++++++++++++ .../src/lib/test-utils/grid-functions.spec.ts | 7 + .../grid-validation-samples.spec.ts | 76 +++++ 4 files changed, 401 insertions(+), 4 deletions(-) create mode 100644 projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts create mode 100644 projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index de6ad3cce91..920e07b39fd 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -518,8 +518,8 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT private get validity() { const state = this.grid.transactions.getAggregatedValidationState(this.intRow.key); - if (state && state.validity && state.validity.some(x => x.valid === false)) { - return state.validity.find(x => x.field === this.column.field); + if (state && state.validity) { + return state.validity.find(x => x.field === this.column.field); } } @@ -836,7 +836,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT this.grid.validationStatusChange.emit( { formGroup: this.formGroup, - value: this.editValue, + value: this.editValue || this.value, state: this.errorTooltip.length > 0 ? Validity.Invalid : Validity.Valid } ); @@ -947,7 +947,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT if (!cell) { cell = this.grid.crudService.createCell(this); } - cell.editValue = val; + this.formControl.setValue(val); this.grid.gridAPI.update_cell(cell); this.grid.crudService.endCellEdit(); this.cdr.markForCheck(); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts new file mode 100644 index 00000000000..af353a9528e --- /dev/null +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts @@ -0,0 +1,314 @@ +import { fakeAsync, flush, TestBed, tick } from '@angular/core/testing'; +import { FormGroup, Validators } from '@angular/forms'; +import { By } from '@angular/platform-browser'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { IgxInputDirective, IgxTooltipTargetDirective } from 'igniteui-angular'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators' +import { configureTestSuite } from '../../test-utils/configure-suite'; +import { GridFunctions, GridSelectionFunctions } from '../../test-utils/grid-functions.spec'; +import { ForbiddenValidatorDirective, IgxGridValidationTestBaseComponent, IgxGridValidationTestCustomErrorComponent } from '../../test-utils/grid-validation-samples.spec'; +import { UIInteractions } from '../../test-utils/ui-interactions.spec'; +import { Validity } from '../common/grid.interface'; +import { IgxGridComponent } from './grid.component'; +import { IgxGridModule } from './grid.module'; + +describe('IgxGrid - Validation #grid', () => { + + configureTestSuite((() => { + TestBed.configureTestingModule({ + declarations: [ + IgxGridValidationTestBaseComponent, + IgxGridValidationTestCustomErrorComponent, + ForbiddenValidatorDirective + ], + imports: [IgxGridModule, NoopAnimationsModule] + }); + })); + + describe('Basic Validation - ', () => { + let fixture; + const $destroyer = new Subject(); + + beforeEach(() => { + fixture = TestBed.createComponent(IgxGridValidationTestBaseComponent); + fixture.detectChanges(); + }); + + afterEach(() => { + UIInteractions.clearOverlay(); + $destroyer.next(true); + }); + + it('should allow setting built-in validators via template-driven configuration on the column', () => { + const grid = fixture.componentInstance.grid as IgxGridComponent; + const firstColumn = grid.columnList.first; + const validators = firstColumn.validators; + expect(validators.length).toBeGreaterThanOrEqual(3); + + const minValidator = validators.find(validator => validator['inputName'] === 'minlength'); + const maxValidator = validators.find(validator => validator['inputName'] === 'maxlength'); + const requiredValidator = validators.find(validator => validator['inputName'] === 'required'); + + expect(parseInt(minValidator['minlength'], 10)).toBe(4); + expect(parseInt(maxValidator['maxlength'], 10)).toBe(8); + expect(requiredValidator).toBeDefined(); + + let cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + UIInteractions.simulateDoubleClickAndSelectEvent(cell.element); + cell.update('asd'); + fixture.detectChanges(); + + cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + //min length should be 4 + GridFunctions.verifyCellValid(cell, false); + + cell.editMode = true; + cell.update('test'); + fixture.detectChanges(); + cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + GridFunctions.verifyCellValid(cell, true); + }); + + it('should allow setting custom validators via template-driven configuration on the column', () => { + const grid = fixture.componentInstance.grid as IgxGridComponent; + const firstColumn = grid.columnList.first; + const validators = firstColumn.validators; + + + const customValidator = validators.find(validator => validator['forbiddenName']); + expect(customValidator).toBeDefined(); + expect(customValidator['forbiddenName']).toEqual('bob'); + let cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + UIInteractions.simulateDoubleClickAndSelectEvent(cell.element); + cell.update('bob'); + fixture.detectChanges(); + + cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + //the name should not contain bob + GridFunctions.verifyCellValid(cell, false); + + cell.editMode = true; + cell.update('valid'); + fixture.detectChanges(); + cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + GridFunctions.verifyCellValid(cell, true); + }); + + it('should allow setting validators on the exposed FormGroup object', () => { + const grid = fixture.componentInstance.grid as IgxGridComponent; + grid.onFormGroupCreate.pipe(takeUntil($destroyer)).subscribe((formGroup: FormGroup) => { + const prodName = formGroup.get('ProductName'); + prodName.addValidators(Validators.email); + }); + + + let cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + UIInteractions.simulateDoubleClickAndSelectEvent(cell.element); + cell.update('test'); + fixture.detectChanges(); + + cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + //the name should be correct email + GridFunctions.verifyCellValid(cell, false); + expect(cell.formControl.errors.email).toBeTrue(); + + cell.editMode = true; + cell.update('m@in.com'); + fixture.detectChanges(); + cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + GridFunctions.verifyCellValid(cell, true); + expect(cell.formControl.errors).toBeFalsy(); + }); + + it('should allow setting validation triggers - "change" , "blur".', async () => { + const grid = fixture.componentInstance.grid as IgxGridComponent; + //changing validation triger to blur + grid.validationTrigger = 'blur'; + fixture.detectChanges(); + + let cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + UIInteractions.simulateDoubleClickAndSelectEvent(cell.element); + fixture.detectChanges(); + + const input = fixture.debugElement.query(By.directive(IgxInputDirective)).nativeElement; + input.value = 'asd'; + input.dispatchEvent(new Event('input')); + fixture.detectChanges(); + + //the cell should be invalid after blur event is fired + cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + GridFunctions.verifyCellValid(cell, true); + + input.dispatchEvent(new Event('blur')); + fixture.detectChanges(); + // fix.detectChanges(); + + cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + + GridFunctions.verifyCellValid(cell, false); + }); + + it('should mark invalid cell with igx-grid__td--invalid class and show the related error cell template', () => { + const grid = fixture.componentInstance.grid as IgxGridComponent; + + let cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + UIInteractions.simulateDoubleClickAndSelectEvent(cell.element); + cell.update('asd'); + fixture.detectChanges(); + + cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + //min length should be 4 + GridFunctions.verifyCellValid(cell, false); + const erorrMessage = cell.errorTooltip.first.elementRef.nativeElement.children[0].textContent; + expect(erorrMessage).toEqual(' Entry should be at least 4 character(s) long '); + }); + + it('should show the error message on error icon hover and when the invalid cell becomes active.', fakeAsync(() => { + const grid = fixture.componentInstance.grid as IgxGridComponent; + + let cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + UIInteractions.simulateDoubleClickAndSelectEvent(cell.element); + const input = fixture.debugElement.query(By.directive(IgxInputDirective)).nativeElement; + input.value = 'asd'; + input.dispatchEvent(new Event('input')); + fixture.detectChanges(); + + cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + //min length should be 4 + GridFunctions.verifyCellValid(cell, false); + GridSelectionFunctions.verifyCellActive(cell, true); + const erorrMessage = cell.errorTooltip.first.elementRef.nativeElement.children[0].textContent; + expect(erorrMessage).toEqual(' Entry should be at least 4 character(s) long '); + + cell.focusout(); + tick(); + fixture.detectChanges(); + expect(cell.errorTooltip.first.collapsed).toBeTrue(); + + const element = fixture.debugElement.query(By.directive(IgxTooltipTargetDirective)).nativeElement; + element.dispatchEvent(new MouseEvent('mouseenter')); + flush(); + fixture.detectChanges(); + expect(cell.errorTooltip.first.collapsed).toBeFalse(); + })); + + it('should allow preventing edit mode for cell/row to end by canceling the related event if isValid event argument is false', () => { + const grid = fixture.componentInstance.grid as IgxGridComponent; + grid.cellEdit.pipe(takeUntil($destroyer)).subscribe((args) => { + if (!args.isValid) { + args.cancel = true; + } + }); + + let cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + UIInteractions.simulateDoubleClickAndSelectEvent(cell.element); + + const lastValue = cell.value; + cell.formControl.setValue('asd'); + fixture.detectChanges(); + grid.gridAPI.crudService.endEdit(true); + fixture.detectChanges(); + + cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + // should have been canceled and left in editmode because of non valid value + // should not have updated the value + GridFunctions.verifyCellValid(cell, false); + expect(!!grid.gridAPI.crudService.rowInEditMode).toEqual(true); + expect(grid.gridAPI.crudService.cellInEditMode).toEqual(true); + expect(cell.value).toEqual(lastValue); + + cell.update('test'); + fixture.detectChanges(); + cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + GridFunctions.verifyCellValid(cell, true); + }); + + it('should trigger the onValidationStatusChange event on grid when validation status changes', () => { + const grid = fixture.componentInstance.grid as IgxGridComponent; + spyOn(grid.validationStatusChange, "emit").and.callThrough(); + + let cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + UIInteractions.simulateDoubleClickAndSelectEvent(cell.element); + cell.update('asd'); + fixture.detectChanges(); + cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + + GridFunctions.verifyCellValid(cell, false); + expect(grid.validationStatusChange.emit).toHaveBeenCalledWith({ + formGroup: cell.formGroup, + state: Validity.Invalid, + value: 'asd' + }); + + cell.editMode = true; + cell.update('test'); + fixture.detectChanges(); + cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + + GridFunctions.verifyCellValid(cell, true); + expect(grid.validationStatusChange.emit).toHaveBeenCalledWith({ + formGroup: cell.formGroup, + state: Validity.Valid, + value: 'test' + }); + }); + + xit('should return invalid transaction using the transaction`s getInvalidTransactionLog method', () => { + const grid = fixture.componentInstance.grid as IgxGridComponent; + + + let cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + UIInteractions.simulateDoubleClickAndSelectEvent(cell.element); + cell.update('asd'); + fixture.detectChanges(); + cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + let transactionLog = grid.transactions.getInvalidTransactionLog(); + + GridFunctions.verifyCellValid(cell, false); + expect(transactionLog[0].newValue).toEqual({ ProductName: 'asd' }); + expect(transactionLog[0].validity[0].valid).toBeFalse(); + + + cell.editMode = true; + cell.update('test'); + fixture.detectChanges(); + cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + + GridFunctions.verifyCellValid(cell, true); + transactionLog = grid.transactions.getInvalidTransactionLog(); + expect(transactionLog[1].newValue).toEqual({ ProductName: 'test' }); + expect(transactionLog[1].validity[0].valid).toBeTrue(); + }); + }); + + describe('Custom Validation - ', () => { + let fixture; + + beforeEach(fakeAsync(() => { + fixture = TestBed.createComponent(IgxGridValidationTestCustomErrorComponent); + fixture.detectChanges(); + })); + + it('should allow setting built-in validators via template-driven configuration on the column', () => { + const grid = fixture.componentInstance.grid as IgxGridComponent; + let cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + + UIInteractions.simulateDoubleClickAndSelectEvent(cell.element); + cell.update('bob'); + fixture.detectChanges(); + + cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + //bob cannot be the name + GridFunctions.verifyCellValid(cell, false); + const erorrMessage = cell.errorTooltip.first.elementRef.nativeElement.children[0].textContent; + expect(erorrMessage).toEqual(' This name is forbidden. '); + + cell.editMode = true; + cell.update('test'); + fixture.detectChanges(); + cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + GridFunctions.verifyCellValid(cell, true); + }); + }); +}); \ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts b/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts index 986b29b74e4..2e3b447a48f 100644 --- a/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts @@ -23,6 +23,7 @@ import { IgxRowDirective } from '../grids/row.directive'; import { CellType, GridType, RowType } from '../grids/common/grid.interface'; import { IgxTreeNodeComponent } from '../tree/tree-node/tree-node.component'; import { IgxColumnComponent } from '../grids/columns/column.component'; +import { IgxGridCell } from 'igniteui-angular'; const SUMMARY_LABEL_CLASS = '.igx-grid-summary__label'; @@ -52,6 +53,7 @@ const ACTIVE_GROUP_ROW_CLASS = 'igx-grid__group-row--active'; const ACTIVE_HEADER_CLASS = 'igx-grid-th--active'; const GROUP_ROW_CLASS = 'igx-grid-groupby-row'; const CELL_SELECTED_CSS_CLASS = 'igx-grid__td--selected'; +const CELL_INVALID_CSS_CLASS = 'igx-grid__td--invalid'; const CELL_ACTIVE_CSS_CLASS = 'igx-grid__td--active'; const ROW_DIV_SELECTION_CHECKBOX_CSS_CLASS = 'igx-grid__cbx-selection'; const ROW_SELECTION_CSS_CLASS = 'igx-grid__tr--selected'; @@ -2077,6 +2079,11 @@ export class GridFunctions { public static getResizer(fix): DebugElement { return fix.debugElement.query(By.css(RESIZE_LINE_CLASS)); } + + public static verifyCellValid(cell: IgxGridCellComponent | CellType, valid = true) { + expect((cell as IgxGridCell).formControl.valid).toEqual(valid); + expect(cell.nativeElement.classList.contains(CELL_INVALID_CSS_CLASS)).not.toEqual(valid); + } } export class GridSummaryFunctions { public static getRootSummaryRow(fix): DebugElement { diff --git a/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts b/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts new file mode 100644 index 00000000000..e6f2fd6fdce --- /dev/null +++ b/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts @@ -0,0 +1,76 @@ +import { Component, Input, ViewChild, Directive } from '@angular/core'; +import { AbstractControl, NG_VALIDATORS, ValidationErrors, ValidatorFn } from '@angular/forms'; +import { IgxColumnValidator, IgxGridComponent } from 'igniteui-angular'; +import { data } from '../../../../../src/app/shared/data'; + +export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const forbidden = nameRe.test(control.value); + return forbidden ? { forbiddenName: { value: control.value } } : null; + }; +} + +@Directive({ + selector: '[appForbiddenName]', + providers: [{ provide: NG_VALIDATORS, useExisting: ForbiddenValidatorDirective, multi: true }] +}) +export class ForbiddenValidatorDirective extends IgxColumnValidator { + @Input('appForbiddenName') + public forbiddenName = ''; + + public validate(control: AbstractControl): ValidationErrors | null { + return this.forbiddenName ? forbiddenNameValidator(new RegExp(this.forbiddenName, 'i'))(control) + : null; + } +} + +@Component({ + template: ` + + + + + ` +}) +export class IgxGridValidationTestBaseComponent { + public rowEditable = true; + public columns = [ + { field: 'ProductID' }, + { field: 'ProductName' }, + { field: 'UnitPrice' }, + { field: 'UnitsInStock' } + ]; + public data = data;; + + @ViewChild('grid', { read: IgxGridComponent, static: true }) public grid: IgxGridComponent; +} + +@Component({ + template: ` + + + +
+ This name is forbidden. +
+
+
+
+ ` +}) +export class IgxGridValidationTestCustomErrorComponent { + public rowEditable = true; + public columns = [ + { field: 'ProductID' }, + { field: 'ProductName' }, + { field: 'UnitPrice' }, + { field: 'UnitsInStock' } + ]; + public data = data;; + + @ViewChild('grid', { read: IgxGridComponent, static: true }) public grid: IgxGridComponent; +} \ No newline at end of file From 106451a4cb39869af7bc661b9de0431d82fd4603 Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 23 Aug 2022 15:52:57 +0300 Subject: [PATCH 083/149] chore(*): Apply discussion comments. --- .../src/lib/grids/cell.component.ts | 7 --- .../src/lib/grids/common/grid.interface.ts | 8 +--- .../src/lib/grids/grid-base.directive.ts | 5 +- .../lib/grids/grid/grid-validation.service.ts | 46 ++++++++++++++----- .../src/lib/grids/public_api.ts | 2 +- .../grid-validation.sample.component.ts | 6 +-- 6 files changed, 42 insertions(+), 32 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 0320645455e..fc9a377117d 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -815,13 +815,6 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT this.cdr.detectChanges(); this.openErrorTooltip(); } - this.grid.validationStatusChange.emit( - { - formGroup: this.formGroup, - value: this.editValue, - state: this.errorTooltip.length > 0 ? Validity.Invalid : Validity.Valid - } - ); }); } diff --git a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts index 69651231a8b..122a3d45e06 100644 --- a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts @@ -209,12 +209,6 @@ export interface IFieldValid { formGroup: FormGroup; } -export interface IValidationStatus { - value: any; - formGroup: FormGroup; - state: Validity -} - export enum Validity { Valid, Invalid @@ -519,7 +513,7 @@ export interface GridType extends IGridDataBindable { rowDragEnd: EventEmitter; rowToggle: EventEmitter; formGroupCreated: EventEmitter; - validationStatusChange: EventEmitter; + validationStatusChange: EventEmitter; toolbarExporting: EventEmitter; rendered$: Observable; diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index 44281fae17c..e904add1417 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -124,7 +124,7 @@ import { IPinColumnCancellableEventArgs } from './common/events'; import { IgxAdvancedFilteringDialogComponent } from './filtering/advanced-filtering/advanced-filtering-dialog.component'; -import { ColumnType, GridServiceType, GridType, IGX_GRID_SERVICE_BASE, ISizeInfo, IValidationStatus, RowType } from './common/grid.interface'; +import { ColumnType, GridServiceType, GridType, IGX_GRID_SERVICE_BASE, ISizeInfo, RowType, Validity } from './common/grid.interface'; import { DropPosition } from './moving/moving.service'; import { IgxHeadSelectorDirective, IgxRowSelectorDirective } from './selection/row-selectors'; import { IgxColumnComponent } from './columns/column.component'; @@ -508,7 +508,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements public formGroupCreated = new EventEmitter(); @Output() - public validationStatusChange = new EventEmitter(); + public validationStatusChange = new EventEmitter(); /** * Emitted when a cell is selected. @@ -5935,6 +5935,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements */ public resizeAndRepositionOverlayById(overlayId: string, newSize: number) { const overlay = this.overlayService.getOverlayById(overlayId); + if(!overlay) return; overlay.initialSize.width = newSize; overlay.elementRef.nativeElement.parentElement.style.width = newSize + 'px'; this.overlayService.reposition(overlayId); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index 03afed4055b..f4b3c5c5e53 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -1,26 +1,42 @@ import { Injectable } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; -import { GridType, IFieldValidationState, IRecordValidationState } from '../common/grid.interface'; +import { GridType, IFieldValidationState, IRecordValidationState, Validity } from '../common/grid.interface'; @Injectable() export class IgxGridValidationService { + /** + * @hidden + * @internal + */ public grid: GridType; private _validityStates = new Map(); + private _valid = true; + + public get valid() { + return this._valid; + } + + public set valid(value: boolean) { + this._valid = value; + } /** * @hidden * @internal */ public create(rowId, data) { - let formGroup = new FormGroup({}); - for (const col of this.grid.columns) { - const field = col.field; - const control = new FormControl(data ? data[field] : undefined, { updateOn: this.grid.validationTrigger }); - control.addValidators(col.validators); - formGroup.addControl(field, control); + let formGroup = this.getFormGroup(rowId); + if (!formGroup) { + formGroup = new FormGroup({}); + for (const col of this.grid.columns) { + const field = col.field; + const control = new FormControl(data ? data[field] : undefined, { updateOn: this.grid.validationTrigger }); + control.addValidators(col.validators); + formGroup.addControl(field, control); + } + this.grid.formGroupCreated.emit(formGroup); + this.add(rowId, formGroup); } - this.grid.formGroupCreated.emit(formGroup); - this.add(rowId, formGroup); return formGroup; } @@ -80,13 +96,19 @@ export class IgxGridValidationService { * @internal */ public update(rowId: any, rowData: any) { + const currentValid = this.valid; + const keys = Object.keys(rowData); const rowGroup = this.getFormGroup(rowId); - for (const col of this.grid.columns) { - const control = rowGroup?.get(col.field); + for (const key of keys) { + const control = rowGroup?.get(key); if (control) { - control.setValue(rowData[col.field]); + control.setValue(rowData[key]); } } + this.valid = this.getInvalid().length === 0; + if (this.valid !== currentValid) { + this.grid.validationStatusChange.emit(this.valid ? Validity.Valid : Validity.Invalid); + } } /** Clears validity state by id or clears all states if no id is passed. diff --git a/projects/igniteui-angular/src/lib/grids/public_api.ts b/projects/igniteui-angular/src/lib/grids/public_api.ts index 7af4b272aa6..6c7b3239a00 100644 --- a/projects/igniteui-angular/src/lib/grids/public_api.ts +++ b/projects/igniteui-angular/src/lib/grids/public_api.ts @@ -13,7 +13,7 @@ export * from './grid-base.directive'; export * from './grid.common'; export * from './grid-public-row'; export * from './grid-public-cell'; -export { CellType, RowType, IGX_GRID_BASE, IValidationStatus } from './common/grid.interface'; +export { CellType, RowType, IGX_GRID_BASE, Validity } from './common/grid.interface'; export * from './summaries/grid-summary'; export * from './grid-common.module'; export * from './grid.rowEdit.directive'; diff --git a/src/app/grid-validation/grid-validation.sample.component.ts b/src/app/grid-validation/grid-validation.sample.component.ts index 12150030663..33040124558 100644 --- a/src/app/grid-validation/grid-validation.sample.component.ts +++ b/src/app/grid-validation/grid-validation.sample.component.ts @@ -1,7 +1,7 @@ import { Component, Directive, ViewChild, Input } from '@angular/core'; import { data } from '../shared/data'; -import { IgxGridComponent, IgxTransactionService, IValidationStatus } from 'igniteui-angular'; +import { IgxGridComponent, IgxTransactionService, Validity } from 'igniteui-angular'; import { AbstractControl, FormGroup, NG_VALIDATORS, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { @@ -85,8 +85,8 @@ export class GridValidationSampleComponent { // prodName.addValidators(forbiddenNameValidator(/bob/i)); } - public validationChange(evtArgs: IValidationStatus){ - //console.log(evtArgs); + public validationChange(evtArgs: Validity){ + alert(evtArgs === Validity.Invalid ? 'state became INVALID' : 'state became VALID'); } } From dd4a5d0b5adc7b90e8f807e5b64a551f2385137d Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 23 Aug 2022 16:00:10 +0300 Subject: [PATCH 084/149] chore(*): Changing event arg to be 'valid' instead of 'invalid'. --- .../src/lib/grids/common/crud.service.ts | 6 ++--- .../src/lib/grids/common/events.ts | 2 +- .../lib/grids/grid/grid-cell-editing.spec.ts | 24 ++++++++--------- .../lib/grids/grid/grid-row-editing.spec.ts | 26 +++++++++---------- .../lib/grids/grid/grid.nested.props.spec.ts | 8 +++--- 5 files changed, 33 insertions(+), 33 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts index 20100bd1981..e69821e272d 100644 --- a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts +++ b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts @@ -24,7 +24,7 @@ export class IgxEditRow { cancel: false, owner: this.grid, isAddRow: false, - invalid: this.rowFormGroup.invalid, + valid: this.rowFormGroup.valid, event }; if (includeNewValue) { @@ -120,7 +120,7 @@ export class IgxCell { cancel: false, column: this.column, owner: this.grid, - invalid: formControl ? formControl.invalid : false, + valid: formControl ? formControl.valid : true, event }; if (includeNewValue) { @@ -141,7 +141,7 @@ export class IgxCell { // the only case we use this.rowData directly, is when there is no rowEditing or transactions enabled rowData, oldValue: this.value, - invalid: formControl ? formControl.invalid : false, + valid: formControl ? formControl.valid : true, newValue: value, column: this.column, owner: this.grid, diff --git a/projects/igniteui-angular/src/lib/grids/common/events.ts b/projects/igniteui-angular/src/lib/grids/common/events.ts index a30e473d738..3f1c4a71356 100644 --- a/projects/igniteui-angular/src/lib/grids/common/events.ts +++ b/projects/igniteui-angular/src/lib/grids/common/events.ts @@ -31,7 +31,7 @@ export interface IGridEditDoneEventArgs extends IBaseEventArgs { column?: ColumnType; owner?: GridType; isAddRow?: boolean; - invalid?: boolean; + valid?: boolean; } export interface IGridEditEventArgs extends CancelableEventArgs, IGridEditDoneEventArgs { diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-cell-editing.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-cell-editing.spec.ts index cc656afb080..a89e6ee4bc3 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-cell-editing.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-cell-editing.spec.ts @@ -578,7 +578,7 @@ describe('IgxGrid - Cell Editing #grid', () => { rowData: initialRowData, oldValue: 'John Brown', cancel: false, - invalid: false, + valid: true, column: cell.column, owner: grid, event: jasmine.anything() as any @@ -599,7 +599,7 @@ describe('IgxGrid - Cell Editing #grid', () => { rowID: cell2.row.key, rowData: initialRowData, oldValue: 20, - invalid: false, + valid: true, cancel: false, column: cell2.column, owner: grid, @@ -628,7 +628,7 @@ describe('IgxGrid - Cell Editing #grid', () => { rowData: initialRowData, oldValue: 'John Brown', cancel: true, - invalid: false, + valid: true, column: cell.column, owner: grid, event: jasmine.anything() as any @@ -655,7 +655,7 @@ describe('IgxGrid - Cell Editing #grid', () => { column: cell.column, owner: grid, event: jasmine.anything() as any, - invalid: false + valid: true }; expect(grid.cellEditEnter.emit).toHaveBeenCalledTimes(2); expect(grid.cellEditEnter.emit).toHaveBeenCalledWith(cellArgs); @@ -680,7 +680,7 @@ describe('IgxGrid - Cell Editing #grid', () => { cellID: cell.cellID, rowData: initialRowData, newValue: 'John Brown', - invalid: false, + valid: true, oldValue: 'John Brown', column: cell.column, owner: grid, @@ -703,7 +703,7 @@ describe('IgxGrid - Cell Editing #grid', () => { rowData: initialRowData, newValue: 20, oldValue: 20, - invalid: false, + valid: true, column: cell.column, owner: grid, event: jasmine.anything() as any @@ -740,7 +740,7 @@ describe('IgxGrid - Cell Editing #grid', () => { column: cell.column, owner: grid, event: jasmine.anything() as any, - invalid: false + valid: true }; expect(grid.cellEdit.emit).toHaveBeenCalledTimes(1); expect(grid.cellEdit.emit).toHaveBeenCalledWith(cellArgs); @@ -766,7 +766,7 @@ describe('IgxGrid - Cell Editing #grid', () => { column: cell.column, owner: grid, event: jasmine.anything() as any, - invalid: false + valid: true }; expect(grid.cellEdit.emit).toHaveBeenCalledTimes(2); expect(grid.cellEdit.emit).toHaveBeenCalledWith(cellArgs); @@ -804,7 +804,7 @@ describe('IgxGrid - Cell Editing #grid', () => { cancel: true, column: cell.column, owner: grid, - invalid: false, + valid: true, event: undefined }; expect(grid.cellEdit.emit).toHaveBeenCalledTimes(1); @@ -977,7 +977,7 @@ describe('IgxGrid - Cell Editing #grid', () => { column: cell.column, owner: grid, event: jasmine.anything() as any, - invalid: false + valid: true }; expect(grid.cellEditExit.emit).toHaveBeenCalledTimes(1); expect(grid.cellEditExit.emit).toHaveBeenCalledWith(cellArgs); @@ -1015,7 +1015,7 @@ describe('IgxGrid - Cell Editing #grid', () => { column: cell.column, owner: grid, event: jasmine.anything() as any, - invalid: false + valid: true }; expect(grid.cellEditDone.emit).toHaveBeenCalledTimes(1); expect(grid.cellEditDone.emit).toHaveBeenCalledWith(cellArgs); @@ -1040,7 +1040,7 @@ describe('IgxGrid - Cell Editing #grid', () => { column: cell.column, owner: grid, event: jasmine.anything() as any, - invalid: false + valid: true }; expect(grid.cellEditDone.emit).toHaveBeenCalledTimes(2); expect(grid.cellEditDone.emit).toHaveBeenCalledWith(cellArgs); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts index 1204eb9fe0f..eff62dcca34 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts @@ -153,7 +153,7 @@ describe('IgxGrid - Row Editing #grid', () => { cancel: false, column: cell.column, owner: grid, - invalid: false, + valid: true, event: jasmine.anything() as any }; let rowEditArgs: IGridEditEventArgs = { @@ -161,7 +161,7 @@ describe('IgxGrid - Row Editing #grid', () => { rowData: initialRowData, oldValue: row.data, cancel: false, - invalid: false, + valid: true, owner: grid, isAddRow: row.addRowUI, event: jasmine.anything() as any @@ -179,7 +179,7 @@ describe('IgxGrid - Row Editing #grid', () => { rowID: cell.row.key, rowData: cell.row.data, oldValue: cell.value, - invalid: false, + valid: true, newValue: cell.value, column: cell.column, owner: grid, @@ -213,7 +213,7 @@ describe('IgxGrid - Row Editing #grid', () => { rowData: Object.assign({}, row.data, { ProductName: newCellValue }), oldValue: cell.value, newValue: newCellValue, - invalid: false, + valid: true, column: cell.column, owner: grid, event: jasmine.anything() as any @@ -230,7 +230,7 @@ describe('IgxGrid - Row Editing #grid', () => { cancel: false, owner: grid, isAddRow: row.addRowUI, - invalid: false, + valid: true, event: jasmine.anything() as any }; @@ -240,7 +240,7 @@ describe('IgxGrid - Row Editing #grid', () => { rowData: updatedRowData, // with rowEditable - IgxGridRowEditingComponent oldValue: cell.value, newValue: newCellValue, - invalid: false, + valid: true, column: cell.column, owner: grid, event: jasmine.anything() as any @@ -1723,7 +1723,7 @@ describe('IgxGrid - Row Editing #grid', () => { cancel: false, owner: grid, isAddRow: false, - invalid: false, + valid: true, event: jasmine.anything() as any }); }); @@ -1764,7 +1764,7 @@ describe('IgxGrid - Row Editing #grid', () => { owner: grid, isAddRow: false, event: jasmine.anything() as any, - invalid: false + valid: true }); // Enter cell edit mode again @@ -1789,7 +1789,7 @@ describe('IgxGrid - Row Editing #grid', () => { owner: grid, isAddRow: false, event: jasmine.anything() as any, - invalid: false + valid: true }); }); @@ -1845,7 +1845,7 @@ describe('IgxGrid - Row Editing #grid', () => { owner: grid, isAddRow: false, event: jasmine.anything() as any, - invalid: false + valid: true }); }); @@ -1876,7 +1876,7 @@ describe('IgxGrid - Row Editing #grid', () => { owner: grid, isAddRow: false, event: jasmine.anything() as any, - invalid: false + valid: true }); }); @@ -1957,7 +1957,7 @@ describe('IgxGrid - Row Editing #grid', () => { column: cell.column, owner: grid, event: jasmine.anything() as any, - invalid: false + valid: true }; UIInteractions.simulateDoubleClickAndSelectEvent(cellElem); @@ -2161,7 +2161,7 @@ describe('IgxGrid - Row Editing #grid', () => { column: cell.column, owner: grid, event: jasmine.anything() as any, - invalid: false + valid: true }; const rowDoneArgs: IGridEditDoneEventArgs = { diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts index 1e8911807f9..f85b3242eab 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts @@ -540,7 +540,7 @@ describe('Edit cell with data of type Array #grid', () => { column: cell.column, owner: grid, event: jasmine.anything() as any, - invalid: false + valid: true }; expect(grid.cellEditEnter.emit).toHaveBeenCalledTimes(1); @@ -603,7 +603,7 @@ describe('Edit cell with data of type Array #grid', () => { column: cell.column, owner: grid, event: jasmine.anything() as any, - invalid: false + valid: true }; expect(grid.cellEditEnter.emit).toHaveBeenCalledTimes(1); @@ -671,7 +671,7 @@ describe('Edit cell with data of type Array #grid', () => { isAddRow: row.addRowUI, cancel: false, event: jasmine.anything() as any, - invalid: false + valid: true }; expect(grid.rowEditEnter.emit).toHaveBeenCalledTimes(1); @@ -736,7 +736,7 @@ describe('Edit cell with data of type Array #grid', () => { isAddRow: row.addRowUI, cancel: false, event: jasmine.anything() as any, - invalid: false + valid: true }; expect(grid.rowEditEnter.emit).toHaveBeenCalledTimes(1); From 85b5843f8f016cbcb06255d48cbf70c547e7adf1 Mon Sep 17 00:00:00 2001 From: Martin Dragnev Date: Tue, 23 Aug 2022 16:08:15 +0300 Subject: [PATCH 085/149] chore(*): fix lint --- .../src/lib/test-utils/grid-validation-samples.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts b/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts index e6f2fd6fdce..edbbb180e67 100644 --- a/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts @@ -11,7 +11,7 @@ export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { } @Directive({ - selector: '[appForbiddenName]', + selector: '[igxAppForbiddenName]', providers: [{ provide: NG_VALIDATORS, useExisting: ForbiddenValidatorDirective, multi: true }] }) export class ForbiddenValidatorDirective extends IgxColumnValidator { @@ -28,7 +28,7 @@ export class ForbiddenValidatorDirective extends IgxColumnValidator { template: ` - @@ -51,7 +51,7 @@ export class IgxGridValidationTestBaseComponent { template: ` -
From fcc1ecb1c4ed8a572a4d16f58c7e2a0ea4912650 Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 23 Aug 2022 16:11:53 +0300 Subject: [PATCH 086/149] chore(*): Update tests and sample. --- .../src/lib/grids/grid/grid.nested.props.spec.ts | 4 ++-- src/app/grid-cellEditing/grid-cellEditing.component.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts index f85b3242eab..6d12d975ad3 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts @@ -701,7 +701,7 @@ describe('Edit cell with data of type Array #grid', () => { expect(rowArgs.newValue.locations.length).toEqual(3); expect(rowArgs.oldValue.locations.length).toEqual(3); - delete rowArgs.invalid; + delete rowArgs.valid; expect(grid.rowEditExit.emit).toHaveBeenCalledTimes(1); expect(grid.rowEditExit.emit).toHaveBeenCalledWith(rowArgs); @@ -770,7 +770,7 @@ describe('Edit cell with data of type Array #grid', () => { delete rowArgs.cancel; rowArgs.rowData = initialRowData; - delete rowArgs.invalid; + delete rowArgs.valid; expect(grid.rowEditDone.emit).toHaveBeenCalledTimes(1); expect(grid.rowEditDone.emit).toHaveBeenCalledWith(rowArgs); diff --git a/src/app/grid-cellEditing/grid-cellEditing.component.ts b/src/app/grid-cellEditing/grid-cellEditing.component.ts index 80dd9e5f565..e02149574c6 100644 --- a/src/app/grid-cellEditing/grid-cellEditing.component.ts +++ b/src/app/grid-cellEditing/grid-cellEditing.component.ts @@ -56,13 +56,13 @@ export class GridCellEditingComponent { public cellEdit(evt) { - if (evt.invalid) { + if (!evt.valid) { evt.cancel = true; } } public rowEdit(evt) { - if (evt.invalid) { + if (!evt.valid) { evt.cancel = true; } } From a461aa1a1b864d5fe258a3ba70d41119c7cd41d5 Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 23 Aug 2022 16:36:49 +0300 Subject: [PATCH 087/149] chore(*): Update more tests. Add check. --- .../src/lib/grids/grid/grid-row-editing.spec.ts | 6 ++---- .../src/lib/grids/grid/grid-validation.service.ts | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts index eff62dcca34..1578e29abb2 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts @@ -2791,8 +2791,7 @@ describe('IgxGrid - Row Editing #grid', () => { expect(trans.add).toHaveBeenCalledWith({ id: 3, type: 'update', - newValue: { ProductName: 'Updated Cell' }, - validity: trans.getTransactionLog()[0].validity + newValue: { ProductName: 'Updated Cell' } }, grid.data[2]); expect(grid.data.length).toBe(10); }); @@ -2816,8 +2815,7 @@ describe('IgxGrid - Row Editing #grid', () => { expect(trans.add).toHaveBeenCalledWith({ id: 3, type: 'update', - newValue: updateRowData, - validity: trans.getTransactionLog()[0].validity + newValue: updateRowData }, oldRowData); expect(grid.data[2]).toBe(oldRowData); }); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index f4b3c5c5e53..70d4597903f 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -96,6 +96,7 @@ export class IgxGridValidationService { * @internal */ public update(rowId: any, rowData: any) { + if(!rowData) return; const currentValid = this.valid; const keys = Object.keys(rowData); const rowGroup = this.getFormGroup(rowId); From 6fa3d472bf14b814d2807639c0f2c94264a74438 Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 23 Aug 2022 17:08:57 +0300 Subject: [PATCH 088/149] chore(*): Don't emit events when externally updating the control. --- .../src/lib/grids/grid/grid-validation.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index 70d4597903f..b44f592ccde 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -103,7 +103,7 @@ export class IgxGridValidationService { for (const key of keys) { const control = rowGroup?.get(key); if (control) { - control.setValue(rowData[key]); + control.setValue(rowData[key], { emitEvent: false }); } } this.valid = this.getInvalid().length === 0; From a1d9e1ca08d1d595cf51bece374b92653f61bd12 Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 23 Aug 2022 17:53:29 +0300 Subject: [PATCH 089/149] chore(*): Add check in case something forces edit mode without going throgh the api. --- projects/igniteui-angular/src/lib/grids/cell.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index fc9a377117d..16868cad015 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -237,7 +237,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT * @memberof IgxGridCellComponent */ public get template(): TemplateRef { - if (this.editMode) { + if (this.editMode && this.formGroup) { const inlineEditorTemplate = this.column.inlineEditorTemplate; return inlineEditorTemplate ? inlineEditorTemplate : this.inlineEditorTemplate; } From 8db50af3e2d5ea8900fcc04796de60535065e64c Mon Sep 17 00:00:00 2001 From: Martin Dragnev Date: Wed, 24 Aug 2022 11:37:29 +0300 Subject: [PATCH 090/149] chore(*): Fix tests, update crudservice cell editValue when using public API, correct input selector of custom validator --- projects/igniteui-angular/src/lib/grids/cell.component.ts | 1 + .../src/lib/test-utils/grid-validation-samples.spec.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 920e07b39fd..aa13deac0a9 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -947,6 +947,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT if (!cell) { cell = this.grid.crudService.createCell(this); } + cell.editValue = val; this.formControl.setValue(val); this.grid.gridAPI.update_cell(cell); this.grid.crudService.endCellEdit(); diff --git a/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts b/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts index edbbb180e67..1e13dfe05b1 100644 --- a/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts @@ -15,7 +15,7 @@ export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { providers: [{ provide: NG_VALIDATORS, useExisting: ForbiddenValidatorDirective, multi: true }] }) export class ForbiddenValidatorDirective extends IgxColumnValidator { - @Input('appForbiddenName') + @Input('igxAppForbiddenName') public forbiddenName = ''; public validate(control: AbstractControl): ValidationErrors | null { From 73a2c09a9162675c122d8ae708538548228cc017 Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 24 Aug 2022 15:54:35 +0300 Subject: [PATCH 091/149] chore(*): Add more complex logic for undo/redo for scenarios like undo/redo the deletion of a previously added record. Also do not trigger update on data changes inside adding row. --- .../igniteui-angular/src/lib/grids/api.service.ts | 7 ++++++- .../src/lib/grids/common/crud.service.ts | 10 ++++++---- .../src/lib/grids/grid-base.directive.ts | 15 +++++++++++++-- .../src/lib/grids/grid/grid-validation.service.ts | 14 ++++++++++++-- .../grid-validation.sample.component.html | 6 +++--- 5 files changed, 40 insertions(+), 12 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/api.service.ts b/projects/igniteui-angular/src/lib/grids/api.service.ts index 1e15d5b467a..6ecf6085a2a 100644 --- a/projects/igniteui-angular/src/lib/grids/api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/api.service.ts @@ -4,7 +4,7 @@ import { cloneArray, reverseMapper, mergeObjects } from '../core/utils'; import { DataUtil, GridColumnDataType } from '../data-operations/data-util'; import { IFilteringExpressionsTree } from '../data-operations/filtering-expressions-tree'; import { Transaction, TransactionType, State } from '../services/transaction/transaction'; -import { IgxCell, IgxGridCRUDService, IgxEditRow } from './common/crud.service'; +import { IgxCell, IgxGridCRUDService, IgxEditRow, IgxAddRow } from './common/crud.service'; import { CellType, ColumnType, GridServiceType, GridType, IFieldValid, RowType } from './common/grid.interface'; import { IGridEditEventArgs, IRowToggleEventArgs } from './common/events'; import { IgxColumnMovingService } from './moving/moving.service'; @@ -300,6 +300,8 @@ export class GridBaseAPIService implements GridServiceType { } else { grid.data.push(rowData); } + const validation = grid.validation as IgxGridValidationService; + validation.update(transactionId, rowData); } public deleteRowFromData(rowID: any, index: number) { @@ -317,6 +319,7 @@ export class GridBaseAPIService implements GridServiceType { const state: State = grid.transactions.getState(rowID); grid.transactions.add({ id: rowID, type: TransactionType.DELETE, newValue: null }, state && state.recordRef); } + grid.validation.clear(rowID); } public deleteRowById(rowId: any): any { @@ -555,6 +558,8 @@ export class GridBaseAPIService implements GridServiceType { } else { mergeObjects(rowValueInDataSource, rowNewValue); } + const isAddRow = this.crudService.row instanceof IgxAddRow; + if (isAddRow) { return; } const validation = grid.validation as IgxGridValidationService; validation.update(rowID, rowNewValue); } diff --git a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts index e69821e272d..ee588a424d6 100644 --- a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts +++ b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts @@ -14,7 +14,7 @@ export class IgxEditRow { constructor(public id: any, public index: number, public data: any, public grid: GridType) { this.rowFormGroup = this.grid.validation.create(id, data); - } + } public createEditEventArgs(includeNewValue = true, event?: Event): IGridEditEventArgs { const args: IGridEditEventArgs = { @@ -99,8 +99,8 @@ export class IgxCell { public editValue: any, public rowData: any, public grid: GridType) { - this.grid.validation.create(id.rowID, rowData); - } + this.grid.validation.create(id.rowID, rowData); + } public castToNumber(value: any): any { if (this.column.dataType === 'number' && !this.column.inlineEditorTemplate) { @@ -639,7 +639,9 @@ export class IgxGridCRUDService extends IgxRowAddCrudState { const id = this.row ? this.row.id : this.cell.id.rowID; const value = this.grid.transactions.getAggregatedValue(id, true); const originalData = this.row ? this.row.data : this.cell.rowData; - this.grid.validation.update(id, value ?? originalData); + if (!(this.row instanceof IgxAddRow)) { + this.grid.validation.update(id, value ?? originalData); + } this.exitCellEdit(event); } diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index e904add1417..c4347660744 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -6202,8 +6202,19 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements } if (event.origin === TransactionEventOrigin.REDO || event.origin === TransactionEventOrigin.UNDO) { event.actions.forEach(x => { - const value = this.transactions.getAggregatedValue(x.transaction.id, true); - this.validation.update(x.transaction.id, value ?? x.recordRef); + if (x.transaction.type === TransactionType.UPDATE) { + const value = this.transactions.getAggregatedValue(x.transaction.id, true); + this.validation.update(x.transaction.id, value ?? x.recordRef); + } else if (x.transaction.type === TransactionType.DELETE || x.transaction.type === TransactionType.ADD) { + const value = this.transactions.getAggregatedValue(x.transaction.id, true); + if (value) { + this.validation.create(x.transaction.id, value ?? x.recordRef); + this.validation.update(x.transaction.id, value ?? x.recordRef); + } else { + this.validation.clear(x.transaction.id); + } + } + }); } this.selectionService.clearHeaderCBState(); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index b44f592ccde..d27c62ee8f7 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -96,8 +96,7 @@ export class IgxGridValidationService { * @internal */ public update(rowId: any, rowData: any) { - if(!rowData) return; - const currentValid = this.valid; + if (!rowData) return; const keys = Object.keys(rowData); const rowGroup = this.getFormGroup(rowId); for (const key of keys) { @@ -106,6 +105,16 @@ export class IgxGridValidationService { control.setValue(rowData[key], { emitEvent: false }); } } + + this.updateStatus(); + } + + /** + * @hidden + * @internal + */ + private updateStatus() { + const currentValid = this.valid; this.valid = this.getInvalid().length === 0; if (this.valid !== currentValid) { this.grid.validationStatusChange.emit(this.valid ? Validity.Valid : Validity.Invalid); @@ -122,6 +131,7 @@ export class IgxGridValidationService { } else { this._validityStates.clear(); } + this.updateStatus(); } } \ No newline at end of file diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index 57f73c17861..f764e49d1af 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -13,7 +13,7 @@

Grid without transactions

- + @@ -21,7 +21,7 @@

Grid without transactions

Grid with transactions

Row edit - @@ -35,7 +35,7 @@

Grid with transactions

- +
From 5b4fd65885768e4263455d31c8983f82c2530b05 Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 24 Aug 2022 18:06:31 +0300 Subject: [PATCH 092/149] chore(*): Refactor public row/cell classes. Hide getValidity. Add formControl to context. Reuse same context for errors template. --- .../src/lib/grids/cell.component.html | 2 +- .../src/lib/grids/cell.component.ts | 1 + .../src/lib/grids/common/grid.interface.ts | 2 ++ .../src/lib/grids/grid-public-cell.ts | 13 ++++++++---- .../src/lib/grids/grid-public-row.ts | 11 ++++++++++ .../lib/grids/grid/grid-validation.service.ts | 21 ++++++++++++++----- .../grid-validation.sample.component.html | 6 +++--- 7 files changed, 43 insertions(+), 13 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.html b/projects/igniteui-angular/src/lib/grids/cell.component.html index c2a24b2c581..9df5a1c8358 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/cell.component.html @@ -119,7 +119,7 @@
- +
diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 16868cad015..16b9689d3b4 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -217,6 +217,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT const ctx = { $implicit: this.value, additionalTemplateContext: this.column.additionalTemplateContext, + formControl: this.editMode ? this.formControl : undefined }; /* Turns the `cell` property from the template context object into lazy-evaluated one. * Otherwise on each detection cycle the cell template is recreating N cell instances where diff --git a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts index 122a3d45e06..73ef74adee2 100644 --- a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts @@ -65,6 +65,7 @@ export interface CellType { grid: GridType; id?: { rowID: any; columnID: number; rowIndex: number }; cellID?: any; + errors?: ValidationErrors; readonly?: boolean; title?: any; width: string; @@ -86,6 +87,7 @@ export interface RowType { summaries?: Map; groupRow?: IGroupByRecord; key?: any; + errors?: ValidationErrors; data?: any; cells?: QueryList | CellType[]; disabled?: boolean; diff --git a/projects/igniteui-angular/src/lib/grids/grid-public-cell.ts b/projects/igniteui-angular/src/lib/grids/grid-public-cell.ts index cda1797f344..48f58fe828a 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-public-cell.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-public-cell.ts @@ -1,7 +1,7 @@ import { CellType, ColumnType, GridType, RowType } from './common/grid.interface'; import { ISelectionNode } from './common/types'; import { resolveNestedPath } from '../core/utils'; -import { FormControl } from '@angular/forms'; +import { ValidationErrors } from '@angular/forms'; export class IgxGridCell implements CellType { @@ -76,11 +76,16 @@ export class IgxGridCell implements CellType { } /** - * Gets the formControl responsible for value changes and validation for this cell. + * Gets the validation errors if any. + * ```typescript + * let errors = this.cell.errors; + * ``` */ - public get formControl(): FormControl { + + public get errors(): ValidationErrors { const editRow = this.grid.crudService.row || this.grid.crudService.cell.row; - return editRow.rowFormGroup.get(this.column.field); + const form = editRow.rowFormGroup.get(this.column.field); + return form.errors; } /** diff --git a/projects/igniteui-angular/src/lib/grids/grid-public-row.ts b/projects/igniteui-angular/src/lib/grids/grid-public-row.ts index 9a53af44eeb..b680459103e 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-public-row.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-public-row.ts @@ -6,6 +6,7 @@ import { IgxSummaryResult } from './summaries/grid-summary'; import { ITreeGridRecord } from './tree-grid/tree-grid.interfaces'; import mergeWith from 'lodash.mergewith'; import { CellType, GridServiceType, GridType, RowType } from './common/grid.interface'; +import { ValidationErrors } from '@angular/forms'; abstract class BaseRow implements RowType { public index: number; @@ -51,6 +52,16 @@ abstract class BaseRow implements RowType { this.grid.crudService.row.id === this.key; } + /** Gets the validation errors if any. + * ```typescript + * let errors = row.errors; + * ``` + */ + public errors(): ValidationErrors { + const editRow = this.grid.crudService.row || this.grid.crudService.cell.row; + return editRow.rowFormGroup.errors; + } + /** * The data record that populates the row. * diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index d27c62ee8f7..64844eb4b5f 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -12,7 +12,10 @@ export class IgxGridValidationService { private _validityStates = new Map(); private _valid = true; - public get valid() { + + /** Gets whether state is valid. + */ + public get valid() : boolean { return this._valid; } @@ -36,7 +39,14 @@ export class IgxGridValidationService { } this.grid.formGroupCreated.emit(formGroup); this.add(rowId, formGroup); + } else { + // reset to pristine. + for (const col of this.grid.columns) { + const formControl = formGroup.get(col.field); + formControl.markAsPristine(); + } } + return formGroup; } @@ -65,10 +75,11 @@ export class IgxGridValidationService { this._validityStates.set(rowId, form); } - /** Returns validity states for records. - * @returns Array os records states. - */ - public getValidity(): IRecordValidationState[] { + /** + * @hidden + * @internal + */ + private getValidity(): IRecordValidationState[] { const states: IRecordValidationState[] = []; this._validityStates.forEach((formGroup, key) => { const state: IFieldValidationState[] = []; diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index f764e49d1af..940680937fa 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -6,9 +6,9 @@

Grid without transactions

[width]="'1200px'" [height]="'800px'"> - @@ -28,7 +28,7 @@

Grid with transactions

-
+
This name is forbidden.
From a0559dd22fb05deb84905dde6034a9b77fc9f81a Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 25 Aug 2022 11:05:43 +0300 Subject: [PATCH 093/149] chore(*): Do not update validation states while doing pending transaction updates. --- projects/igniteui-angular/src/lib/grids/api.service.ts | 10 ++++++---- .../src/lib/grids/grid/grid-validation.service.ts | 1 + .../src/lib/services/transaction/base-transaction.ts | 5 +++++ .../src/lib/services/transaction/transaction.ts | 3 +++ 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/api.service.ts b/projects/igniteui-angular/src/lib/grids/api.service.ts index 6ecf6085a2a..afb8d124950 100644 --- a/projects/igniteui-angular/src/lib/grids/api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/api.service.ts @@ -558,10 +558,12 @@ export class GridBaseAPIService implements GridServiceType { } else { mergeObjects(rowValueInDataSource, rowNewValue); } - const isAddRow = this.crudService.row instanceof IgxAddRow; - if (isAddRow) { return; } - const validation = grid.validation as IgxGridValidationService; - validation.update(rowID, rowNewValue); + + if (!this.grid.transactions.isPending) { + const validation = grid.validation as IgxGridValidationService; + validation.update(rowID, rowNewValue); + } + } diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index 64844eb4b5f..49b67741a18 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -143,6 +143,7 @@ export class IgxGridValidationService { this._validityStates.clear(); } this.updateStatus(); + (this.grid as any).markForCheck(); } } \ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts index 597d751b471..51bcbe9c364 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts @@ -38,6 +38,11 @@ export class IgxBaseTransactionService i return this._isPending; } + /** @hidden @internal **/ + public get isPending() { + return this._isPending; + } + /** * @inheritdoc */ diff --git a/projects/igniteui-angular/src/lib/services/transaction/transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/transaction.ts index 941fd07ee09..6cc7bba7bfa 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/transaction.ts @@ -61,6 +61,9 @@ export interface TransactionService { */ readonly enabled: boolean; + /** @hidden @internal **/ + readonly isPending: boolean; + /** * Gets/Sets the data clone strategy used to clone data */ From 3568703b1a383a50f3d723f377b3ffc2a754a3c7 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 25 Aug 2022 11:10:56 +0300 Subject: [PATCH 094/149] chore(*): Fix getters. --- projects/igniteui-angular/src/lib/grids/grid-public-cell.ts | 5 ++--- projects/igniteui-angular/src/lib/grids/grid-public-row.ts | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid-public-cell.ts b/projects/igniteui-angular/src/lib/grids/grid-public-cell.ts index 48f58fe828a..d1976562f64 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-public-cell.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-public-cell.ts @@ -83,9 +83,8 @@ export class IgxGridCell implements CellType { */ public get errors(): ValidationErrors { - const editRow = this.grid.crudService.row || this.grid.crudService.cell.row; - const form = editRow.rowFormGroup.get(this.column.field); - return form.errors; + const form = this.grid.validation.getFormControl(this.row.key, this.column.field); + return form?.errors; } /** diff --git a/projects/igniteui-angular/src/lib/grids/grid-public-row.ts b/projects/igniteui-angular/src/lib/grids/grid-public-row.ts index b680459103e..49a3b2ee271 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-public-row.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-public-row.ts @@ -58,8 +58,8 @@ abstract class BaseRow implements RowType { * ``` */ public errors(): ValidationErrors { - const editRow = this.grid.crudService.row || this.grid.crudService.cell.row; - return editRow.rowFormGroup.errors; + const formGroup = this.grid.validation.getFormGroup(this.key); + return formGroup?.errors; } /** From befb3e9fab772df0fe798b08efc2df6b46d6d710 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 25 Aug 2022 11:15:25 +0300 Subject: [PATCH 095/149] chore(*): Expose default error template in context. --- projects/igniteui-angular/src/lib/grids/cell.component.ts | 3 ++- src/app/grid-validation/grid-validation.sample.component.html | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 16b9689d3b4..f422bae0774 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -217,7 +217,8 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT const ctx = { $implicit: this.value, additionalTemplateContext: this.column.additionalTemplateContext, - formControl: this.editMode ? this.formControl : undefined + formControl: this.editMode ? this.formControl : undefined, + defaultErrorTemplate: this.defaultErrorTemplate }; /* Turns the `cell` property from the template context object into lazy-evaluated one. * Otherwise on each detection cycle the cell template is recreating N cell instances where diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index 940680937fa..c7719b36a80 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -25,8 +25,8 @@

Grid with transactions

[width]="'1200px'" [height]="'800px'" > - - + +
This name is forbidden. From 4a6a2af29b5603bc6d10d7014270fb8b27487335 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 25 Aug 2022 11:31:13 +0300 Subject: [PATCH 096/149] chore(*): Move update on cancel after transaction is no longer pending. --- .../src/lib/grids/common/crud.service.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts index ee588a424d6..ccd33a39e7b 100644 --- a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts +++ b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts @@ -352,6 +352,13 @@ export class IgxRowCrudState extends IgxCellCrudState { let nonCancelableArgs; if (!commit) { this.grid.transactions.endPending(false); + const isAddRow = this.row && this.row.getClassName() === IgxAddRow.name; + if (!isAddRow) { + const id = this.row ? this.row.id : this.cell.id.rowID; + const value = this.grid.transactions.getAggregatedValue(id, true); + const originalData = this.row ? this.row.data : this.cell.rowData; + this.grid.validation.update(id, value ?? originalData); + } } else if (this.row.getClassName() === IgxEditRow.name) { rowEditArgs = this.grid.gridAPI.update_row(this.row, this.row.newData, event); nonCancelableArgs = this.rowEditDone(rowEditArgs.oldValue, event); @@ -636,12 +643,6 @@ export class IgxGridCRUDService extends IgxRowAddCrudState { return args.cancel; } } else { - const id = this.row ? this.row.id : this.cell.id.rowID; - const value = this.grid.transactions.getAggregatedValue(id, true); - const originalData = this.row ? this.row.data : this.cell.rowData; - if (!(this.row instanceof IgxAddRow)) { - this.grid.validation.update(id, value ?? originalData); - } this.exitCellEdit(event); } From c2dc7d11215ccd1af932884a452d9d4a456c7b51 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 25 Aug 2022 14:24:40 +0300 Subject: [PATCH 097/149] chore(*): Update on cell edit with commit:false as well. --- .../igniteui-angular/src/lib/grids/common/crud.service.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts index ccd33a39e7b..ed609e152e1 100644 --- a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts +++ b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts @@ -643,6 +643,10 @@ export class IgxGridCRUDService extends IgxRowAddCrudState { return args.cancel; } } else { + if (!this.grid.rowEditable && this.cell) { + const value = this.grid.transactions.getAggregatedValue(this.cell.id.rowID, true) || this.cell.rowData; + this.grid.validation.update(this.cell.id.rowID, value); + } this.exitCellEdit(event); } From 250fa85d3bc6f744c09a9405d1204462f472e487 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 25 Aug 2022 15:52:36 +0300 Subject: [PATCH 098/149] chore(*): Show error state only if form is touched. For adding mark all forms as touched. --- .../igniteui-angular/src/lib/grids/api.service.ts | 1 + .../src/lib/grids/cell.component.ts | 3 ++- .../src/lib/grids/grid/grid-validation.service.ts | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/api.service.ts b/projects/igniteui-angular/src/lib/grids/api.service.ts index afb8d124950..eb0f0943903 100644 --- a/projects/igniteui-angular/src/lib/grids/api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/api.service.ts @@ -301,6 +301,7 @@ export class GridBaseAPIService implements GridServiceType { grid.data.push(rowData); } const validation = grid.validation as IgxGridValidationService; + validation.markAsTouched(transactionId); validation.update(transactionId, rowData); } diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index f422bae0774..05b25c0f961 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -487,7 +487,8 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT @HostBinding('class.igx-grid__td--invalid') @HostBinding('attr.aria-invalid') public get isInvalid() { - return !this.intRow.deleted && this.formGroup?.get(this.column?.field)?.invalid; + const isInvalid = this.formGroup?.get(this.column?.field)?.invalid && this.formGroup?.get(this.column?.field)?.touched; + return !this.intRow.deleted && isInvalid; } /** diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index 49b67741a18..1e10c3bedc7 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -120,6 +120,20 @@ export class IgxGridValidationService { this.updateStatus(); } + /** + * @hidden + * @internal + */ + public markAsTouched(rowId: any) { + const rowGroup = this.getFormGroup(rowId); + rowGroup.markAsTouched(); + for (const col of this.grid.columns) { + const field = col.field; + const control = rowGroup?.get(field); + control.markAsTouched(); + } + } + /** * @hidden * @internal From 6d958439dd49be0b50f4686e293f9226bc37e9a6 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 25 Aug 2022 15:58:36 +0300 Subject: [PATCH 099/149] chore(*): Fix tests. --- .../src/lib/grids/grid/grid-validation.service.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index 1e10c3bedc7..020b3ef8996 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -43,7 +43,9 @@ export class IgxGridValidationService { // reset to pristine. for (const col of this.grid.columns) { const formControl = formGroup.get(col.field); - formControl.markAsPristine(); + if (formControl) { + formControl.markAsPristine(); + } } } From 59874231e502452757e11559c17c6fd028a82442 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 25 Aug 2022 16:11:07 +0300 Subject: [PATCH 100/149] chore(*): Add check. --- .../src/lib/grids/grid/grid-validation.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index 020b3ef8996..54422e2880b 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -128,6 +128,7 @@ export class IgxGridValidationService { */ public markAsTouched(rowId: any) { const rowGroup = this.getFormGroup(rowId); + if (!rowGroup) return; rowGroup.markAsTouched(); for (const col of this.grid.columns) { const field = col.field; From 164ff2438f21761d4d89e107d641d3d978820322 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 25 Aug 2022 16:36:08 +0300 Subject: [PATCH 101/149] chore(*): Update sample. --- .../lib/grids/grid/grid-validation.service.ts | 1 - .../grid-validation.sample.component.html | 23 ++++++++++++------- .../grid-validation.sample.component.ts | 1 + 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index 54422e2880b..e344afac80a 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -160,7 +160,6 @@ export class IgxGridValidationService { this._validityStates.clear(); } this.updateStatus(); - (this.grid as any).markForCheck(); } } \ No newline at end of file diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index c7719b36a80..2cfe4ebafb3 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -4,13 +4,18 @@

Grid without transactions

Row edit - - + + + + + +
+ This name is forbidden. +
+
+ + @@ -23,8 +28,8 @@

Grid with transactions

Row edit - + + @@ -33,6 +38,8 @@

Grid with transactions

+ + diff --git a/src/app/grid-validation/grid-validation.sample.component.ts b/src/app/grid-validation/grid-validation.sample.component.ts index 33040124558..e38a45be653 100644 --- a/src/app/grid-validation/grid-validation.sample.component.ts +++ b/src/app/grid-validation/grid-validation.sample.component.ts @@ -70,6 +70,7 @@ export class GridValidationSampleComponent { public clearValidity() { this.gridNoTransactions.validation.clear(); + this.gridNoTransactions.markForCheck(); } public cellEdit(evt) { From ee79c045590489c313ac6a9256f4941474788192 Mon Sep 17 00:00:00 2001 From: MKirova Date: Fri, 26 Aug 2022 12:24:46 +0300 Subject: [PATCH 102/149] chore(*): Mark as touched on value changes. Minor sample update. --- projects/igniteui-angular/src/lib/grids/cell.component.ts | 1 + src/app/grid-validation/grid-validation.sample.component.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 05b25c0f961..4662bc4675c 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -866,6 +866,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT // while in edit mode subscribe to value changes on the current form control and set to editValue this.formControl.valueChanges.pipe(takeWhile(x => this.editMode)).subscribe(value => { this.editValue = value; + this.formControl.markAsTouched(); }); this.formControl.statusChanges.pipe(takeWhile(x => this.editMode)).subscribe(status => { if (status === 'INVALID' && this.errorTooltip.length > 0) { diff --git a/src/app/grid-validation/grid-validation.sample.component.ts b/src/app/grid-validation/grid-validation.sample.component.ts index e38a45be653..01857379455 100644 --- a/src/app/grid-validation/grid-validation.sample.component.ts +++ b/src/app/grid-validation/grid-validation.sample.component.ts @@ -87,7 +87,7 @@ export class GridValidationSampleComponent { } public validationChange(evtArgs: Validity){ - alert(evtArgs === Validity.Invalid ? 'state became INVALID' : 'state became VALID'); + console.log(evtArgs === Validity.Invalid ? 'state became INVALID' : 'state became VALID'); } } From ba17349f8aa8c8bf16c7606389b2e72b451a36f7 Mon Sep 17 00:00:00 2001 From: MKirova Date: Fri, 26 Aug 2022 14:49:21 +0300 Subject: [PATCH 103/149] chore(*): Apply review comments. --- projects/igniteui-angular/src/lib/grids/api.service.ts | 7 +++---- projects/igniteui-angular/src/lib/grids/cell.component.ts | 4 ++++ .../igniteui-angular/src/lib/grids/common/crud.service.ts | 4 +--- projects/igniteui-angular/src/lib/grids/public_api.ts | 2 +- .../grid-validation/grid-validation.sample.component.ts | 4 ++-- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/api.service.ts b/projects/igniteui-angular/src/lib/grids/api.service.ts index eb0f0943903..b0b658e698d 100644 --- a/projects/igniteui-angular/src/lib/grids/api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/api.service.ts @@ -4,7 +4,7 @@ import { cloneArray, reverseMapper, mergeObjects } from '../core/utils'; import { DataUtil, GridColumnDataType } from '../data-operations/data-util'; import { IFilteringExpressionsTree } from '../data-operations/filtering-expressions-tree'; import { Transaction, TransactionType, State } from '../services/transaction/transaction'; -import { IgxCell, IgxGridCRUDService, IgxEditRow, IgxAddRow } from './common/crud.service'; +import { IgxCell, IgxGridCRUDService, IgxEditRow } from './common/crud.service'; import { CellType, ColumnType, GridServiceType, GridType, IFieldValid, RowType } from './common/grid.interface'; import { IGridEditEventArgs, IRowToggleEventArgs } from './common/events'; import { IgxColumnMovingService } from './moving/moving.service'; @@ -300,9 +300,8 @@ export class GridBaseAPIService implements GridServiceType { } else { grid.data.push(rowData); } - const validation = grid.validation as IgxGridValidationService; - validation.markAsTouched(transactionId); - validation.update(transactionId, rowData); + grid.validation.markAsTouched(transactionId); + grid.validation.update(transactionId, rowData); } public deleteRowFromData(rowID: any, index: number) { diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 4662bc4675c..fa5fcef7a89 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -107,6 +107,10 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT public column: ColumnType; + /** + * @hidden + * @internal + */ public get formGroup(): FormGroup { return this.grid.validation.getFormGroup(this.intRow.key); } diff --git a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts index ed609e152e1..bb9c2934ee4 100644 --- a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts +++ b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts @@ -355,9 +355,7 @@ export class IgxRowCrudState extends IgxCellCrudState { const isAddRow = this.row && this.row.getClassName() === IgxAddRow.name; if (!isAddRow) { const id = this.row ? this.row.id : this.cell.id.rowID; - const value = this.grid.transactions.getAggregatedValue(id, true); - const originalData = this.row ? this.row.data : this.cell.rowData; - this.grid.validation.update(id, value ?? originalData); + this.grid.validation.update(id, rowEditArgs.oldValue); } } else if (this.row.getClassName() === IgxEditRow.name) { rowEditArgs = this.grid.gridAPI.update_row(this.row, this.row.newData, event); diff --git a/projects/igniteui-angular/src/lib/grids/public_api.ts b/projects/igniteui-angular/src/lib/grids/public_api.ts index 6c7b3239a00..46ab8e10a86 100644 --- a/projects/igniteui-angular/src/lib/grids/public_api.ts +++ b/projects/igniteui-angular/src/lib/grids/public_api.ts @@ -13,7 +13,7 @@ export * from './grid-base.directive'; export * from './grid.common'; export * from './grid-public-row'; export * from './grid-public-cell'; -export { CellType, RowType, IGX_GRID_BASE, Validity } from './common/grid.interface'; +export { CellType, RowType, IGX_GRID_BASE, Validity, IRecordValidationState, IFieldValidationState } from './common/grid.interface'; export * from './summaries/grid-summary'; export * from './grid-common.module'; export * from './grid.rowEdit.directive'; diff --git a/src/app/grid-validation/grid-validation.sample.component.ts b/src/app/grid-validation/grid-validation.sample.component.ts index 01857379455..ca5ddc4c768 100644 --- a/src/app/grid-validation/grid-validation.sample.component.ts +++ b/src/app/grid-validation/grid-validation.sample.component.ts @@ -1,7 +1,7 @@ import { Component, Directive, ViewChild, Input } from '@angular/core'; import { data } from '../shared/data'; -import { IgxGridComponent, IgxTransactionService, Validity } from 'igniteui-angular'; +import { IgxGridComponent, IgxTransactionService, Validity, IRecordValidationState } from 'igniteui-angular'; import { AbstractControl, FormGroup, NG_VALIDATORS, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { @@ -50,7 +50,7 @@ export class GridValidationSampleComponent { public gridNoTransactions: IgxGridComponent; public commitWithTransactions() { - const invalid = this.gridWithTransaction.validation.getInvalid(); + const invalid: IRecordValidationState[] = this.gridWithTransaction.validation.getInvalid(); if (invalid.length > 0) { if (confirm('There are invalid values about to be submitted. Do you want to continue')) { this.gridWithTransaction.transactions.commit(this.transactionData); From e1b65c312abbad99e9f1395bf69b64be8a3d8c2c Mon Sep 17 00:00:00 2001 From: MKirova Date: Fri, 26 Aug 2022 16:27:07 +0300 Subject: [PATCH 104/149] chore(*): Hide validationService from other grids. --- .../grids/hierarchical-grid/hierarchical-grid-base.directive.ts | 2 +- .../src/lib/grids/hierarchical-grid/row-island.component.ts | 2 +- .../src/lib/grids/pivot-grid/pivot-grid.component.ts | 2 +- .../src/lib/grids/tree-grid/tree-grid.component.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid-base.directive.ts index ae9cca02bca..d69f8727956 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid-base.directive.ts @@ -149,7 +149,7 @@ export abstract class IgxHierarchicalGridBaseDirective extends IgxGridBaseDirect public abstract expandChildren: boolean; constructor( - public validationService: IgxGridValidationService, + validationService: IgxGridValidationService, public selectionService: IgxGridSelectionService, public colResizingService: IgxColumnResizingService, @Inject(IGX_GRID_SERVICE_BASE) public gridAPI: IgxHierarchicalGridAPIService, diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/row-island.component.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/row-island.component.ts index c068a23e769..80a61ae976a 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/row-island.component.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/row-island.component.ts @@ -218,7 +218,7 @@ export class IgxRowIslandComponent extends IgxHierarchicalGridBaseDirective } constructor( - public validationService: IgxGridValidationService, + validationService: IgxGridValidationService, public selectionService: IgxGridSelectionService, public colResizingService: IgxColumnResizingService, @Inject(IGX_GRID_SERVICE_BASE) gridAPI: IgxHierarchicalGridAPIService, diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.ts index f470555e3a8..1a6a7073ebe 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.ts @@ -913,7 +913,7 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni } constructor( - public validationService: IgxGridValidationService, + validationService: IgxGridValidationService, public selectionService: IgxGridSelectionService, public colResizingService: IgxPivotColumnResizingService, gridAPI: GridBaseAPIService, diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts index de0a4ba2c5e..ee179ffd9a6 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts @@ -393,7 +393,7 @@ export class IgxTreeGridComponent extends IgxGridBaseDirective implements GridTy // } constructor( - public validationService: IgxGridValidationService, + protected validationService: IgxGridValidationService, public selectionService: IgxGridSelectionService, public colResizingService: IgxColumnResizingService, @Inject(IGX_GRID_SERVICE_BASE) public gridAPI: GridServiceType, From 3ab2ac190d403b51fb1b64bda4ba2d2276bbddf9 Mon Sep 17 00:00:00 2001 From: Svetoslav Krastev Date: Fri, 26 Aug 2022 19:22:42 +0300 Subject: [PATCH 105/149] test(validator): Some fixes to the validator tests and add a few more. --- .../lib/grids/grid/grid-validation.spec.ts | 135 +++++++++++++++--- .../src/lib/test-utils/grid-functions.spec.ts | 6 +- .../grid-validation-samples.spec.ts | 46 +++--- 3 files changed, 142 insertions(+), 45 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts index af353a9528e..5f021884ceb 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts @@ -7,7 +7,11 @@ import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators' import { configureTestSuite } from '../../test-utils/configure-suite'; import { GridFunctions, GridSelectionFunctions } from '../../test-utils/grid-functions.spec'; -import { ForbiddenValidatorDirective, IgxGridValidationTestBaseComponent, IgxGridValidationTestCustomErrorComponent } from '../../test-utils/grid-validation-samples.spec'; +import { + ForbiddenValidatorDirective, + IgxGridValidationTestBaseComponent, + IgxGridValidationTestCustomErrorComponent +} from '../../test-utils/grid-validation-samples.spec'; import { UIInteractions } from '../../test-utils/ui-interactions.spec'; import { Validity } from '../common/grid.interface'; import { IgxGridComponent } from './grid.component'; @@ -97,7 +101,7 @@ describe('IgxGrid - Validation #grid', () => { it('should allow setting validators on the exposed FormGroup object', () => { const grid = fixture.componentInstance.grid as IgxGridComponent; - grid.onFormGroupCreate.pipe(takeUntil($destroyer)).subscribe((formGroup: FormGroup) => { + grid.formGroupCreated.pipe(takeUntil($destroyer)).subscribe((formGroup: FormGroup) => { const prodName = formGroup.get('ProductName'); prodName.addValidators(Validators.email); }); @@ -196,7 +200,7 @@ describe('IgxGrid - Validation #grid', () => { it('should allow preventing edit mode for cell/row to end by canceling the related event if isValid event argument is false', () => { const grid = fixture.componentInstance.grid as IgxGridComponent; grid.cellEdit.pipe(takeUntil($destroyer)).subscribe((args) => { - if (!args.isValid) { + if (!args.valid) { args.cancel = true; } }); @@ -224,34 +228,34 @@ describe('IgxGrid - Validation #grid', () => { GridFunctions.verifyCellValid(cell, true); }); - it('should trigger the onValidationStatusChange event on grid when validation status changes', () => { + it('should trigger the validationStatusChange event on grid when validation status changes', () => { const grid = fixture.componentInstance.grid as IgxGridComponent; spyOn(grid.validationStatusChange, "emit").and.callThrough(); let cell = grid.gridAPI.get_cell_by_visible_index(1, 1); UIInteractions.simulateDoubleClickAndSelectEvent(cell.element); + cell.editMode = true; cell.update('asd'); fixture.detectChanges(); cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + grid.crudService.endEdit(true); + fixture.detectChanges(); + GridFunctions.verifyCellValid(cell, false); - expect(grid.validationStatusChange.emit).toHaveBeenCalledWith({ - formGroup: cell.formGroup, - state: Validity.Invalid, - value: 'asd' - }); + expect(grid.validationStatusChange.emit).toHaveBeenCalledWith(Validity.Invalid); + UIInteractions.simulateDoubleClickAndSelectEvent(cell.element); cell.editMode = true; cell.update('test'); fixture.detectChanges(); cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + grid.crudService.endEdit(true); + fixture.detectChanges(); + GridFunctions.verifyCellValid(cell, true); - expect(grid.validationStatusChange.emit).toHaveBeenCalledWith({ - formGroup: cell.formGroup, - state: Validity.Valid, - value: 'test' - }); + expect(grid.validationStatusChange.emit).toHaveBeenCalledWith(Validity.Valid); }); xit('should return invalid transaction using the transaction`s getInvalidTransactionLog method', () => { @@ -263,11 +267,10 @@ describe('IgxGrid - Validation #grid', () => { cell.update('asd'); fixture.detectChanges(); cell = grid.gridAPI.get_cell_by_visible_index(1, 1); - let transactionLog = grid.transactions.getInvalidTransactionLog(); + let transactionLog = grid.validation.getInvalid(); GridFunctions.verifyCellValid(cell, false); - expect(transactionLog[0].newValue).toEqual({ ProductName: 'asd' }); - expect(transactionLog[0].validity[0].valid).toBeFalse(); + expect(transactionLog[0].state[0].valid).toBeFalse(); cell.editMode = true; @@ -276,9 +279,8 @@ describe('IgxGrid - Validation #grid', () => { cell = grid.gridAPI.get_cell_by_visible_index(1, 1); GridFunctions.verifyCellValid(cell, true); - transactionLog = grid.transactions.getInvalidTransactionLog(); - expect(transactionLog[1].newValue).toEqual({ ProductName: 'test' }); - expect(transactionLog[1].validity[0].valid).toBeTrue(); + transactionLog = grid.validation.getInvalid(); + expect(transactionLog[1].state[0].valid).toBeTrue(); }); }); @@ -290,7 +292,7 @@ describe('IgxGrid - Validation #grid', () => { fixture.detectChanges(); })); - it('should allow setting built-in validators via template-driven configuration on the column', () => { + it('should allow setting custom validators via template-driven configuration on the column', () => { const grid = fixture.componentInstance.grid as IgxGridComponent; let cell = grid.gridAPI.get_cell_by_visible_index(1, 1); @@ -311,4 +313,93 @@ describe('IgxGrid - Validation #grid', () => { GridFunctions.verifyCellValid(cell, true); }); }); -}); \ No newline at end of file + + describe('Trensactions integration - ', () => { + let fixture; + + beforeEach(fakeAsync(() => { + fixture = TestBed.createComponent(IgxGridValidationTestBaseComponent); + fixture.componentInstance.batchEditing = true; + fixture.detectChanges(); + })); + + it('should allow setting built-in validators via template-driven configuration on the column', () => { + const grid = fixture.componentInstance.grid as IgxGridComponent; + let cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + + UIInteractions.simulateDoubleClickAndSelectEvent(cell.element); + cell.editMode = true; + cell.update('IG'); + fixture.detectChanges(); + + grid.gridAPI.crudService.endEdit(true); + fixture.detectChanges(); + + GridFunctions.verifyCellValid(cell, false); + + grid.transactions.undo(); + fixture.detectChanges(); + + cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + GridFunctions.verifyCellValid(cell, true); + + grid.transactions.redo(); + fixture.detectChanges(); + + cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + GridFunctions.verifyCellValid(cell, false); + + grid.transactions.commit(grid.data); + fixture.detectChanges(); + + GridFunctions.verifyCellValid(cell, false); + expect((grid.validation as any).getValidity().length).toEqual(1); + + grid.validation.clear(); + fixture.detectChanges(); + }); + + it('should not invalidate cleared number cell when transactions are enabled', () => { + const grid = fixture.componentInstance.grid as IgxGridComponent; + let cell = grid.gridAPI.get_cell_by_visible_index(1, 3); + + // Set cell to null, which should invalidate + UIInteractions.simulateDoubleClickAndSelectEvent(cell.element); + cell.editMode = true; + cell.update(null); + fixture.detectChanges(); + + expect(grid.validation.getInvalid().length).toEqual(1); + GridFunctions.verifyCellValid(cell, false); + + // Exit edit. CRUD service sets number value to 0 + grid.gridAPI.crudService.endEdit(true); + fixture.detectChanges(); + + expect(grid.validation.getInvalid().length).toEqual(0); + GridFunctions.verifyCellValid(cell, true); + + // Undo. Expect previous value + grid.transactions.undo(); + fixture.detectChanges(); + + expect(grid.validation.getInvalid().length).toEqual(0); + expect((grid.validation as any).getValidity().length).toEqual(1); + GridFunctions.verifyCellValid(cell, true); + + // Redo. Expect value 0 + grid.transactions.redo(); + fixture.detectChanges(); + + expect(grid.validation.getInvalid().length).toEqual(0); + expect((grid.validation as any).getValidity().length).toEqual(1); + GridFunctions.verifyCellValid(cell, true); + + grid.transactions.commit(grid.data); + grid.validation.clear(); + fixture.detectChanges(); + + expect((grid.validation as any).getValidity().length).toEqual(0); + }); + }); +}); diff --git a/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts b/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts index 2e3b447a48f..9d1cce9efaf 100644 --- a/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts @@ -2079,9 +2079,9 @@ export class GridFunctions { public static getResizer(fix): DebugElement { return fix.debugElement.query(By.css(RESIZE_LINE_CLASS)); } - - public static verifyCellValid(cell: IgxGridCellComponent | CellType, valid = true) { - expect((cell as IgxGridCell).formControl.valid).toEqual(valid); + + public static verifyCellValid(cell: IgxGridCellComponent, valid = true) { + expect(cell.formControl.valid).toEqual(valid); expect(cell.nativeElement.classList.contains(CELL_INVALID_CSS_CLASS)).not.toEqual(valid); } } diff --git a/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts b/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts index 1e13dfe05b1..4863210b31a 100644 --- a/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts @@ -1,6 +1,6 @@ import { Component, Input, ViewChild, Directive } from '@angular/core'; -import { AbstractControl, NG_VALIDATORS, ValidationErrors, ValidatorFn } from '@angular/forms'; -import { IgxColumnValidator, IgxGridComponent } from 'igniteui-angular'; +import { AbstractControl, NG_VALIDATORS, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; +import { IgxGridComponent } from 'igniteui-angular'; import { data } from '../../../../../src/app/shared/data'; export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { @@ -14,7 +14,7 @@ export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { selector: '[igxAppForbiddenName]', providers: [{ provide: NG_VALIDATORS, useExisting: ForbiddenValidatorDirective, multi: true }] }) -export class ForbiddenValidatorDirective extends IgxColumnValidator { +export class ForbiddenValidatorDirective extends Validators { @Input('igxAppForbiddenName') public forbiddenName = ''; @@ -26,23 +26,26 @@ export class ForbiddenValidatorDirective extends IgxColumnValidator { @Component({ template: ` - - + ` }) export class IgxGridValidationTestBaseComponent { + public batchEditing = false; public rowEditable = true; public columns = [ - { field: 'ProductID' }, - { field: 'ProductName' }, - { field: 'UnitPrice' }, - { field: 'UnitsInStock' } + { field: 'ProductID', dataType: 'string' }, + { field: 'ProductName', dataType: 'string' }, + { field: 'UnitPrice', dataType: 'string' }, + { field: 'UnitsInStock', dataType: 'number' } ]; - public data = data;; + public data = [...data]; @ViewChild('grid', { read: IgxGridComponent, static: true }) public grid: IgxGridComponent; } @@ -51,10 +54,12 @@ export class IgxGridValidationTestBaseComponent { template: ` - + -
+
This name is forbidden.
@@ -63,14 +68,15 @@ export class IgxGridValidationTestBaseComponent { ` }) export class IgxGridValidationTestCustomErrorComponent { + public batchEditing = false; public rowEditable = true; public columns = [ - { field: 'ProductID' }, - { field: 'ProductName' }, - { field: 'UnitPrice' }, - { field: 'UnitsInStock' } + { field: 'ProductID', dataType: 'string' }, + { field: 'ProductName', dataType: 'string' }, + { field: 'UnitPrice', dataType: 'string' }, + { field: 'UnitsInStock', dataType: 'number' } ]; - public data = data;; + public data = [...data]; @ViewChild('grid', { read: IgxGridComponent, static: true }) public grid: IgxGridComponent; -} \ No newline at end of file +} From c2e288b7d5a19d8327c1e6c623b3994d1cc4ef7f Mon Sep 17 00:00:00 2001 From: MKirova Date: Mon, 29 Aug 2022 11:14:41 +0300 Subject: [PATCH 106/149] chore(*): Move validation updates on update_cell/update_row if the edit mode matches accordingly. Remove isPending check. --- .../igniteui-angular/src/lib/grids/api.service.ts | 15 ++++++++------- .../lib/services/transaction/base-transaction.ts | 5 ----- .../src/lib/services/transaction/transaction.ts | 3 --- .../grid-validation.sample.component.ts | 2 +- 4 files changed, 9 insertions(+), 16 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/api.service.ts b/projects/igniteui-angular/src/lib/grids/api.service.ts index b0b658e698d..8bfad870759 100644 --- a/projects/igniteui-angular/src/lib/grids/api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/api.service.ts @@ -146,7 +146,11 @@ export class GridBaseAPIService implements GridServiceType { this.grid.summaryService.clearSummaryCache(args); const data = this.getRowData(cell.id.rowID); - this.updateData(this.grid, cell.id.rowID, data, cell.rowData, reverseMapper(cell.column.field, args.newValue)); + const newRowData = reverseMapper(cell.column.field, args.newValue); + this.updateData(this.grid, cell.id.rowID, data, cell.rowData, newRowData); + if (!this.grid.rowEditable) { + this.grid.validation.update(cell.id.rowID, newRowData); + } if (this.grid.primaryKey === cell.column.field) { if (this.grid.selectionService.isRowSelected(cell.id.rowID)) { this.grid.selectionService.deselectRow(cell.id.rowID); @@ -199,6 +203,9 @@ export class GridBaseAPIService implements GridServiceType { } this.updateData(grid, row.id, data[index], args.oldValue, args.newValue); + if (this.grid.rowEditable) { + this.grid.validation.update(row.id, args.newValue); + } const newId = grid.primaryKey ? args.newValue[grid.primaryKey] : args.newValue; if (selected) { grid.selectionService.deselectRow(row.id); @@ -558,12 +565,6 @@ export class GridBaseAPIService implements GridServiceType { } else { mergeObjects(rowValueInDataSource, rowNewValue); } - - if (!this.grid.transactions.isPending) { - const validation = grid.validation as IgxGridValidationService; - validation.update(rowID, rowNewValue); - } - } diff --git a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts index 51bcbe9c364..597d751b471 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts @@ -38,11 +38,6 @@ export class IgxBaseTransactionService i return this._isPending; } - /** @hidden @internal **/ - public get isPending() { - return this._isPending; - } - /** * @inheritdoc */ diff --git a/projects/igniteui-angular/src/lib/services/transaction/transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/transaction.ts index 6cc7bba7bfa..941fd07ee09 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/transaction.ts @@ -61,9 +61,6 @@ export interface TransactionService { */ readonly enabled: boolean; - /** @hidden @internal **/ - readonly isPending: boolean; - /** * Gets/Sets the data clone strategy used to clone data */ diff --git a/src/app/grid-validation/grid-validation.sample.component.ts b/src/app/grid-validation/grid-validation.sample.component.ts index ca5ddc4c768..5a8dbe2eb92 100644 --- a/src/app/grid-validation/grid-validation.sample.component.ts +++ b/src/app/grid-validation/grid-validation.sample.component.ts @@ -87,7 +87,7 @@ export class GridValidationSampleComponent { } public validationChange(evtArgs: Validity){ - console.log(evtArgs === Validity.Invalid ? 'state became INVALID' : 'state became VALID'); + alert(evtArgs === Validity.Invalid ? 'state became INVALID' : 'state became VALID'); } } From 4a7845000136219484e75580be2cb6d277862b91 Mon Sep 17 00:00:00 2001 From: MKirova Date: Mon, 29 Aug 2022 12:43:53 +0300 Subject: [PATCH 107/149] chore(*): Add sample with all grids. --- .../grid-validation.sample.component.html | 31 + .../grid-validation.sample.component.ts | 67 + src/app/shared/hierarchicalData.ts | 12622 ++++++++++++++++ 3 files changed, 12720 insertions(+) create mode 100644 src/app/shared/hierarchicalData.ts diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index 2cfe4ebafb3..d5c9f01b6d1 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -48,4 +48,35 @@

Grid with transactions

+
+ + +
+

TreeGrid

+ + + + + + + + +
+ + +
+

Hierarchical Grid

+ + + + + + + +
\ No newline at end of file diff --git a/src/app/grid-validation/grid-validation.sample.component.ts b/src/app/grid-validation/grid-validation.sample.component.ts index ca5ddc4c768..03bbef02a34 100644 --- a/src/app/grid-validation/grid-validation.sample.component.ts +++ b/src/app/grid-validation/grid-validation.sample.component.ts @@ -3,6 +3,7 @@ import { data } from '../shared/data'; import { IgxGridComponent, IgxTransactionService, Validity, IRecordValidationState } from 'igniteui-angular'; import { AbstractControl, FormGroup, NG_VALIDATORS, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; +import { HIERARCHICAL_DATA } from '../shared/hierarchicalData'; export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { return (control: AbstractControl): ValidationErrors | null => { @@ -43,6 +44,72 @@ export class GridValidationSampleComponent { { field: 'UnitsInStock' } ]; + public treeColumns = [ + { field: 'employeeID', label: 'ID', width: 200, resizable: true, dataType: 'number', hasSummary: false }, + { field: 'Salary', label: 'Salary', width: 200, resizable: true, dataType: 'number', hasSummary: true }, + { field: 'firstName', label: 'First Name', width: 300, resizable: true, dataType: 'string', hasSummary: false }, + { field: 'lastName', label: 'Last Name', width: 150, resizable: true, dataType: 'string', hasSummary: false }, + { field: 'Title', label: 'Title', width: 200, resizable: true, dataType: 'string', hasSummary: true } + ]; + public treeData = [ + { Salary: 2500, employeeID: 0, PID: -1, firstName: 'Andrew', lastName: 'Fuller', Title: 'Vice President, Sales' }, + { Salary: 3500, employeeID: 1, PID: -1, firstName: 'Jonathan', lastName: 'Smith', Title: 'Human resources' }, + { Salary: 1500, employeeID: 2, PID: -1, firstName: 'Nancy', lastName: 'Davolio', Title: 'CFO' }, + { Salary: 2500, employeeID: 3, PID: -1, firstName: 'Steven', lastName: 'Buchanan', Title: 'CTO' }, + // sub of ID 0 + { Salary: 2500, employeeID: 4, PID: 0, firstName: 'Janet', lastName: 'Leverling', Title: 'Sales Manager' }, + { + Salary: 3500, employeeID: 5, PID: 0, firstName: 'Laura', lastName: 'Callahan', + Title: 'Inside Sales Coordinator' + }, + { Salary: 1500, employeeID: 6, PID: 0, firstName: 'Margaret', lastName: 'Peacock', Title: 'Sales Representative' }, + { Salary: 2500, employeeID: 7, PID: 0, firstName: 'Michael', lastName: 'Suyama', Title: 'Sales Representative' }, + // sub of ID 4 + { Salary: 2500, employeeID: 8, PID: 4, firstName: 'Anne', lastName: 'Dodsworth', Title: 'Sales Representative' }, + { Salary: 3500, employeeID: 9, PID: 4, firstName: 'Danielle', lastName: 'Davis', Title: 'Sales Representative' }, + { Salary: 1500, employeeID: 10, PID: 4, firstName: 'Robert', lastName: 'King', Title: 'Sales Representative' }, + // sub of ID 2 + { Salary: 2500, employeeID: 11, PID: 2, firstName: 'Peter', lastName: 'Lewis', Title: 'Chief Accountant' }, + { Salary: 3500, employeeID: 12, PID: 2, firstName: 'Ryder', lastName: 'Zenaida', Title: 'Accountant' }, + { Salary: 1500, employeeID: 13, PID: 2, firstName: 'Wang', lastName: 'Mercedes', Title: 'Accountant' }, + // sub of ID 3 + { Salary: 1500, employeeID: 14, PID: 3, firstName: 'Theodore', lastName: 'Zia', Title: 'Software Architect' }, + { Salary: 4500, employeeID: 15, PID: 3, firstName: 'Lacota', lastName: 'Mufutau', Title: 'Product Manager' }, + // sub of ID 16 + { Salary: 2500, employeeID: 16, PID: 15, firstName: 'Jin', lastName: 'Elliott', Title: 'Product Owner' }, + { Salary: 3500, employeeID: 17, PID: 15, firstName: 'Armand', lastName: 'Ross', Title: 'Product Owner' }, + { Salary: 1500, employeeID: 18, PID: 15, firstName: 'Dane', lastName: 'Rodriquez', Title: 'Team Leader' }, + // sub of ID 19 + { + Salary: 2500, employeeID: 19, PID: 18, firstName: 'Declan', lastName: 'Lester', + Title: 'Senior Software Developer' + }, + { + Salary: 3500, employeeID: 20, PID: 18, firstName: 'Bernard', lastName: 'Jarvis', + Title: 'Senior Software Developer' + }, + { Salary: 1500, employeeID: 21, PID: 18, firstName: 'Jason', lastName: 'Clark', Title: 'QA' }, + { Salary: 1500, employeeID: 22, PID: 18, firstName: 'Mark', lastName: 'Young', Title: 'QA' }, + // sub of ID 20 + { Salary: 1500, employeeID: 23, PID: 20, firstName: 'Jeremy', lastName: 'Donaldson', Title: 'Software Developer' } + ]; + + public hGridData = HIERARCHICAL_DATA; + public hColumns = [ + { field: 'FirstName' }, + { field: 'LastName' }, + { field: 'Title' }, + { field: 'City' } +]; + +public hColumns2 = [ + { field: 'ShipName' }, + { field: 'ShipAddress' }, + { field: 'ShipCity' }, + { field: 'OrderDate' } +]; + + @ViewChild('gridTransactions', {read: IgxGridComponent }) public gridWithTransaction: IgxGridComponent; diff --git a/src/app/shared/hierarchicalData.ts b/src/app/shared/hierarchicalData.ts new file mode 100644 index 00000000000..86f367c59fa --- /dev/null +++ b/src/app/shared/hierarchicalData.ts @@ -0,0 +1,12622 @@ +export const HIERARCHICAL_DATA = [{ + "Orders": [{ + "OrderID": 10258, + "CustomerID": "ERNSH", + "EmployeeID": 1, + "OrderDate": "1996-07-17T00:00:00", + "RequiredDate": "1996-08-14T00:00:00", + "ShippedDate": "1996-07-23T00:00:00", + "ShipVia": 1, + "Freight": "140.5100", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10270, + "CustomerID": "WARTH", + "EmployeeID": 1, + "OrderDate": "1996-08-01T00:00:00", + "RequiredDate": "1996-08-29T00:00:00", + "ShippedDate": "1996-08-02T00:00:00", + "ShipVia": 1, + "Freight": "136.5400", + "ShipName": "Wartian Herkku", + "ShipAddress": "Torikatu 38", + "ShipCity": "Oulu", + "ShipRegion": null, + "ShipPostalCode": "90110", + "ShipCountry": "Finland" + }, { + "OrderID": 10275, + "CustomerID": "MAGAA", + "EmployeeID": 1, + "OrderDate": "1996-08-07T00:00:00", + "RequiredDate": "1996-09-04T00:00:00", + "ShippedDate": "1996-08-09T00:00:00", + "ShipVia": 1, + "Freight": "26.9300", + "ShipName": "Magazzini Alimentari Riuniti", + "ShipAddress": "Via Ludovico il Moro 22", + "ShipCity": "Bergamo", + "ShipRegion": null, + "ShipPostalCode": "24100", + "ShipCountry": "Italy" + }, { + "OrderID": 10285, + "CustomerID": "QUICK", + "EmployeeID": 1, + "OrderDate": "1996-08-20T00:00:00", + "RequiredDate": "1996-09-17T00:00:00", + "ShippedDate": "1996-08-26T00:00:00", + "ShipVia": 2, + "Freight": "76.8300", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10292, + "CustomerID": "TRADH", + "EmployeeID": 1, + "OrderDate": "1996-08-28T00:00:00", + "RequiredDate": "1996-09-25T00:00:00", + "ShippedDate": "1996-09-02T00:00:00", + "ShipVia": 2, + "Freight": "1.3500", + "ShipName": "Tradi\u00e7ao Hipermercados", + "ShipAddress": "Av. In\u00eas de Castro, 414", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05634-030", + "ShipCountry": "Brazil" + }, { + "OrderID": 10293, + "CustomerID": "TORTU", + "EmployeeID": 1, + "OrderDate": "1996-08-29T00:00:00", + "RequiredDate": "1996-09-26T00:00:00", + "ShippedDate": "1996-09-11T00:00:00", + "ShipVia": 3, + "Freight": "21.1800", + "ShipName": "Tortuga Restaurante", + "ShipAddress": "Avda. Azteca 123", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05033", + "ShipCountry": "Mexico" + }, { + "OrderID": 10304, + "CustomerID": "TORTU", + "EmployeeID": 1, + "OrderDate": "1996-09-12T00:00:00", + "RequiredDate": "1996-10-10T00:00:00", + "ShippedDate": "1996-09-17T00:00:00", + "ShipVia": 2, + "Freight": "63.7900", + "ShipName": "Tortuga Restaurante", + "ShipAddress": "Avda. Azteca 123", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05033", + "ShipCountry": "Mexico" + }, { + "OrderID": 10306, + "CustomerID": "ROMEY", + "EmployeeID": 1, + "OrderDate": "1996-09-16T00:00:00", + "RequiredDate": "1996-10-14T00:00:00", + "ShippedDate": "1996-09-23T00:00:00", + "ShipVia": 3, + "Freight": "7.5600", + "ShipName": "Romero y tomillo", + "ShipAddress": "Gran V\u00eda, 1", + "ShipCity": "Madrid", + "ShipRegion": null, + "ShipPostalCode": "28001", + "ShipCountry": "Spain" + }, { + "OrderID": 10311, + "CustomerID": "DUMON", + "EmployeeID": 1, + "OrderDate": "1996-09-20T00:00:00", + "RequiredDate": "1996-10-04T00:00:00", + "ShippedDate": "1996-09-26T00:00:00", + "ShipVia": 3, + "Freight": "24.6900", + "ShipName": "Du monde entier", + "ShipAddress": "67, rue des Cinquante Otages", + "ShipCity": "Nantes", + "ShipRegion": null, + "ShipPostalCode": "44000", + "ShipCountry": "France" + }, { + "OrderID": 10314, + "CustomerID": "RATTC", + "EmployeeID": 1, + "OrderDate": "1996-09-25T00:00:00", + "RequiredDate": "1996-10-23T00:00:00", + "ShippedDate": "1996-10-04T00:00:00", + "ShipVia": 2, + "Freight": "74.1600", + "ShipName": "Rattlesnake Canyon Grocery", + "ShipAddress": "2817 Milton Dr.", + "ShipCity": "Albuquerque", + "ShipRegion": "NM", + "ShipPostalCode": "87110", + "ShipCountry": "USA" + }, { + "OrderID": 10316, + "CustomerID": "RATTC", + "EmployeeID": 1, + "OrderDate": "1996-09-27T00:00:00", + "RequiredDate": "1996-10-25T00:00:00", + "ShippedDate": "1996-10-08T00:00:00", + "ShipVia": 3, + "Freight": "150.1500", + "ShipName": "Rattlesnake Canyon Grocery", + "ShipAddress": "2817 Milton Dr.", + "ShipCity": "Albuquerque", + "ShipRegion": "NM", + "ShipPostalCode": "87110", + "ShipCountry": "USA" + }, { + "OrderID": 10325, + "CustomerID": "KOENE", + "EmployeeID": 1, + "OrderDate": "1996-10-09T00:00:00", + "RequiredDate": "1996-10-23T00:00:00", + "ShippedDate": "1996-10-14T00:00:00", + "ShipVia": 3, + "Freight": "64.8600", + "ShipName": "K\u00f6niglich Essen", + "ShipAddress": "Maubelstr. 90", + "ShipCity": "Brandenburg", + "ShipRegion": null, + "ShipPostalCode": "14776", + "ShipCountry": "Germany" + }, { + "OrderID": 10340, + "CustomerID": "BONAP", + "EmployeeID": 1, + "OrderDate": "1996-10-29T00:00:00", + "RequiredDate": "1996-11-26T00:00:00", + "ShippedDate": "1996-11-08T00:00:00", + "ShipVia": 3, + "Freight": "166.3100", + "ShipName": "Bon app'", + "ShipAddress": "12, rue des Bouchers", + "ShipCity": "Marseille", + "ShipRegion": null, + "ShipPostalCode": "13008", + "ShipCountry": "France" + }, { + "OrderID": 10351, + "CustomerID": "ERNSH", + "EmployeeID": 1, + "OrderDate": "1996-11-11T00:00:00", + "RequiredDate": "1996-12-09T00:00:00", + "ShippedDate": "1996-11-20T00:00:00", + "ShipVia": 1, + "Freight": "162.3300", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10357, + "CustomerID": "LILAS", + "EmployeeID": 1, + "OrderDate": "1996-11-19T00:00:00", + "RequiredDate": "1996-12-17T00:00:00", + "ShippedDate": "1996-12-02T00:00:00", + "ShipVia": 3, + "Freight": "34.8800", + "ShipName": "LILA-Supermercado", + "ShipAddress": "Carrera 52 con Ave. Bol\u00edvar #65-98 Llano Largo", + "ShipCity": "Barquisimeto", + "ShipRegion": "Lara", + "ShipPostalCode": "3508", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10361, + "CustomerID": "QUICK", + "EmployeeID": 1, + "OrderDate": "1996-11-22T00:00:00", + "RequiredDate": "1996-12-20T00:00:00", + "ShippedDate": "1996-12-03T00:00:00", + "ShipVia": 2, + "Freight": "183.1700", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10364, + "CustomerID": "EASTC", + "EmployeeID": 1, + "OrderDate": "1996-11-26T00:00:00", + "RequiredDate": "1997-01-07T00:00:00", + "ShippedDate": "1996-12-04T00:00:00", + "ShipVia": 1, + "Freight": "71.9700", + "ShipName": "Eastern Connection", + "ShipAddress": "35 King George", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "WX3 6FW", + "ShipCountry": "UK" + }, { + "OrderID": 10371, + "CustomerID": "LAMAI", + "EmployeeID": 1, + "OrderDate": "1996-12-03T00:00:00", + "RequiredDate": "1996-12-31T00:00:00", + "ShippedDate": "1996-12-24T00:00:00", + "ShipVia": 1, + "Freight": "0.4500", + "ShipName": "La maison d'Asie", + "ShipAddress": "1 rue Alsace-Lorraine", + "ShipCity": "Toulouse", + "ShipRegion": null, + "ShipPostalCode": "31000", + "ShipCountry": "France" + }, { + "OrderID": 10374, + "CustomerID": "WOLZA", + "EmployeeID": 1, + "OrderDate": "1996-12-05T00:00:00", + "RequiredDate": "1997-01-02T00:00:00", + "ShippedDate": "1996-12-09T00:00:00", + "ShipVia": 3, + "Freight": "3.9400", + "ShipName": "Wolski Zajazd", + "ShipAddress": "ul. Filtrowa 68", + "ShipCity": "Warszawa", + "ShipRegion": null, + "ShipPostalCode": "01-012", + "ShipCountry": "Poland" + }, { + "OrderID": 10376, + "CustomerID": "MEREP", + "EmployeeID": 1, + "OrderDate": "1996-12-09T00:00:00", + "RequiredDate": "1997-01-06T00:00:00", + "ShippedDate": "1996-12-13T00:00:00", + "ShipVia": 2, + "Freight": "20.3900", + "ShipName": "M\u00e8re Paillarde", + "ShipAddress": "43 rue St. Laurent", + "ShipCity": "Montr\u00e9al", + "ShipRegion": "Qu\u00e9bec", + "ShipPostalCode": "H1J 1C3", + "ShipCountry": "Canada" + }, { + "OrderID": 10377, + "CustomerID": "SEVES", + "EmployeeID": 1, + "OrderDate": "1996-12-09T00:00:00", + "RequiredDate": "1997-01-06T00:00:00", + "ShippedDate": "1996-12-13T00:00:00", + "ShipVia": 3, + "Freight": "22.2100", + "ShipName": "Seven Seas Imports", + "ShipAddress": "90 Wadhurst Rd.", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "OX15 4NB", + "ShipCountry": "UK" + }, { + "OrderID": 10385, + "CustomerID": "SPLIR", + "EmployeeID": 1, + "OrderDate": "1996-12-17T00:00:00", + "RequiredDate": "1997-01-14T00:00:00", + "ShippedDate": "1996-12-23T00:00:00", + "ShipVia": 2, + "Freight": "30.9600", + "ShipName": "Split Rail Beer & Ale", + "ShipAddress": "P.O. Box 555", + "ShipCity": "Lander", + "ShipRegion": "WY", + "ShipPostalCode": "82520", + "ShipCountry": "USA" + }, { + "OrderID": 10387, + "CustomerID": "SANTG", + "EmployeeID": 1, + "OrderDate": "1996-12-18T00:00:00", + "RequiredDate": "1997-01-15T00:00:00", + "ShippedDate": "1996-12-20T00:00:00", + "ShipVia": 2, + "Freight": "93.6300", + "ShipName": "Sant\u00e9 Gourmet", + "ShipAddress": "Erling Skakkes gate 78", + "ShipCity": "Stavern", + "ShipRegion": null, + "ShipPostalCode": "4110", + "ShipCountry": "Norway" + }, { + "OrderID": 10393, + "CustomerID": "SAVEA", + "EmployeeID": 1, + "OrderDate": "1996-12-25T00:00:00", + "RequiredDate": "1997-01-22T00:00:00", + "ShippedDate": "1997-01-03T00:00:00", + "ShipVia": 3, + "Freight": "126.5600", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10394, + "CustomerID": "HUNGC", + "EmployeeID": 1, + "OrderDate": "1996-12-25T00:00:00", + "RequiredDate": "1997-01-22T00:00:00", + "ShippedDate": "1997-01-03T00:00:00", + "ShipVia": 3, + "Freight": "30.3400", + "ShipName": "Hungry Coyote Import Store", + "ShipAddress": "City Center Plaza 516 Main St.", + "ShipCity": "Elgin", + "ShipRegion": "OR", + "ShipPostalCode": "97827", + "ShipCountry": "USA" + }, { + "OrderID": 10396, + "CustomerID": "FRANK", + "EmployeeID": 1, + "OrderDate": "1996-12-27T00:00:00", + "RequiredDate": "1997-01-10T00:00:00", + "ShippedDate": "1997-01-06T00:00:00", + "ShipVia": 3, + "Freight": "135.3500", + "ShipName": "Frankenversand", + "ShipAddress": "Berliner Platz 43", + "ShipCity": "M\u00fcnchen", + "ShipRegion": null, + "ShipPostalCode": "80805", + "ShipCountry": "Germany" + }, { + "OrderID": 10400, + "CustomerID": "EASTC", + "EmployeeID": 1, + "OrderDate": "1997-01-01T00:00:00", + "RequiredDate": "1997-01-29T00:00:00", + "ShippedDate": "1997-01-16T00:00:00", + "ShipVia": 3, + "Freight": "83.9300", + "ShipName": "Eastern Connection", + "ShipAddress": "35 King George", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "WX3 6FW", + "ShipCountry": "UK" + }, { + "OrderID": 10401, + "CustomerID": "RATTC", + "EmployeeID": 1, + "OrderDate": "1997-01-01T00:00:00", + "RequiredDate": "1997-01-29T00:00:00", + "ShippedDate": "1997-01-10T00:00:00", + "ShipVia": 1, + "Freight": "12.5100", + "ShipName": "Rattlesnake Canyon Grocery", + "ShipAddress": "2817 Milton Dr.", + "ShipCity": "Albuquerque", + "ShipRegion": "NM", + "ShipPostalCode": "87110", + "ShipCountry": "USA" + }, { + "OrderID": 10405, + "CustomerID": "LINOD", + "EmployeeID": 1, + "OrderDate": "1997-01-06T00:00:00", + "RequiredDate": "1997-02-03T00:00:00", + "ShippedDate": "1997-01-22T00:00:00", + "ShipVia": 1, + "Freight": "34.8200", + "ShipName": "LINO-Delicateses", + "ShipAddress": "Ave. 5 de Mayo Porlamar", + "ShipCity": "I. de Margarita", + "ShipRegion": "Nueva Esparta", + "ShipPostalCode": "4980", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10453, + "CustomerID": "AROUT", + "EmployeeID": 1, + "OrderDate": "1997-02-21T00:00:00", + "RequiredDate": "1997-03-21T00:00:00", + "ShippedDate": "1997-02-26T00:00:00", + "ShipVia": 2, + "Freight": "25.3600", + "ShipName": "Around the Horn", + "ShipAddress": "Brook Farm Stratford St. Mary", + "ShipCity": "Colchester", + "ShipRegion": "Essex", + "ShipPostalCode": "CO7 6JX", + "ShipCountry": "UK" + }, { + "OrderID": 10461, + "CustomerID": "LILAS", + "EmployeeID": 1, + "OrderDate": "1997-02-28T00:00:00", + "RequiredDate": "1997-03-28T00:00:00", + "ShippedDate": "1997-03-05T00:00:00", + "ShipVia": 3, + "Freight": "148.6100", + "ShipName": "LILA-Supermercado", + "ShipAddress": "Carrera 52 con Ave. Bol\u00edvar #65-98 Llano Largo", + "ShipCity": "Barquisimeto", + "ShipRegion": "Lara", + "ShipPostalCode": "3508", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10465, + "CustomerID": "VAFFE", + "EmployeeID": 1, + "OrderDate": "1997-03-05T00:00:00", + "RequiredDate": "1997-04-02T00:00:00", + "ShippedDate": "1997-03-14T00:00:00", + "ShipVia": 3, + "Freight": "145.0400", + "ShipName": "Vaffeljernet", + "ShipAddress": "Smagsloget 45", + "ShipCity": "\u00c5rhus", + "ShipRegion": null, + "ShipPostalCode": "8200", + "ShipCountry": "Denmark" + }, { + "OrderID": 10469, + "CustomerID": "WHITC", + "EmployeeID": 1, + "OrderDate": "1997-03-10T00:00:00", + "RequiredDate": "1997-04-07T00:00:00", + "ShippedDate": "1997-03-14T00:00:00", + "ShipVia": 1, + "Freight": "60.1800", + "ShipName": "White Clover Markets", + "ShipAddress": "1029 - 12th Ave. S.", + "ShipCity": "Seattle", + "ShipRegion": "WA", + "ShipPostalCode": "98124", + "ShipCountry": "USA" + }, { + "OrderID": 10473, + "CustomerID": "ISLAT", + "EmployeeID": 1, + "OrderDate": "1997-03-13T00:00:00", + "RequiredDate": "1997-03-27T00:00:00", + "ShippedDate": "1997-03-21T00:00:00", + "ShipVia": 3, + "Freight": "16.3700", + "ShipName": "Island Trading", + "ShipAddress": "Garden House Crowther Way", + "ShipCity": "Cowes", + "ShipRegion": "Isle of Wight", + "ShipPostalCode": "PO31 7PJ", + "ShipCountry": "UK" + }, { + "OrderID": 10482, + "CustomerID": "LAZYK", + "EmployeeID": 1, + "OrderDate": "1997-03-21T00:00:00", + "RequiredDate": "1997-04-18T00:00:00", + "ShippedDate": "1997-04-10T00:00:00", + "ShipVia": 3, + "Freight": "7.4800", + "ShipName": "Lazy K Kountry Store", + "ShipAddress": "12 Orchestra Terrace", + "ShipCity": "Walla Walla", + "ShipRegion": "WA", + "ShipPostalCode": "99362", + "ShipCountry": "USA" + }, { + "OrderID": 10486, + "CustomerID": "HILAA", + "EmployeeID": 1, + "OrderDate": "1997-03-26T00:00:00", + "RequiredDate": "1997-04-23T00:00:00", + "ShippedDate": "1997-04-02T00:00:00", + "ShipVia": 2, + "Freight": "30.5300", + "ShipName": "HILARION-Abastos", + "ShipAddress": "Carrera 22 con Ave. Carlos Soublette #8-35", + "ShipCity": "San Crist\u00f3bal", + "ShipRegion": "T\u00e1chira", + "ShipPostalCode": "5022", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10508, + "CustomerID": "OTTIK", + "EmployeeID": 1, + "OrderDate": "1997-04-16T00:00:00", + "RequiredDate": "1997-05-14T00:00:00", + "ShippedDate": "1997-05-13T00:00:00", + "ShipVia": 2, + "Freight": "4.9900", + "ShipName": "Ottilies K\u00e4seladen", + "ShipAddress": "Mehrheimerstr. 369", + "ShipCity": "K\u00f6ln", + "ShipRegion": null, + "ShipPostalCode": "50739", + "ShipCountry": "Germany" + }, { + "OrderID": 10524, + "CustomerID": "BERGS", + "EmployeeID": 1, + "OrderDate": "1997-05-01T00:00:00", + "RequiredDate": "1997-05-29T00:00:00", + "ShippedDate": "1997-05-07T00:00:00", + "ShipVia": 2, + "Freight": "244.7900", + "ShipName": "Berglunds snabbk\u00f6p", + "ShipAddress": "Berguvsv\u00e4gen 8", + "ShipCity": "Lule\u00e5", + "ShipRegion": null, + "ShipPostalCode": "S-958 22", + "ShipCountry": "Sweden" + }, { + "OrderID": 10525, + "CustomerID": "BONAP", + "EmployeeID": 1, + "OrderDate": "1997-05-02T00:00:00", + "RequiredDate": "1997-05-30T00:00:00", + "ShippedDate": "1997-05-23T00:00:00", + "ShipVia": 2, + "Freight": "11.0600", + "ShipName": "Bon app'", + "ShipAddress": "12, rue des Bouchers", + "ShipCity": "Marseille", + "ShipRegion": null, + "ShipPostalCode": "13008", + "ShipCountry": "France" + }, { + "OrderID": 10537, + "CustomerID": "RICSU", + "EmployeeID": 1, + "OrderDate": "1997-05-14T00:00:00", + "RequiredDate": "1997-05-28T00:00:00", + "ShippedDate": "1997-05-19T00:00:00", + "ShipVia": 1, + "Freight": "78.8500", + "ShipName": "Richter Supermarkt", + "ShipAddress": "Starenweg 5", + "ShipCity": "Gen\u00e8ve", + "ShipRegion": null, + "ShipPostalCode": "1204", + "ShipCountry": "Switzerland" + }, { + "OrderID": 10542, + "CustomerID": "KOENE", + "EmployeeID": 1, + "OrderDate": "1997-05-20T00:00:00", + "RequiredDate": "1997-06-17T00:00:00", + "ShippedDate": "1997-05-26T00:00:00", + "ShipVia": 3, + "Freight": "10.9500", + "ShipName": "K\u00f6niglich Essen", + "ShipAddress": "Maubelstr. 90", + "ShipCity": "Brandenburg", + "ShipRegion": null, + "ShipPostalCode": "14776", + "ShipCountry": "Germany" + }, { + "OrderID": 10546, + "CustomerID": "VICTE", + "EmployeeID": 1, + "OrderDate": "1997-05-23T00:00:00", + "RequiredDate": "1997-06-20T00:00:00", + "ShippedDate": "1997-05-27T00:00:00", + "ShipVia": 3, + "Freight": "194.7200", + "ShipName": "Victuailles en stock", + "ShipAddress": "2, rue du Commerce", + "ShipCity": "Lyon", + "ShipRegion": null, + "ShipPostalCode": "69004", + "ShipCountry": "France" + }, { + "OrderID": 10558, + "CustomerID": "AROUT", + "EmployeeID": 1, + "OrderDate": "1997-06-04T00:00:00", + "RequiredDate": "1997-07-02T00:00:00", + "ShippedDate": "1997-06-10T00:00:00", + "ShipVia": 2, + "Freight": "72.9700", + "ShipName": "Around the Horn", + "ShipAddress": "Brook Farm Stratford St. Mary", + "ShipCity": "Colchester", + "ShipRegion": "Essex", + "ShipPostalCode": "CO7 6JX", + "ShipCountry": "UK" + }, { + "OrderID": 10562, + "CustomerID": "REGGC", + "EmployeeID": 1, + "OrderDate": "1997-06-09T00:00:00", + "RequiredDate": "1997-07-07T00:00:00", + "ShippedDate": "1997-06-12T00:00:00", + "ShipVia": 1, + "Freight": "22.9500", + "ShipName": "Reggiani Caseifici", + "ShipAddress": "Strada Provinciale 124", + "ShipCity": "Reggio Emilia", + "ShipRegion": null, + "ShipPostalCode": "42100", + "ShipCountry": "Italy" + }, { + "OrderID": 10567, + "CustomerID": "HUNGO", + "EmployeeID": 1, + "OrderDate": "1997-06-12T00:00:00", + "RequiredDate": "1997-07-10T00:00:00", + "ShippedDate": "1997-06-17T00:00:00", + "ShipVia": 1, + "Freight": "33.9700", + "ShipName": "Hungry Owl All-Night Grocers", + "ShipAddress": "8 Johnstown Road", + "ShipCity": "Cork", + "ShipRegion": "Co. Cork", + "ShipPostalCode": null, + "ShipCountry": "Ireland" + }, { + "OrderID": 10579, + "CustomerID": "LETSS", + "EmployeeID": 1, + "OrderDate": "1997-06-25T00:00:00", + "RequiredDate": "1997-07-23T00:00:00", + "ShippedDate": "1997-07-04T00:00:00", + "ShipVia": 2, + "Freight": "13.7300", + "ShipName": "Let's Stop N Shop", + "ShipAddress": "87 Polk St. Suite 5", + "ShipCity": "San Francisco", + "ShipRegion": "CA", + "ShipPostalCode": "94117", + "ShipCountry": "USA" + }, { + "OrderID": 10587, + "CustomerID": "QUEDE", + "EmployeeID": 1, + "OrderDate": "1997-07-02T00:00:00", + "RequiredDate": "1997-07-30T00:00:00", + "ShippedDate": "1997-07-09T00:00:00", + "ShipVia": 1, + "Freight": "62.5200", + "ShipName": "Que Del\u00edcia", + "ShipAddress": "Rua da Panificadora, 12", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "02389-673", + "ShipCountry": "Brazil" + }, { + "OrderID": 10591, + "CustomerID": "VAFFE", + "EmployeeID": 1, + "OrderDate": "1997-07-07T00:00:00", + "RequiredDate": "1997-07-21T00:00:00", + "ShippedDate": "1997-07-16T00:00:00", + "ShipVia": 1, + "Freight": "55.9200", + "ShipName": "Vaffeljernet", + "ShipAddress": "Smagsloget 45", + "ShipCity": "\u00c5rhus", + "ShipRegion": null, + "ShipPostalCode": "8200", + "ShipCountry": "Denmark" + }, { + "OrderID": 10598, + "CustomerID": "RATTC", + "EmployeeID": 1, + "OrderDate": "1997-07-14T00:00:00", + "RequiredDate": "1997-08-11T00:00:00", + "ShippedDate": "1997-07-18T00:00:00", + "ShipVia": 3, + "Freight": "44.4200", + "ShipName": "Rattlesnake Canyon Grocery", + "ShipAddress": "2817 Milton Dr.", + "ShipCity": "Albuquerque", + "ShipRegion": "NM", + "ShipPostalCode": "87110", + "ShipCountry": "USA" + }, { + "OrderID": 10604, + "CustomerID": "FURIB", + "EmployeeID": 1, + "OrderDate": "1997-07-18T00:00:00", + "RequiredDate": "1997-08-15T00:00:00", + "ShippedDate": "1997-07-29T00:00:00", + "ShipVia": 1, + "Freight": "7.4600", + "ShipName": "Furia Bacalhau e Frutos do Mar", + "ShipAddress": "Jardim das rosas n. 32", + "ShipCity": "Lisboa", + "ShipRegion": null, + "ShipPostalCode": "1675", + "ShipCountry": "Portugal" + }, { + "OrderID": 10605, + "CustomerID": "MEREP", + "EmployeeID": 1, + "OrderDate": "1997-07-21T00:00:00", + "RequiredDate": "1997-08-18T00:00:00", + "ShippedDate": "1997-07-29T00:00:00", + "ShipVia": 2, + "Freight": "379.1300", + "ShipName": "M\u00e8re Paillarde", + "ShipAddress": "43 rue St. Laurent", + "ShipCity": "Montr\u00e9al", + "ShipRegion": "Qu\u00e9bec", + "ShipPostalCode": "H1J 1C3", + "ShipCountry": "Canada" + }, { + "OrderID": 10612, + "CustomerID": "SAVEA", + "EmployeeID": 1, + "OrderDate": "1997-07-28T00:00:00", + "RequiredDate": "1997-08-25T00:00:00", + "ShippedDate": "1997-08-01T00:00:00", + "ShipVia": 2, + "Freight": "544.0800", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10616, + "CustomerID": "GREAL", + "EmployeeID": 1, + "OrderDate": "1997-07-31T00:00:00", + "RequiredDate": "1997-08-28T00:00:00", + "ShippedDate": "1997-08-05T00:00:00", + "ShipVia": 2, + "Freight": "116.5300", + "ShipName": "Great Lakes Food Market", + "ShipAddress": "2732 Baker Blvd.", + "ShipCity": "Eugene", + "ShipRegion": "OR", + "ShipPostalCode": "97403", + "ShipCountry": "USA" + }, { + "OrderID": 10618, + "CustomerID": "MEREP", + "EmployeeID": 1, + "OrderDate": "1997-08-01T00:00:00", + "RequiredDate": "1997-09-12T00:00:00", + "ShippedDate": "1997-08-08T00:00:00", + "ShipVia": 1, + "Freight": "154.6800", + "ShipName": "M\u00e8re Paillarde", + "ShipAddress": "43 rue St. Laurent", + "ShipCity": "Montr\u00e9al", + "ShipRegion": "Qu\u00e9bec", + "ShipPostalCode": "H1J 1C3", + "ShipCountry": "Canada" + }, { + "OrderID": 10626, + "CustomerID": "BERGS", + "EmployeeID": 1, + "OrderDate": "1997-08-11T00:00:00", + "RequiredDate": "1997-09-08T00:00:00", + "ShippedDate": "1997-08-20T00:00:00", + "ShipVia": 2, + "Freight": "138.6900", + "ShipName": "Berglunds snabbk\u00f6p", + "ShipAddress": "Berguvsv\u00e4gen 8", + "ShipCity": "Lule\u00e5", + "ShipRegion": null, + "ShipPostalCode": "S-958 22", + "ShipCountry": "Sweden" + }, { + "OrderID": 10630, + "CustomerID": "KOENE", + "EmployeeID": 1, + "OrderDate": "1997-08-13T00:00:00", + "RequiredDate": "1997-09-10T00:00:00", + "ShippedDate": "1997-08-19T00:00:00", + "ShipVia": 2, + "Freight": "32.3500", + "ShipName": "K\u00f6niglich Essen", + "ShipAddress": "Maubelstr. 90", + "ShipCity": "Brandenburg", + "ShipRegion": null, + "ShipPostalCode": "14776", + "ShipCountry": "Germany" + }, { + "OrderID": 10653, + "CustomerID": "FRANK", + "EmployeeID": 1, + "OrderDate": "1997-09-02T00:00:00", + "RequiredDate": "1997-09-30T00:00:00", + "ShippedDate": "1997-09-19T00:00:00", + "ShipVia": 1, + "Freight": "93.2500", + "ShipName": "Frankenversand", + "ShipAddress": "Berliner Platz 43", + "ShipCity": "M\u00fcnchen", + "ShipRegion": null, + "ShipPostalCode": "80805", + "ShipCountry": "Germany" + }, { + "OrderID": 10655, + "CustomerID": "REGGC", + "EmployeeID": 1, + "OrderDate": "1997-09-03T00:00:00", + "RequiredDate": "1997-10-01T00:00:00", + "ShippedDate": "1997-09-11T00:00:00", + "ShipVia": 2, + "Freight": "4.4100", + "ShipName": "Reggiani Caseifici", + "ShipAddress": "Strada Provinciale 124", + "ShipCity": "Reggio Emilia", + "ShipRegion": null, + "ShipPostalCode": "42100", + "ShipCountry": "Italy" + }, { + "OrderID": 10664, + "CustomerID": "FURIB", + "EmployeeID": 1, + "OrderDate": "1997-09-10T00:00:00", + "RequiredDate": "1997-10-08T00:00:00", + "ShippedDate": "1997-09-19T00:00:00", + "ShipVia": 3, + "Freight": "1.2700", + "ShipName": "Furia Bacalhau e Frutos do Mar", + "ShipAddress": "Jardim das rosas n. 32", + "ShipCity": "Lisboa", + "ShipRegion": null, + "ShipPostalCode": "1675", + "ShipCountry": "Portugal" + }, { + "OrderID": 10665, + "CustomerID": "LONEP", + "EmployeeID": 1, + "OrderDate": "1997-09-11T00:00:00", + "RequiredDate": "1997-10-09T00:00:00", + "ShippedDate": "1997-09-17T00:00:00", + "ShipVia": 2, + "Freight": "26.3100", + "ShipName": "Lonesome Pine Restaurant", + "ShipAddress": "89 Chiaroscuro Rd.", + "ShipCity": "Portland", + "ShipRegion": "OR", + "ShipPostalCode": "97219", + "ShipCountry": "USA" + }, { + "OrderID": 10668, + "CustomerID": "WANDK", + "EmployeeID": 1, + "OrderDate": "1997-09-15T00:00:00", + "RequiredDate": "1997-10-13T00:00:00", + "ShippedDate": "1997-09-23T00:00:00", + "ShipVia": 2, + "Freight": "47.2200", + "ShipName": "Die Wandernde Kuh", + "ShipAddress": "Adenauerallee 900", + "ShipCity": "Stuttgart", + "ShipRegion": null, + "ShipPostalCode": "70563", + "ShipCountry": "Germany" + }, { + "OrderID": 10671, + "CustomerID": "FRANR", + "EmployeeID": 1, + "OrderDate": "1997-09-17T00:00:00", + "RequiredDate": "1997-10-15T00:00:00", + "ShippedDate": "1997-09-24T00:00:00", + "ShipVia": 1, + "Freight": "30.3400", + "ShipName": "France restauration", + "ShipAddress": "54, rue Royale", + "ShipCity": "Nantes", + "ShipRegion": null, + "ShipPostalCode": "44000", + "ShipCountry": "France" + }, { + "OrderID": 10677, + "CustomerID": "ANTON", + "EmployeeID": 1, + "OrderDate": "1997-09-22T00:00:00", + "RequiredDate": "1997-10-20T00:00:00", + "ShippedDate": "1997-09-26T00:00:00", + "ShipVia": 3, + "Freight": "4.0300", + "ShipName": "Antonio Moreno Taquer\u00eda", + "ShipAddress": "Mataderos 2312", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05023", + "ShipCountry": "Mexico" + }, { + "OrderID": 10680, + "CustomerID": "OLDWO", + "EmployeeID": 1, + "OrderDate": "1997-09-24T00:00:00", + "RequiredDate": "1997-10-22T00:00:00", + "ShippedDate": "1997-09-26T00:00:00", + "ShipVia": 1, + "Freight": "26.6100", + "ShipName": "Old World Delicatessen", + "ShipAddress": "2743 Bering St.", + "ShipCity": "Anchorage", + "ShipRegion": "AK", + "ShipPostalCode": "99508", + "ShipCountry": "USA" + }, { + "OrderID": 10689, + "CustomerID": "BERGS", + "EmployeeID": 1, + "OrderDate": "1997-10-01T00:00:00", + "RequiredDate": "1997-10-29T00:00:00", + "ShippedDate": "1997-10-07T00:00:00", + "ShipVia": 2, + "Freight": "13.4200", + "ShipName": "Berglunds snabbk\u00f6p", + "ShipAddress": "Berguvsv\u00e4gen 8", + "ShipCity": "Lule\u00e5", + "ShipRegion": null, + "ShipPostalCode": "S-958 22", + "ShipCountry": "Sweden" + }, { + "OrderID": 10690, + "CustomerID": "HANAR", + "EmployeeID": 1, + "OrderDate": "1997-10-02T00:00:00", + "RequiredDate": "1997-10-30T00:00:00", + "ShippedDate": "1997-10-03T00:00:00", + "ShipVia": 1, + "Freight": "15.8000", + "ShipName": "Hanari Carnes", + "ShipAddress": "Rua do Pa\u00e7o, 67", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "05454-876", + "ShipCountry": "Brazil" + }, { + "OrderID": 10709, + "CustomerID": "GOURL", + "EmployeeID": 1, + "OrderDate": "1997-10-17T00:00:00", + "RequiredDate": "1997-11-14T00:00:00", + "ShippedDate": "1997-11-20T00:00:00", + "ShipVia": 3, + "Freight": "210.8000", + "ShipName": "Gourmet Lanchonetes", + "ShipAddress": "Av. Brasil, 442", + "ShipCity": "Campinas", + "ShipRegion": "SP", + "ShipPostalCode": "04876-786", + "ShipCountry": "Brazil" + }, { + "OrderID": 10710, + "CustomerID": "FRANS", + "EmployeeID": 1, + "OrderDate": "1997-10-20T00:00:00", + "RequiredDate": "1997-11-17T00:00:00", + "ShippedDate": "1997-10-23T00:00:00", + "ShipVia": 1, + "Freight": "4.9800", + "ShipName": "Franchi S.p.A.", + "ShipAddress": "Via Monte Bianco 34", + "ShipCity": "Torino", + "ShipRegion": null, + "ShipPostalCode": "10100", + "ShipCountry": "Italy" + }, { + "OrderID": 10713, + "CustomerID": "SAVEA", + "EmployeeID": 1, + "OrderDate": "1997-10-22T00:00:00", + "RequiredDate": "1997-11-19T00:00:00", + "ShippedDate": "1997-10-24T00:00:00", + "ShipVia": 1, + "Freight": "167.0500", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10717, + "CustomerID": "FRANK", + "EmployeeID": 1, + "OrderDate": "1997-10-24T00:00:00", + "RequiredDate": "1997-11-21T00:00:00", + "ShippedDate": "1997-10-29T00:00:00", + "ShipVia": 2, + "Freight": "59.2500", + "ShipName": "Frankenversand", + "ShipAddress": "Berliner Platz 43", + "ShipCity": "M\u00fcnchen", + "ShipRegion": null, + "ShipPostalCode": "80805", + "ShipCountry": "Germany" + }, { + "OrderID": 10718, + "CustomerID": "KOENE", + "EmployeeID": 1, + "OrderDate": "1997-10-27T00:00:00", + "RequiredDate": "1997-11-24T00:00:00", + "ShippedDate": "1997-10-29T00:00:00", + "ShipVia": 3, + "Freight": "170.8800", + "ShipName": "K\u00f6niglich Essen", + "ShipAddress": "Maubelstr. 90", + "ShipCity": "Brandenburg", + "ShipRegion": null, + "ShipPostalCode": "14776", + "ShipCountry": "Germany" + }, { + "OrderID": 10733, + "CustomerID": "BERGS", + "EmployeeID": 1, + "OrderDate": "1997-11-07T00:00:00", + "RequiredDate": "1997-12-05T00:00:00", + "ShippedDate": "1997-11-10T00:00:00", + "ShipVia": 3, + "Freight": "110.1100", + "ShipName": "Berglunds snabbk\u00f6p", + "ShipAddress": "Berguvsv\u00e4gen 8", + "ShipCity": "Lule\u00e5", + "ShipRegion": null, + "ShipPostalCode": "S-958 22", + "ShipCountry": "Sweden" + }, { + "OrderID": 10743, + "CustomerID": "AROUT", + "EmployeeID": 1, + "OrderDate": "1997-11-17T00:00:00", + "RequiredDate": "1997-12-15T00:00:00", + "ShippedDate": "1997-11-21T00:00:00", + "ShipVia": 2, + "Freight": "23.7200", + "ShipName": "Around the Horn", + "ShipAddress": "Brook Farm Stratford St. Mary", + "ShipCity": "Colchester", + "ShipRegion": "Essex", + "ShipPostalCode": "CO7 6JX", + "ShipCountry": "UK" + }, { + "OrderID": 10746, + "CustomerID": "CHOPS", + "EmployeeID": 1, + "OrderDate": "1997-11-19T00:00:00", + "RequiredDate": "1997-12-17T00:00:00", + "ShippedDate": "1997-11-21T00:00:00", + "ShipVia": 3, + "Freight": "31.4300", + "ShipName": "Chop-suey Chinese", + "ShipAddress": "Hauptstr. 31", + "ShipCity": "Bern", + "ShipRegion": null, + "ShipPostalCode": "3012", + "ShipCountry": "Switzerland" + }, { + "OrderID": 10773, + "CustomerID": "ERNSH", + "EmployeeID": 1, + "OrderDate": "1997-12-11T00:00:00", + "RequiredDate": "1998-01-08T00:00:00", + "ShippedDate": "1997-12-16T00:00:00", + "ShipVia": 3, + "Freight": "96.4300", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10776, + "CustomerID": "ERNSH", + "EmployeeID": 1, + "OrderDate": "1997-12-15T00:00:00", + "RequiredDate": "1998-01-12T00:00:00", + "ShippedDate": "1997-12-18T00:00:00", + "ShipVia": 3, + "Freight": "351.5300", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10785, + "CustomerID": "GROSR", + "EmployeeID": 1, + "OrderDate": "1997-12-18T00:00:00", + "RequiredDate": "1998-01-15T00:00:00", + "ShippedDate": "1997-12-24T00:00:00", + "ShipVia": 3, + "Freight": "1.5100", + "ShipName": "GROSELLA-Restaurante", + "ShipAddress": "5\u00aa Ave. Los Palos Grandes", + "ShipCity": "Caracas", + "ShipRegion": "DF", + "ShipPostalCode": "1081", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10788, + "CustomerID": "QUICK", + "EmployeeID": 1, + "OrderDate": "1997-12-22T00:00:00", + "RequiredDate": "1998-01-19T00:00:00", + "ShippedDate": "1998-01-19T00:00:00", + "ShipVia": 2, + "Freight": "42.7000", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10789, + "CustomerID": "FOLIG", + "EmployeeID": 1, + "OrderDate": "1997-12-22T00:00:00", + "RequiredDate": "1998-01-19T00:00:00", + "ShippedDate": "1997-12-31T00:00:00", + "ShipVia": 2, + "Freight": "100.6000", + "ShipName": "Folies gourmandes", + "ShipAddress": "184, chauss\u00e9e de Tournai", + "ShipCity": "Lille", + "ShipRegion": null, + "ShipPostalCode": "59000", + "ShipCountry": "France" + }, { + "OrderID": 10792, + "CustomerID": "WOLZA", + "EmployeeID": 1, + "OrderDate": "1997-12-23T00:00:00", + "RequiredDate": "1998-01-20T00:00:00", + "ShippedDate": "1997-12-31T00:00:00", + "ShipVia": 3, + "Freight": "23.7900", + "ShipName": "Wolski Zajazd", + "ShipAddress": "ul. Filtrowa 68", + "ShipCity": "Warszawa", + "ShipRegion": null, + "ShipPostalCode": "01-012", + "ShipCountry": "Poland" + }, { + "OrderID": 10800, + "CustomerID": "SEVES", + "EmployeeID": 1, + "OrderDate": "1997-12-26T00:00:00", + "RequiredDate": "1998-01-23T00:00:00", + "ShippedDate": "1998-01-05T00:00:00", + "ShipVia": 3, + "Freight": "137.4400", + "ShipName": "Seven Seas Imports", + "ShipAddress": "90 Wadhurst Rd.", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "OX15 4NB", + "ShipCountry": "UK" + }, { + "OrderID": 10813, + "CustomerID": "RICAR", + "EmployeeID": 1, + "OrderDate": "1998-01-05T00:00:00", + "RequiredDate": "1998-02-02T00:00:00", + "ShippedDate": "1998-01-09T00:00:00", + "ShipVia": 1, + "Freight": "47.3800", + "ShipName": "Ricardo Adocicados", + "ShipAddress": "Av. Copacabana, 267", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "02389-890", + "ShipCountry": "Brazil" + }, { + "OrderID": 10821, + "CustomerID": "SPLIR", + "EmployeeID": 1, + "OrderDate": "1998-01-08T00:00:00", + "RequiredDate": "1998-02-05T00:00:00", + "ShippedDate": "1998-01-15T00:00:00", + "ShipVia": 1, + "Freight": "36.6800", + "ShipName": "Split Rail Beer & Ale", + "ShipAddress": "P.O. Box 555", + "ShipCity": "Lander", + "ShipRegion": "WY", + "ShipPostalCode": "82520", + "ShipCountry": "USA" + }, { + "OrderID": 10825, + "CustomerID": "DRACD", + "EmployeeID": 1, + "OrderDate": "1998-01-09T00:00:00", + "RequiredDate": "1998-02-06T00:00:00", + "ShippedDate": "1998-01-14T00:00:00", + "ShipVia": 1, + "Freight": "79.2500", + "ShipName": "Drachenblut Delikatessen", + "ShipAddress": "Walserweg 21", + "ShipCity": "Aachen", + "ShipRegion": null, + "ShipPostalCode": "52066", + "ShipCountry": "Germany" + }, { + "OrderID": 10827, + "CustomerID": "BONAP", + "EmployeeID": 1, + "OrderDate": "1998-01-12T00:00:00", + "RequiredDate": "1998-01-26T00:00:00", + "ShippedDate": "1998-02-06T00:00:00", + "ShipVia": 2, + "Freight": "63.5400", + "ShipName": "Bon app'", + "ShipAddress": "12, rue des Bouchers", + "ShipCity": "Marseille", + "ShipRegion": null, + "ShipPostalCode": "13008", + "ShipCountry": "France" + }, { + "OrderID": 10834, + "CustomerID": "TRADH", + "EmployeeID": 1, + "OrderDate": "1998-01-15T00:00:00", + "RequiredDate": "1998-02-12T00:00:00", + "ShippedDate": "1998-01-19T00:00:00", + "ShipVia": 3, + "Freight": "29.7800", + "ShipName": "Tradi\u00e7ao Hipermercados", + "ShipAddress": "Av. In\u00eas de Castro, 414", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05634-030", + "ShipCountry": "Brazil" + }, { + "OrderID": 10835, + "CustomerID": "ALFKI", + "EmployeeID": 1, + "OrderDate": "1998-01-15T00:00:00", + "RequiredDate": "1998-02-12T00:00:00", + "ShippedDate": "1998-01-21T00:00:00", + "ShipVia": 3, + "Freight": "69.5300", + "ShipName": "Alfred's Futterkiste", + "ShipAddress": "Obere Str. 57", + "ShipCity": "Berlin", + "ShipRegion": null, + "ShipPostalCode": "12209", + "ShipCountry": "Germany" + }, { + "OrderID": 10842, + "CustomerID": "TORTU", + "EmployeeID": 1, + "OrderDate": "1998-01-20T00:00:00", + "RequiredDate": "1998-02-17T00:00:00", + "ShippedDate": "1998-01-29T00:00:00", + "ShipVia": 3, + "Freight": "54.4200", + "ShipName": "Tortuga Restaurante", + "ShipAddress": "Avda. Azteca 123", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05033", + "ShipCountry": "Mexico" + }, { + "OrderID": 10850, + "CustomerID": "VICTE", + "EmployeeID": 1, + "OrderDate": "1998-01-23T00:00:00", + "RequiredDate": "1998-03-06T00:00:00", + "ShippedDate": "1998-01-30T00:00:00", + "ShipVia": 1, + "Freight": "49.1900", + "ShipName": "Victuailles en stock", + "ShipAddress": "2, rue du Commerce", + "ShipCity": "Lyon", + "ShipRegion": null, + "ShipPostalCode": "69004", + "ShipCountry": "France" + }, { + "OrderID": 10859, + "CustomerID": "FRANK", + "EmployeeID": 1, + "OrderDate": "1998-01-29T00:00:00", + "RequiredDate": "1998-02-26T00:00:00", + "ShippedDate": "1998-02-02T00:00:00", + "ShipVia": 2, + "Freight": "76.1000", + "ShipName": "Frankenversand", + "ShipAddress": "Berliner Platz 43", + "ShipCity": "M\u00fcnchen", + "ShipRegion": null, + "ShipPostalCode": "80805", + "ShipCountry": "Germany" + }, { + "OrderID": 10877, + "CustomerID": "RICAR", + "EmployeeID": 1, + "OrderDate": "1998-02-09T00:00:00", + "RequiredDate": "1998-03-09T00:00:00", + "ShippedDate": "1998-02-19T00:00:00", + "ShipVia": 1, + "Freight": "38.0600", + "ShipName": "Ricardo Adocicados", + "ShipAddress": "Av. Copacabana, 267", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "02389-890", + "ShipCountry": "Brazil" + }, { + "OrderID": 10886, + "CustomerID": "HANAR", + "EmployeeID": 1, + "OrderDate": "1998-02-13T00:00:00", + "RequiredDate": "1998-03-13T00:00:00", + "ShippedDate": "1998-03-02T00:00:00", + "ShipVia": 1, + "Freight": "4.9900", + "ShipName": "Hanari Carnes", + "ShipAddress": "Rua do Pa\u00e7o, 67", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "05454-876", + "ShipCountry": "Brazil" + }, { + "OrderID": 10888, + "CustomerID": "GODOS", + "EmployeeID": 1, + "OrderDate": "1998-02-16T00:00:00", + "RequiredDate": "1998-03-16T00:00:00", + "ShippedDate": "1998-02-23T00:00:00", + "ShipVia": 2, + "Freight": "51.8700", + "ShipName": "Godos Cocina T\u00edpica", + "ShipAddress": "C/ Romero, 33", + "ShipCity": "Sevilla", + "ShipRegion": null, + "ShipPostalCode": "41101", + "ShipCountry": "Spain" + }, { + "OrderID": 10894, + "CustomerID": "SAVEA", + "EmployeeID": 1, + "OrderDate": "1998-02-18T00:00:00", + "RequiredDate": "1998-03-18T00:00:00", + "ShippedDate": "1998-02-20T00:00:00", + "ShipVia": 1, + "Freight": "116.1300", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10900, + "CustomerID": "WELLI", + "EmployeeID": 1, + "OrderDate": "1998-02-20T00:00:00", + "RequiredDate": "1998-03-20T00:00:00", + "ShippedDate": "1998-03-04T00:00:00", + "ShipVia": 2, + "Freight": "1.6600", + "ShipName": "Wellington Importadora", + "ShipAddress": "Rua do Mercado, 12", + "ShipCity": "Resende", + "ShipRegion": "SP", + "ShipPostalCode": "08737-363", + "ShipCountry": "Brazil" + }, { + "OrderID": 10902, + "CustomerID": "FOLKO", + "EmployeeID": 1, + "OrderDate": "1998-02-23T00:00:00", + "RequiredDate": "1998-03-23T00:00:00", + "ShippedDate": "1998-03-03T00:00:00", + "ShipVia": 1, + "Freight": "44.1500", + "ShipName": "Folk och f\u00e4 HB", + "ShipAddress": "\u00c5kergatan 24", + "ShipCity": "Br\u00e4cke", + "ShipRegion": null, + "ShipPostalCode": "S-844 67", + "ShipCountry": "Sweden" + }, { + "OrderID": 10909, + "CustomerID": "SANTG", + "EmployeeID": 1, + "OrderDate": "1998-02-26T00:00:00", + "RequiredDate": "1998-03-26T00:00:00", + "ShippedDate": "1998-03-10T00:00:00", + "ShipVia": 2, + "Freight": "53.0500", + "ShipName": "Sant\u00e9 Gourmet", + "ShipAddress": "Erling Skakkes gate 78", + "ShipCity": "Stavern", + "ShipRegion": null, + "ShipPostalCode": "4110", + "ShipCountry": "Norway" + }, { + "OrderID": 10910, + "CustomerID": "WILMK", + "EmployeeID": 1, + "OrderDate": "1998-02-26T00:00:00", + "RequiredDate": "1998-03-26T00:00:00", + "ShippedDate": "1998-03-04T00:00:00", + "ShipVia": 3, + "Freight": "38.1100", + "ShipName": "Wilman Kala", + "ShipAddress": "Keskuskatu 45", + "ShipCity": "Helsinki", + "ShipRegion": null, + "ShipPostalCode": "21240", + "ShipCountry": "Finland" + }, { + "OrderID": 10916, + "CustomerID": "RANCH", + "EmployeeID": 1, + "OrderDate": "1998-02-27T00:00:00", + "RequiredDate": "1998-03-27T00:00:00", + "ShippedDate": "1998-03-09T00:00:00", + "ShipVia": 2, + "Freight": "63.7700", + "ShipName": "Rancho grande", + "ShipAddress": "Av. del Libertador 900", + "ShipCity": "Buenos Aires", + "ShipRegion": null, + "ShipPostalCode": "1010", + "ShipCountry": "Argentina" + }, { + "OrderID": 10921, + "CustomerID": "VAFFE", + "EmployeeID": 1, + "OrderDate": "1998-03-03T00:00:00", + "RequiredDate": "1998-04-14T00:00:00", + "ShippedDate": "1998-03-09T00:00:00", + "ShipVia": 1, + "Freight": "176.4800", + "ShipName": "Vaffeljernet", + "ShipAddress": "Smagsloget 45", + "ShipCity": "\u00c5rhus", + "ShipRegion": null, + "ShipPostalCode": "8200", + "ShipCountry": "Denmark" + }, { + "OrderID": 10928, + "CustomerID": "GALED", + "EmployeeID": 1, + "OrderDate": "1998-03-05T00:00:00", + "RequiredDate": "1998-04-02T00:00:00", + "ShippedDate": "1998-03-18T00:00:00", + "ShipVia": 1, + "Freight": "1.3600", + "ShipName": "Galer\u00eda del gastron\u00f3mo", + "ShipAddress": "Rambla de Catalu\u00f1a, 23", + "ShipCity": "Barcelona", + "ShipRegion": null, + "ShipPostalCode": "8022", + "ShipCountry": "Spain" + }, { + "OrderID": 10946, + "CustomerID": "VAFFE", + "EmployeeID": 1, + "OrderDate": "1998-03-12T00:00:00", + "RequiredDate": "1998-04-09T00:00:00", + "ShippedDate": "1998-03-19T00:00:00", + "ShipVia": 2, + "Freight": "27.2000", + "ShipName": "Vaffeljernet", + "ShipAddress": "Smagsloget 45", + "ShipCity": "\u00c5rhus", + "ShipRegion": null, + "ShipPostalCode": "8200", + "ShipCountry": "Denmark" + }, { + "OrderID": 10950, + "CustomerID": "MAGAA", + "EmployeeID": 1, + "OrderDate": "1998-03-16T00:00:00", + "RequiredDate": "1998-04-13T00:00:00", + "ShippedDate": "1998-03-23T00:00:00", + "ShipVia": 2, + "Freight": "2.5000", + "ShipName": "Magazzini Alimentari Riuniti", + "ShipAddress": "Via Ludovico il Moro 22", + "ShipCity": "Bergamo", + "ShipRegion": null, + "ShipPostalCode": "24100", + "ShipCountry": "Italy" + }, { + "OrderID": 10952, + "CustomerID": "ALFKI", + "EmployeeID": 1, + "OrderDate": "1998-03-16T00:00:00", + "RequiredDate": "1998-04-27T00:00:00", + "ShippedDate": "1998-03-24T00:00:00", + "ShipVia": 1, + "Freight": "40.4200", + "ShipName": "Alfred's Futterkiste", + "ShipAddress": "Obere Str. 57", + "ShipCity": "Berlin", + "ShipRegion": null, + "ShipPostalCode": "12209", + "ShipCountry": "Germany" + }, { + "OrderID": 10968, + "CustomerID": "ERNSH", + "EmployeeID": 1, + "OrderDate": "1998-03-23T00:00:00", + "RequiredDate": "1998-04-20T00:00:00", + "ShippedDate": "1998-04-01T00:00:00", + "ShipVia": 3, + "Freight": "74.6000", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10969, + "CustomerID": "COMMI", + "EmployeeID": 1, + "OrderDate": "1998-03-23T00:00:00", + "RequiredDate": "1998-04-20T00:00:00", + "ShippedDate": "1998-03-30T00:00:00", + "ShipVia": 2, + "Freight": "0.2100", + "ShipName": "Com\u00e9rcio Mineiro", + "ShipAddress": "Av. dos Lus\u00edadas, 23", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05432-043", + "ShipCountry": "Brazil" + }, { + "OrderID": 10975, + "CustomerID": "BOTTM", + "EmployeeID": 1, + "OrderDate": "1998-03-25T00:00:00", + "RequiredDate": "1998-04-22T00:00:00", + "ShippedDate": "1998-03-27T00:00:00", + "ShipVia": 3, + "Freight": "32.2700", + "ShipName": "Bottom-Dollar Markets", + "ShipAddress": "23 Tsawassen Blvd.", + "ShipCity": "Tsawassen", + "ShipRegion": "BC", + "ShipPostalCode": "T2F 8M4", + "ShipCountry": "Canada" + }, { + "OrderID": 10976, + "CustomerID": "HILAA", + "EmployeeID": 1, + "OrderDate": "1998-03-25T00:00:00", + "RequiredDate": "1998-05-06T00:00:00", + "ShippedDate": "1998-04-03T00:00:00", + "ShipVia": 1, + "Freight": "37.9700", + "ShipName": "HILARION-Abastos", + "ShipAddress": "Carrera 22 con Ave. Carlos Soublette #8-35", + "ShipCity": "San Crist\u00f3bal", + "ShipRegion": "T\u00e1chira", + "ShipPostalCode": "5022", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10981, + "CustomerID": "HANAR", + "EmployeeID": 1, + "OrderDate": "1998-03-27T00:00:00", + "RequiredDate": "1998-04-24T00:00:00", + "ShippedDate": "1998-04-02T00:00:00", + "ShipVia": 2, + "Freight": "193.3700", + "ShipName": "Hanari Carnes", + "ShipAddress": "Rua do Pa\u00e7o, 67", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "05454-876", + "ShipCountry": "Brazil" + }, { + "OrderID": 10984, + "CustomerID": "SAVEA", + "EmployeeID": 1, + "OrderDate": "1998-03-30T00:00:00", + "RequiredDate": "1998-04-27T00:00:00", + "ShippedDate": "1998-04-03T00:00:00", + "ShipVia": 3, + "Freight": "211.2200", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10991, + "CustomerID": "QUICK", + "EmployeeID": 1, + "OrderDate": "1998-04-01T00:00:00", + "RequiredDate": "1998-04-29T00:00:00", + "ShippedDate": "1998-04-07T00:00:00", + "ShipVia": 1, + "Freight": "38.5100", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10992, + "CustomerID": "THEBI", + "EmployeeID": 1, + "OrderDate": "1998-04-01T00:00:00", + "RequiredDate": "1998-04-29T00:00:00", + "ShippedDate": "1998-04-03T00:00:00", + "ShipVia": 3, + "Freight": "4.2700", + "ShipName": "The Big Cheese", + "ShipAddress": "89 Jefferson Way Suite 2", + "ShipCity": "Portland", + "ShipRegion": "OR", + "ShipPostalCode": "97201", + "ShipCountry": "USA" + }, { + "OrderID": 10995, + "CustomerID": "PERIC", + "EmployeeID": 1, + "OrderDate": "1998-04-02T00:00:00", + "RequiredDate": "1998-04-30T00:00:00", + "ShippedDate": "1998-04-06T00:00:00", + "ShipVia": 3, + "Freight": "46.0000", + "ShipName": "Pericles Comidas cl\u00e1sicas", + "ShipAddress": "Calle Dr. Jorge Cash 321", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05033", + "ShipCountry": "Mexico" + }, { + "OrderID": 11012, + "CustomerID": "FRANK", + "EmployeeID": 1, + "OrderDate": "1998-04-09T00:00:00", + "RequiredDate": "1998-04-23T00:00:00", + "ShippedDate": "1998-04-17T00:00:00", + "ShipVia": 3, + "Freight": "242.9500", + "ShipName": "Frankenversand", + "ShipAddress": "Berliner Platz 43", + "ShipCity": "M\u00fcnchen", + "ShipRegion": null, + "ShipPostalCode": "80805", + "ShipCountry": "Germany" + }, { + "OrderID": 11023, + "CustomerID": "BSBEV", + "EmployeeID": 1, + "OrderDate": "1998-04-14T00:00:00", + "RequiredDate": "1998-04-28T00:00:00", + "ShippedDate": "1998-04-24T00:00:00", + "ShipVia": 2, + "Freight": "123.8300", + "ShipName": "B's Beverages", + "ShipAddress": "Fauntleroy Circus", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "EC2 5NT", + "ShipCountry": "UK" + }, { + "OrderID": 11027, + "CustomerID": "BOTTM", + "EmployeeID": 1, + "OrderDate": "1998-04-16T00:00:00", + "RequiredDate": "1998-05-14T00:00:00", + "ShippedDate": "1998-04-20T00:00:00", + "ShipVia": 1, + "Freight": "52.5200", + "ShipName": "Bottom-Dollar Markets", + "ShipAddress": "23 Tsawassen Blvd.", + "ShipCity": "Tsawassen", + "ShipRegion": "BC", + "ShipPostalCode": "T2F 8M4", + "ShipCountry": "Canada" + }, { + "OrderID": 11038, + "CustomerID": "SUPRD", + "EmployeeID": 1, + "OrderDate": "1998-04-21T00:00:00", + "RequiredDate": "1998-05-19T00:00:00", + "ShippedDate": "1998-04-30T00:00:00", + "ShipVia": 2, + "Freight": "29.5900", + "ShipName": "Supr\u00eames d\u00e9lices", + "ShipAddress": "Boulevard Tirou, 255", + "ShipCity": "Charleroi", + "ShipRegion": null, + "ShipPostalCode": "B-6000", + "ShipCountry": "Belgium" + }, { + "OrderID": 11039, + "CustomerID": "LINOD", + "EmployeeID": 1, + "OrderDate": "1998-04-21T00:00:00", + "RequiredDate": "1998-05-19T00:00:00", + "ShippedDate": null, + "ShipVia": 2, + "Freight": "65.0000", + "ShipName": "LINO-Delicateses", + "ShipAddress": "Ave. 5 de Mayo Porlamar", + "ShipCity": "I. de Margarita", + "ShipRegion": "Nueva Esparta", + "ShipPostalCode": "4980", + "ShipCountry": "Venezuela" + }, { + "OrderID": 11064, + "CustomerID": "SAVEA", + "EmployeeID": 1, + "OrderDate": "1998-05-01T00:00:00", + "RequiredDate": "1998-05-29T00:00:00", + "ShippedDate": "1998-05-04T00:00:00", + "ShipVia": 1, + "Freight": "30.0900", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 11067, + "CustomerID": "DRACD", + "EmployeeID": 1, + "OrderDate": "1998-05-04T00:00:00", + "RequiredDate": "1998-05-18T00:00:00", + "ShippedDate": "1998-05-06T00:00:00", + "ShipVia": 2, + "Freight": "7.9800", + "ShipName": "Drachenblut Delikatessen", + "ShipAddress": "Walserweg 21", + "ShipCity": "Aachen", + "ShipRegion": null, + "ShipPostalCode": "52066", + "ShipCountry": "Germany" + }, { + "OrderID": 11069, + "CustomerID": "TORTU", + "EmployeeID": 1, + "OrderDate": "1998-05-04T00:00:00", + "RequiredDate": "1998-06-01T00:00:00", + "ShippedDate": "1998-05-06T00:00:00", + "ShipVia": 2, + "Freight": "15.6700", + "ShipName": "Tortuga Restaurante", + "ShipAddress": "Avda. Azteca 123", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05033", + "ShipCountry": "Mexico" + }, { + "OrderID": 11071, + "CustomerID": "LILAS", + "EmployeeID": 1, + "OrderDate": "1998-05-05T00:00:00", + "RequiredDate": "1998-06-02T00:00:00", + "ShippedDate": null, + "ShipVia": 1, + "Freight": "0.9300", + "ShipName": "LILA-Supermercado", + "ShipAddress": "Carrera 52 con Ave. Bol\u00edvar #65-98 Llano Largo", + "ShipCity": "Barquisimeto", + "ShipRegion": "Lara", + "ShipPostalCode": "3508", + "ShipCountry": "Venezuela" + }, { + "OrderID": 11077, + "CustomerID": "RATTC", + "EmployeeID": 1, + "OrderDate": "1998-05-06T00:00:00", + "RequiredDate": "1998-06-03T00:00:00", + "ShippedDate": null, + "ShipVia": 2, + "Freight": "8.5300", + "ShipName": "Rattlesnake Canyon Grocery", + "ShipAddress": "2817 Milton Dr.", + "ShipCity": "Albuquerque", + "ShipRegion": "NM", + "ShipPostalCode": "87110", + "ShipCountry": "USA" + }], + "EmployeeID": 1, + "LastName": "Davolio", + "FirstName": "Nancy", + "Title": "Sales Representative", + "TitleOfCourtesy": "Ms.", + "BirthDate": "1948-12-08T00:00:00", + "HireDate": "1992-05-01T00:00:00", + "Address": "507 - 20th Ave. E.\r\nApt. 2A", + "City": "Seattle", + "Region": "WA", + "PostalCode": "98122", + "Country": "USA", + "HomePhone": "(206) 555-9857", + "Extension": "5467", + "Notes": "Education includes a BA in psychology from Colorado State University in 1970. She also completed \"The Art of the Cold Call.\" Nancy is a member of Toastmasters International.", + "ReportsTo": 2, + "PhotoPath": "http://accweb/emmployees/davolio.bmp" + }, { + "Orders": [{ + "OrderID": 10265, + "CustomerID": "BLONP", + "EmployeeID": 2, + "OrderDate": "1996-07-25T00:00:00", + "RequiredDate": "1996-08-22T00:00:00", + "ShippedDate": "1996-08-12T00:00:00", + "ShipVia": 1, + "Freight": "55.2800", + "ShipName": "Blondel p\u00e8re et fils", + "ShipAddress": "24, place Kl\u00e9ber", + "ShipCity": "Strasbourg", + "ShipRegion": null, + "ShipPostalCode": "67000", + "ShipCountry": "France" + }, { + "OrderID": 10277, + "CustomerID": "MORGK", + "EmployeeID": 2, + "OrderDate": "1996-08-09T00:00:00", + "RequiredDate": "1996-09-06T00:00:00", + "ShippedDate": "1996-08-13T00:00:00", + "ShipVia": 3, + "Freight": "125.7700", + "ShipName": "Morgenstern Gesundkost", + "ShipAddress": "Heerstr. 22", + "ShipCity": "Leipzig", + "ShipRegion": null, + "ShipPostalCode": "04179", + "ShipCountry": "Germany" + }, { + "OrderID": 10280, + "CustomerID": "BERGS", + "EmployeeID": 2, + "OrderDate": "1996-08-14T00:00:00", + "RequiredDate": "1996-09-11T00:00:00", + "ShippedDate": "1996-09-12T00:00:00", + "ShipVia": 1, + "Freight": "8.9800", + "ShipName": "Berglunds snabbk\u00f6p", + "ShipAddress": "Berguvsv\u00e4gen 8", + "ShipCity": "Lule\u00e5", + "ShipRegion": null, + "ShipPostalCode": "S-958 22", + "ShipCountry": "Sweden" + }, { + "OrderID": 10295, + "CustomerID": "VINET", + "EmployeeID": 2, + "OrderDate": "1996-09-02T00:00:00", + "RequiredDate": "1996-09-30T00:00:00", + "ShippedDate": "1996-09-10T00:00:00", + "ShipVia": 2, + "Freight": "1.1500", + "ShipName": "Vins et alcools Chevalier", + "ShipAddress": "59 rue de l'Abbaye", + "ShipCity": "Reims", + "ShipRegion": null, + "ShipPostalCode": "51100", + "ShipCountry": "France" + }, { + "OrderID": 10300, + "CustomerID": "MAGAA", + "EmployeeID": 2, + "OrderDate": "1996-09-09T00:00:00", + "RequiredDate": "1996-10-07T00:00:00", + "ShippedDate": "1996-09-18T00:00:00", + "ShipVia": 2, + "Freight": "17.6800", + "ShipName": "Magazzini Alimentari Riuniti", + "ShipAddress": "Via Ludovico il Moro 22", + "ShipCity": "Bergamo", + "ShipRegion": null, + "ShipPostalCode": "24100", + "ShipCountry": "Italy" + }, { + "OrderID": 10307, + "CustomerID": "LONEP", + "EmployeeID": 2, + "OrderDate": "1996-09-17T00:00:00", + "RequiredDate": "1996-10-15T00:00:00", + "ShippedDate": "1996-09-25T00:00:00", + "ShipVia": 2, + "Freight": "0.5600", + "ShipName": "Lonesome Pine Restaurant", + "ShipAddress": "89 Chiaroscuro Rd.", + "ShipCity": "Portland", + "ShipRegion": "OR", + "ShipPostalCode": "97219", + "ShipCountry": "USA" + }, { + "OrderID": 10312, + "CustomerID": "WANDK", + "EmployeeID": 2, + "OrderDate": "1996-09-23T00:00:00", + "RequiredDate": "1996-10-21T00:00:00", + "ShippedDate": "1996-10-03T00:00:00", + "ShipVia": 2, + "Freight": "40.2600", + "ShipName": "Die Wandernde Kuh", + "ShipAddress": "Adenauerallee 900", + "ShipCity": "Stuttgart", + "ShipRegion": null, + "ShipPostalCode": "70563", + "ShipCountry": "Germany" + }, { + "OrderID": 10313, + "CustomerID": "QUICK", + "EmployeeID": 2, + "OrderDate": "1996-09-24T00:00:00", + "RequiredDate": "1996-10-22T00:00:00", + "ShippedDate": "1996-10-04T00:00:00", + "ShipVia": 2, + "Freight": "1.9600", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10327, + "CustomerID": "FOLKO", + "EmployeeID": 2, + "OrderDate": "1996-10-11T00:00:00", + "RequiredDate": "1996-11-08T00:00:00", + "ShippedDate": "1996-10-14T00:00:00", + "ShipVia": 1, + "Freight": "63.3600", + "ShipName": "Folk och f\u00e4 HB", + "ShipAddress": "\u00c5kergatan 24", + "ShipCity": "Br\u00e4cke", + "ShipRegion": null, + "ShipPostalCode": "S-844 67", + "ShipCountry": "Sweden" + }, { + "OrderID": 10339, + "CustomerID": "MEREP", + "EmployeeID": 2, + "OrderDate": "1996-10-28T00:00:00", + "RequiredDate": "1996-11-25T00:00:00", + "ShippedDate": "1996-11-04T00:00:00", + "ShipVia": 2, + "Freight": "15.6600", + "ShipName": "M\u00e8re Paillarde", + "ShipAddress": "43 rue St. Laurent", + "ShipCity": "Montr\u00e9al", + "ShipRegion": "Qu\u00e9bec", + "ShipPostalCode": "H1J 1C3", + "ShipCountry": "Canada" + }, { + "OrderID": 10345, + "CustomerID": "QUICK", + "EmployeeID": 2, + "OrderDate": "1996-11-04T00:00:00", + "RequiredDate": "1996-12-02T00:00:00", + "ShippedDate": "1996-11-11T00:00:00", + "ShipVia": 2, + "Freight": "249.0600", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10368, + "CustomerID": "ERNSH", + "EmployeeID": 2, + "OrderDate": "1996-11-29T00:00:00", + "RequiredDate": "1996-12-27T00:00:00", + "ShippedDate": "1996-12-02T00:00:00", + "ShipVia": 2, + "Freight": "101.9500", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10379, + "CustomerID": "QUEDE", + "EmployeeID": 2, + "OrderDate": "1996-12-11T00:00:00", + "RequiredDate": "1997-01-08T00:00:00", + "ShippedDate": "1996-12-13T00:00:00", + "ShipVia": 1, + "Freight": "45.0300", + "ShipName": "Que Del\u00edcia", + "ShipAddress": "Rua da Panificadora, 12", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "02389-673", + "ShipCountry": "Brazil" + }, { + "OrderID": 10388, + "CustomerID": "SEVES", + "EmployeeID": 2, + "OrderDate": "1996-12-19T00:00:00", + "RequiredDate": "1997-01-16T00:00:00", + "ShippedDate": "1996-12-20T00:00:00", + "ShipVia": 1, + "Freight": "34.8600", + "ShipName": "Seven Seas Imports", + "ShipAddress": "90 Wadhurst Rd.", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "OX15 4NB", + "ShipCountry": "UK" + }, { + "OrderID": 10392, + "CustomerID": "PICCO", + "EmployeeID": 2, + "OrderDate": "1996-12-24T00:00:00", + "RequiredDate": "1997-01-21T00:00:00", + "ShippedDate": "1997-01-01T00:00:00", + "ShipVia": 3, + "Freight": "122.4600", + "ShipName": "Piccolo und mehr", + "ShipAddress": "Geislweg 14", + "ShipCity": "Salzburg", + "ShipRegion": null, + "ShipPostalCode": "5020", + "ShipCountry": "Austria" + }, { + "OrderID": 10398, + "CustomerID": "SAVEA", + "EmployeeID": 2, + "OrderDate": "1996-12-30T00:00:00", + "RequiredDate": "1997-01-27T00:00:00", + "ShippedDate": "1997-01-09T00:00:00", + "ShipVia": 3, + "Freight": "89.1600", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10404, + "CustomerID": "MAGAA", + "EmployeeID": 2, + "OrderDate": "1997-01-03T00:00:00", + "RequiredDate": "1997-01-31T00:00:00", + "ShippedDate": "1997-01-08T00:00:00", + "ShipVia": 1, + "Freight": "155.9700", + "ShipName": "Magazzini Alimentari Riuniti", + "ShipAddress": "Via Ludovico il Moro 22", + "ShipCity": "Bergamo", + "ShipRegion": null, + "ShipPostalCode": "24100", + "ShipCountry": "Italy" + }, { + "OrderID": 10407, + "CustomerID": "OTTIK", + "EmployeeID": 2, + "OrderDate": "1997-01-07T00:00:00", + "RequiredDate": "1997-02-04T00:00:00", + "ShippedDate": "1997-01-30T00:00:00", + "ShipVia": 2, + "Freight": "91.4800", + "ShipName": "Ottilies K\u00e4seladen", + "ShipAddress": "Mehrheimerstr. 369", + "ShipCity": "K\u00f6ln", + "ShipRegion": null, + "ShipPostalCode": "50739", + "ShipCountry": "Germany" + }, { + "OrderID": 10414, + "CustomerID": "FAMIA", + "EmployeeID": 2, + "OrderDate": "1997-01-14T00:00:00", + "RequiredDate": "1997-02-11T00:00:00", + "ShippedDate": "1997-01-17T00:00:00", + "ShipVia": 3, + "Freight": "21.4800", + "ShipName": "Familia Arquibaldo", + "ShipAddress": "Rua Or\u00f3s, 92", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05442-030", + "ShipCountry": "Brazil" + }, { + "OrderID": 10422, + "CustomerID": "FRANS", + "EmployeeID": 2, + "OrderDate": "1997-01-22T00:00:00", + "RequiredDate": "1997-02-19T00:00:00", + "ShippedDate": "1997-01-31T00:00:00", + "ShipVia": 1, + "Freight": "3.0200", + "ShipName": "Franchi S.p.A.", + "ShipAddress": "Via Monte Bianco 34", + "ShipCity": "Torino", + "ShipRegion": null, + "ShipPostalCode": "10100", + "ShipCountry": "Italy" + }, { + "OrderID": 10457, + "CustomerID": "KOENE", + "EmployeeID": 2, + "OrderDate": "1997-02-25T00:00:00", + "RequiredDate": "1997-03-25T00:00:00", + "ShippedDate": "1997-03-03T00:00:00", + "ShipVia": 1, + "Freight": "11.5700", + "ShipName": "K\u00f6niglich Essen", + "ShipAddress": "Maubelstr. 90", + "ShipCity": "Brandenburg", + "ShipRegion": null, + "ShipPostalCode": "14776", + "ShipCountry": "Germany" + }, { + "OrderID": 10462, + "CustomerID": "CONSH", + "EmployeeID": 2, + "OrderDate": "1997-03-03T00:00:00", + "RequiredDate": "1997-03-31T00:00:00", + "ShippedDate": "1997-03-18T00:00:00", + "ShipVia": 1, + "Freight": "6.1700", + "ShipName": "Consolidated Holdings", + "ShipAddress": "Berkeley Gardens 12 Brewery", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "WX1 6LT", + "ShipCountry": "UK" + }, { + "OrderID": 10471, + "CustomerID": "BSBEV", + "EmployeeID": 2, + "OrderDate": "1997-03-11T00:00:00", + "RequiredDate": "1997-04-08T00:00:00", + "ShippedDate": "1997-03-18T00:00:00", + "ShipVia": 3, + "Freight": "45.5900", + "ShipName": "B's Beverages", + "ShipAddress": "Fauntleroy Circus", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "EC2 5NT", + "ShipCountry": "UK" + }, { + "OrderID": 10478, + "CustomerID": "VICTE", + "EmployeeID": 2, + "OrderDate": "1997-03-18T00:00:00", + "RequiredDate": "1997-04-01T00:00:00", + "ShippedDate": "1997-03-26T00:00:00", + "ShipVia": 3, + "Freight": "4.8100", + "ShipName": "Victuailles en stock", + "ShipAddress": "2, rue du Commerce", + "ShipCity": "Lyon", + "ShipRegion": null, + "ShipPostalCode": "69004", + "ShipCountry": "France" + }, { + "OrderID": 10487, + "CustomerID": "QUEEN", + "EmployeeID": 2, + "OrderDate": "1997-03-26T00:00:00", + "RequiredDate": "1997-04-23T00:00:00", + "ShippedDate": "1997-03-28T00:00:00", + "ShipVia": 2, + "Freight": "71.0700", + "ShipName": "Queen Cozinha", + "ShipAddress": "Alameda dos Can\u00e0rios, 891", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05487-020", + "ShipCountry": "Brazil" + }, { + "OrderID": 10502, + "CustomerID": "PERIC", + "EmployeeID": 2, + "OrderDate": "1997-04-10T00:00:00", + "RequiredDate": "1997-05-08T00:00:00", + "ShippedDate": "1997-04-29T00:00:00", + "ShipVia": 1, + "Freight": "69.3200", + "ShipName": "Pericles Comidas cl\u00e1sicas", + "ShipAddress": "Calle Dr. Jorge Cash 321", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05033", + "ShipCountry": "Mexico" + }, { + "OrderID": 10515, + "CustomerID": "QUICK", + "EmployeeID": 2, + "OrderDate": "1997-04-23T00:00:00", + "RequiredDate": "1997-05-07T00:00:00", + "ShippedDate": "1997-05-23T00:00:00", + "ShipVia": 1, + "Freight": "204.4700", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10516, + "CustomerID": "HUNGO", + "EmployeeID": 2, + "OrderDate": "1997-04-24T00:00:00", + "RequiredDate": "1997-05-22T00:00:00", + "ShippedDate": "1997-05-01T00:00:00", + "ShipVia": 3, + "Freight": "62.7800", + "ShipName": "Hungry Owl All-Night Grocers", + "ShipAddress": "8 Johnstown Road", + "ShipCity": "Cork", + "ShipRegion": "Co. Cork", + "ShipPostalCode": null, + "ShipCountry": "Ireland" + }, { + "OrderID": 10541, + "CustomerID": "HANAR", + "EmployeeID": 2, + "OrderDate": "1997-05-19T00:00:00", + "RequiredDate": "1997-06-16T00:00:00", + "ShippedDate": "1997-05-29T00:00:00", + "ShipVia": 1, + "Freight": "68.6500", + "ShipName": "Hanari Carnes", + "ShipAddress": "Rua do Pa\u00e7o, 67", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "05454-876", + "ShipCountry": "Brazil" + }, { + "OrderID": 10552, + "CustomerID": "HILAA", + "EmployeeID": 2, + "OrderDate": "1997-05-29T00:00:00", + "RequiredDate": "1997-06-26T00:00:00", + "ShippedDate": "1997-06-05T00:00:00", + "ShipVia": 1, + "Freight": "83.2200", + "ShipName": "HILARION-Abastos", + "ShipAddress": "Carrera 22 con Ave. Carlos Soublette #8-35", + "ShipCity": "San Crist\u00f3bal", + "ShipRegion": "T\u00e1chira", + "ShipPostalCode": "5022", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10553, + "CustomerID": "WARTH", + "EmployeeID": 2, + "OrderDate": "1997-05-30T00:00:00", + "RequiredDate": "1997-06-27T00:00:00", + "ShippedDate": "1997-06-03T00:00:00", + "ShipVia": 2, + "Freight": "149.4900", + "ShipName": "Wartian Herkku", + "ShipAddress": "Torikatu 38", + "ShipCity": "Oulu", + "ShipRegion": null, + "ShipPostalCode": "90110", + "ShipCountry": "Finland" + }, { + "OrderID": 10556, + "CustomerID": "SIMOB", + "EmployeeID": 2, + "OrderDate": "1997-06-03T00:00:00", + "RequiredDate": "1997-07-15T00:00:00", + "ShippedDate": "1997-06-13T00:00:00", + "ShipVia": 1, + "Freight": "9.8000", + "ShipName": "Simons bistro", + "ShipAddress": "Vinb\u00e6ltet 34", + "ShipCity": "Kobenhavn", + "ShipRegion": null, + "ShipPostalCode": "1734", + "ShipCountry": "Denmark" + }, { + "OrderID": 10561, + "CustomerID": "FOLKO", + "EmployeeID": 2, + "OrderDate": "1997-06-06T00:00:00", + "RequiredDate": "1997-07-04T00:00:00", + "ShippedDate": "1997-06-09T00:00:00", + "ShipVia": 2, + "Freight": "242.2100", + "ShipName": "Folk och f\u00e4 HB", + "ShipAddress": "\u00c5kergatan 24", + "ShipCity": "Br\u00e4cke", + "ShipRegion": null, + "ShipPostalCode": "S-844 67", + "ShipCountry": "Sweden" + }, { + "OrderID": 10563, + "CustomerID": "RICAR", + "EmployeeID": 2, + "OrderDate": "1997-06-10T00:00:00", + "RequiredDate": "1997-07-22T00:00:00", + "ShippedDate": "1997-06-24T00:00:00", + "ShipVia": 2, + "Freight": "60.4300", + "ShipName": "Ricardo Adocicados", + "ShipAddress": "Av. Copacabana, 267", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "02389-890", + "ShipCountry": "Brazil" + }, { + "OrderID": 10583, + "CustomerID": "WARTH", + "EmployeeID": 2, + "OrderDate": "1997-06-30T00:00:00", + "RequiredDate": "1997-07-28T00:00:00", + "ShippedDate": "1997-07-04T00:00:00", + "ShipVia": 2, + "Freight": "7.2800", + "ShipName": "Wartian Herkku", + "ShipAddress": "Torikatu 38", + "ShipCity": "Oulu", + "ShipRegion": null, + "ShipPostalCode": "90110", + "ShipCountry": "Finland" + }, { + "OrderID": 10588, + "CustomerID": "QUICK", + "EmployeeID": 2, + "OrderDate": "1997-07-03T00:00:00", + "RequiredDate": "1997-07-31T00:00:00", + "ShippedDate": "1997-07-10T00:00:00", + "ShipVia": 3, + "Freight": "194.6700", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10595, + "CustomerID": "ERNSH", + "EmployeeID": 2, + "OrderDate": "1997-07-10T00:00:00", + "RequiredDate": "1997-08-07T00:00:00", + "ShippedDate": "1997-07-14T00:00:00", + "ShipVia": 1, + "Freight": "96.7800", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10615, + "CustomerID": "WILMK", + "EmployeeID": 2, + "OrderDate": "1997-07-30T00:00:00", + "RequiredDate": "1997-08-27T00:00:00", + "ShippedDate": "1997-08-06T00:00:00", + "ShipVia": 3, + "Freight": "0.7500", + "ShipName": "Wilman Kala", + "ShipAddress": "Keskuskatu 45", + "ShipCity": "Helsinki", + "ShipRegion": null, + "ShipPostalCode": "21240", + "ShipCountry": "Finland" + }, { + "OrderID": 10620, + "CustomerID": "LAUGB", + "EmployeeID": 2, + "OrderDate": "1997-08-05T00:00:00", + "RequiredDate": "1997-09-02T00:00:00", + "ShippedDate": "1997-08-14T00:00:00", + "ShipVia": 3, + "Freight": "0.9400", + "ShipName": "Laughing Bacchus Wine Cellars", + "ShipAddress": "2319 Elm St.", + "ShipCity": "Vancouver", + "ShipRegion": "BC", + "ShipPostalCode": "V3F 2K1", + "ShipCountry": "Canada" + }, { + "OrderID": 10657, + "CustomerID": "SAVEA", + "EmployeeID": 2, + "OrderDate": "1997-09-04T00:00:00", + "RequiredDate": "1997-10-02T00:00:00", + "ShippedDate": "1997-09-15T00:00:00", + "ShipVia": 2, + "Freight": "352.6900", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10663, + "CustomerID": "BONAP", + "EmployeeID": 2, + "OrderDate": "1997-09-10T00:00:00", + "RequiredDate": "1997-09-24T00:00:00", + "ShippedDate": "1997-10-03T00:00:00", + "ShipVia": 2, + "Freight": "113.1500", + "ShipName": "Bon app'", + "ShipAddress": "12, rue des Bouchers", + "ShipCity": "Marseille", + "ShipRegion": null, + "ShipPostalCode": "13008", + "ShipCountry": "France" + }, { + "OrderID": 10669, + "CustomerID": "SIMOB", + "EmployeeID": 2, + "OrderDate": "1997-09-15T00:00:00", + "RequiredDate": "1997-10-13T00:00:00", + "ShippedDate": "1997-09-22T00:00:00", + "ShipVia": 1, + "Freight": "24.3900", + "ShipName": "Simons bistro", + "ShipAddress": "Vinb\u00e6ltet 34", + "ShipCity": "Kobenhavn", + "ShipRegion": null, + "ShipPostalCode": "1734", + "ShipCountry": "Denmark" + }, { + "OrderID": 10673, + "CustomerID": "WILMK", + "EmployeeID": 2, + "OrderDate": "1997-09-18T00:00:00", + "RequiredDate": "1997-10-16T00:00:00", + "ShippedDate": "1997-09-19T00:00:00", + "ShipVia": 1, + "Freight": "22.7600", + "ShipName": "Wilman Kala", + "ShipAddress": "Keskuskatu 45", + "ShipCity": "Helsinki", + "ShipRegion": null, + "ShipPostalCode": "21240", + "ShipCountry": "Finland" + }, { + "OrderID": 10676, + "CustomerID": "TORTU", + "EmployeeID": 2, + "OrderDate": "1997-09-22T00:00:00", + "RequiredDate": "1997-10-20T00:00:00", + "ShippedDate": "1997-09-29T00:00:00", + "ShipVia": 2, + "Freight": "2.0100", + "ShipName": "Tortuga Restaurante", + "ShipAddress": "Avda. Azteca 123", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05033", + "ShipCountry": "Mexico" + }, { + "OrderID": 10683, + "CustomerID": "DUMON", + "EmployeeID": 2, + "OrderDate": "1997-09-26T00:00:00", + "RequiredDate": "1997-10-24T00:00:00", + "ShippedDate": "1997-10-01T00:00:00", + "ShipVia": 1, + "Freight": "4.4000", + "ShipName": "Du monde entier", + "ShipAddress": "67, rue des Cinquante Otages", + "ShipCity": "Nantes", + "ShipRegion": null, + "ShipPostalCode": "44000", + "ShipCountry": "France" + }, { + "OrderID": 10686, + "CustomerID": "PICCO", + "EmployeeID": 2, + "OrderDate": "1997-09-30T00:00:00", + "RequiredDate": "1997-10-28T00:00:00", + "ShippedDate": "1997-10-08T00:00:00", + "ShipVia": 1, + "Freight": "96.5000", + "ShipName": "Piccolo und mehr", + "ShipAddress": "Geislweg 14", + "ShipCity": "Salzburg", + "ShipRegion": null, + "ShipPostalCode": "5020", + "ShipCountry": "Austria" + }, { + "OrderID": 10691, + "CustomerID": "QUICK", + "EmployeeID": 2, + "OrderDate": "1997-10-03T00:00:00", + "RequiredDate": "1997-11-14T00:00:00", + "ShippedDate": "1997-10-22T00:00:00", + "ShipVia": 2, + "Freight": "810.0500", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10727, + "CustomerID": "REGGC", + "EmployeeID": 2, + "OrderDate": "1997-11-03T00:00:00", + "RequiredDate": "1997-12-01T00:00:00", + "ShippedDate": "1997-12-05T00:00:00", + "ShipVia": 1, + "Freight": "89.9000", + "ShipName": "Reggiani Caseifici", + "ShipAddress": "Strada Provinciale 124", + "ShipCity": "Reggio Emilia", + "ShipRegion": null, + "ShipPostalCode": "42100", + "ShipCountry": "Italy" + }, { + "OrderID": 10734, + "CustomerID": "GOURL", + "EmployeeID": 2, + "OrderDate": "1997-11-07T00:00:00", + "RequiredDate": "1997-12-05T00:00:00", + "ShippedDate": "1997-11-12T00:00:00", + "ShipVia": 3, + "Freight": "1.6300", + "ShipName": "Gourmet Lanchonetes", + "ShipAddress": "Av. Brasil, 442", + "ShipCity": "Campinas", + "ShipRegion": "SP", + "ShipPostalCode": "04876-786", + "ShipCountry": "Brazil" + }, { + "OrderID": 10737, + "CustomerID": "VINET", + "EmployeeID": 2, + "OrderDate": "1997-11-11T00:00:00", + "RequiredDate": "1997-12-09T00:00:00", + "ShippedDate": "1997-11-18T00:00:00", + "ShipVia": 2, + "Freight": "7.7900", + "ShipName": "Vins et alcools Chevalier", + "ShipAddress": "59 rue de l'Abbaye", + "ShipCity": "Reims", + "ShipRegion": null, + "ShipPostalCode": "51100", + "ShipCountry": "France" + }, { + "OrderID": 10738, + "CustomerID": "SPECD", + "EmployeeID": 2, + "OrderDate": "1997-11-12T00:00:00", + "RequiredDate": "1997-12-10T00:00:00", + "ShippedDate": "1997-11-18T00:00:00", + "ShipVia": 1, + "Freight": "2.9100", + "ShipName": "Sp\u00e9cialit\u00e9s du monde", + "ShipAddress": "25, rue Lauriston", + "ShipCity": "Paris", + "ShipRegion": null, + "ShipPostalCode": "75016", + "ShipCountry": "France" + }, { + "OrderID": 10752, + "CustomerID": "NORTS", + "EmployeeID": 2, + "OrderDate": "1997-11-24T00:00:00", + "RequiredDate": "1997-12-22T00:00:00", + "ShippedDate": "1997-11-28T00:00:00", + "ShipVia": 3, + "Freight": "1.3900", + "ShipName": "North/South", + "ShipAddress": "South House 300 Queensbridge", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "SW7 1RZ", + "ShipCountry": "UK" + }, { + "OrderID": 10780, + "CustomerID": "LILAS", + "EmployeeID": 2, + "OrderDate": "1997-12-16T00:00:00", + "RequiredDate": "1997-12-30T00:00:00", + "ShippedDate": "1997-12-25T00:00:00", + "ShipVia": 1, + "Freight": "42.1300", + "ShipName": "LILA-Supermercado", + "ShipAddress": "Carrera 52 con Ave. Bol\u00edvar #65-98 Llano Largo", + "ShipCity": "Barquisimeto", + "ShipRegion": "Lara", + "ShipPostalCode": "3508", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10781, + "CustomerID": "WARTH", + "EmployeeID": 2, + "OrderDate": "1997-12-17T00:00:00", + "RequiredDate": "1998-01-14T00:00:00", + "ShippedDate": "1997-12-19T00:00:00", + "ShipVia": 3, + "Freight": "73.1600", + "ShipName": "Wartian Herkku", + "ShipAddress": "Torikatu 38", + "ShipCity": "Oulu", + "ShipRegion": null, + "ShipPostalCode": "90110", + "ShipCountry": "Finland" + }, { + "OrderID": 10787, + "CustomerID": "LAMAI", + "EmployeeID": 2, + "OrderDate": "1997-12-19T00:00:00", + "RequiredDate": "1998-01-02T00:00:00", + "ShippedDate": "1997-12-26T00:00:00", + "ShipVia": 1, + "Freight": "249.9300", + "ShipName": "La maison d'Asie", + "ShipAddress": "1 rue Alsace-Lorraine", + "ShipCity": "Toulouse", + "ShipRegion": null, + "ShipPostalCode": "31000", + "ShipCountry": "France" + }, { + "OrderID": 10798, + "CustomerID": "ISLAT", + "EmployeeID": 2, + "OrderDate": "1997-12-26T00:00:00", + "RequiredDate": "1998-01-23T00:00:00", + "ShippedDate": "1998-01-05T00:00:00", + "ShipVia": 1, + "Freight": "2.3300", + "ShipName": "Island Trading", + "ShipAddress": "Garden House Crowther Way", + "ShipCity": "Cowes", + "ShipRegion": "Isle of Wight", + "ShipPostalCode": "PO31 7PJ", + "ShipCountry": "UK" + }, { + "OrderID": 10805, + "CustomerID": "THEBI", + "EmployeeID": 2, + "OrderDate": "1997-12-30T00:00:00", + "RequiredDate": "1998-01-27T00:00:00", + "ShippedDate": "1998-01-09T00:00:00", + "ShipVia": 3, + "Freight": "237.3400", + "ShipName": "The Big Cheese", + "ShipAddress": "89 Jefferson Way Suite 2", + "ShipCity": "Portland", + "ShipRegion": "OR", + "ShipPostalCode": "97201", + "ShipCountry": "USA" + }, { + "OrderID": 10808, + "CustomerID": "OLDWO", + "EmployeeID": 2, + "OrderDate": "1998-01-01T00:00:00", + "RequiredDate": "1998-01-29T00:00:00", + "ShippedDate": "1998-01-09T00:00:00", + "ShipVia": 3, + "Freight": "45.5300", + "ShipName": "Old World Delicatessen", + "ShipAddress": "2743 Bering St.", + "ShipCity": "Anchorage", + "ShipRegion": "AK", + "ShipPostalCode": "99508", + "ShipCountry": "USA" + }, { + "OrderID": 10810, + "CustomerID": "LAUGB", + "EmployeeID": 2, + "OrderDate": "1998-01-01T00:00:00", + "RequiredDate": "1998-01-29T00:00:00", + "ShippedDate": "1998-01-07T00:00:00", + "ShipVia": 3, + "Freight": "4.3300", + "ShipName": "Laughing Bacchus Wine Cellars", + "ShipAddress": "2319 Elm St.", + "ShipCity": "Vancouver", + "ShipRegion": "BC", + "ShipPostalCode": "V3F 2K1", + "ShipCountry": "Canada" + }, { + "OrderID": 10815, + "CustomerID": "SAVEA", + "EmployeeID": 2, + "OrderDate": "1998-01-05T00:00:00", + "RequiredDate": "1998-02-02T00:00:00", + "ShippedDate": "1998-01-14T00:00:00", + "ShipVia": 3, + "Freight": "14.6200", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10819, + "CustomerID": "CACTU", + "EmployeeID": 2, + "OrderDate": "1998-01-07T00:00:00", + "RequiredDate": "1998-02-04T00:00:00", + "ShippedDate": "1998-01-16T00:00:00", + "ShipVia": 3, + "Freight": "19.7600", + "ShipName": "Cactus Comidas para llevar", + "ShipAddress": "Cerrito 333", + "ShipCity": "Buenos Aires", + "ShipRegion": null, + "ShipPostalCode": "1010", + "ShipCountry": "Argentina" + }, { + "OrderID": 10832, + "CustomerID": "LAMAI", + "EmployeeID": 2, + "OrderDate": "1998-01-14T00:00:00", + "RequiredDate": "1998-02-11T00:00:00", + "ShippedDate": "1998-01-19T00:00:00", + "ShipVia": 2, + "Freight": "43.2600", + "ShipName": "La maison d'Asie", + "ShipAddress": "1 rue Alsace-Lorraine", + "ShipCity": "Toulouse", + "ShipRegion": null, + "ShipPostalCode": "31000", + "ShipCountry": "France" + }, { + "OrderID": 10846, + "CustomerID": "SUPRD", + "EmployeeID": 2, + "OrderDate": "1998-01-22T00:00:00", + "RequiredDate": "1998-03-05T00:00:00", + "ShippedDate": "1998-01-23T00:00:00", + "ShipVia": 3, + "Freight": "56.4600", + "ShipName": "Supr\u00eames d\u00e9lices", + "ShipAddress": "Boulevard Tirou, 255", + "ShipCity": "Charleroi", + "ShipRegion": null, + "ShipPostalCode": "B-6000", + "ShipCountry": "Belgium" + }, { + "OrderID": 10858, + "CustomerID": "LACOR", + "EmployeeID": 2, + "OrderDate": "1998-01-29T00:00:00", + "RequiredDate": "1998-02-26T00:00:00", + "ShippedDate": "1998-02-03T00:00:00", + "ShipVia": 1, + "Freight": "52.5100", + "ShipName": "La corne d'abondance", + "ShipAddress": "67, avenue de l'Europe", + "ShipCity": "Versailles", + "ShipRegion": null, + "ShipPostalCode": "78000", + "ShipCountry": "France" + }, { + "OrderID": 10865, + "CustomerID": "QUICK", + "EmployeeID": 2, + "OrderDate": "1998-02-02T00:00:00", + "RequiredDate": "1998-02-16T00:00:00", + "ShippedDate": "1998-02-12T00:00:00", + "ShipVia": 1, + "Freight": "348.1400", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10912, + "CustomerID": "HUNGO", + "EmployeeID": 2, + "OrderDate": "1998-02-26T00:00:00", + "RequiredDate": "1998-03-26T00:00:00", + "ShippedDate": "1998-03-18T00:00:00", + "ShipVia": 2, + "Freight": "580.9100", + "ShipName": "Hungry Owl All-Night Grocers", + "ShipAddress": "8 Johnstown Road", + "ShipCity": "Cork", + "ShipRegion": "Co. Cork", + "ShipPostalCode": null, + "ShipCountry": "Ireland" + }, { + "OrderID": 10915, + "CustomerID": "TORTU", + "EmployeeID": 2, + "OrderDate": "1998-02-27T00:00:00", + "RequiredDate": "1998-03-27T00:00:00", + "ShippedDate": "1998-03-02T00:00:00", + "ShipVia": 2, + "Freight": "3.5100", + "ShipName": "Tortuga Restaurante", + "ShipAddress": "Avda. Azteca 123", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05033", + "ShipCountry": "Mexico" + }, { + "OrderID": 10919, + "CustomerID": "LINOD", + "EmployeeID": 2, + "OrderDate": "1998-03-02T00:00:00", + "RequiredDate": "1998-03-30T00:00:00", + "ShippedDate": "1998-03-04T00:00:00", + "ShipVia": 2, + "Freight": "19.8000", + "ShipName": "LINO-Delicateses", + "ShipAddress": "Ave. 5 de Mayo Porlamar", + "ShipCity": "I. de Margarita", + "ShipRegion": "Nueva Esparta", + "ShipPostalCode": "4980", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10939, + "CustomerID": "MAGAA", + "EmployeeID": 2, + "OrderDate": "1998-03-10T00:00:00", + "RequiredDate": "1998-04-07T00:00:00", + "ShippedDate": "1998-03-13T00:00:00", + "ShipVia": 2, + "Freight": "76.3300", + "ShipName": "Magazzini Alimentari Riuniti", + "ShipAddress": "Via Ludovico il Moro 22", + "ShipCity": "Bergamo", + "ShipRegion": null, + "ShipPostalCode": "24100", + "ShipCountry": "Italy" + }, { + "OrderID": 10949, + "CustomerID": "BOTTM", + "EmployeeID": 2, + "OrderDate": "1998-03-13T00:00:00", + "RequiredDate": "1998-04-10T00:00:00", + "ShippedDate": "1998-03-17T00:00:00", + "ShipVia": 3, + "Freight": "74.4400", + "ShipName": "Bottom-Dollar Markets", + "ShipAddress": "23 Tsawassen Blvd.", + "ShipCity": "Tsawassen", + "ShipRegion": "BC", + "ShipPostalCode": "T2F 8M4", + "ShipCountry": "Canada" + }, { + "OrderID": 10967, + "CustomerID": "TOMSP", + "EmployeeID": 2, + "OrderDate": "1998-03-23T00:00:00", + "RequiredDate": "1998-04-20T00:00:00", + "ShippedDate": "1998-04-02T00:00:00", + "ShipVia": 2, + "Freight": "62.2200", + "ShipName": "Toms Spezialit\u00e4ten", + "ShipAddress": "Luisenstr. 48", + "ShipCity": "M\u00fcnster", + "ShipRegion": null, + "ShipPostalCode": "44087", + "ShipCountry": "Germany" + }, { + "OrderID": 10971, + "CustomerID": "FRANR", + "EmployeeID": 2, + "OrderDate": "1998-03-24T00:00:00", + "RequiredDate": "1998-04-21T00:00:00", + "ShippedDate": "1998-04-02T00:00:00", + "ShipVia": 2, + "Freight": "121.8200", + "ShipName": "France restauration", + "ShipAddress": "54, rue Royale", + "ShipCity": "Nantes", + "ShipRegion": null, + "ShipPostalCode": "44000", + "ShipCountry": "France" + }, { + "OrderID": 10982, + "CustomerID": "BOTTM", + "EmployeeID": 2, + "OrderDate": "1998-03-27T00:00:00", + "RequiredDate": "1998-04-24T00:00:00", + "ShippedDate": "1998-04-08T00:00:00", + "ShipVia": 1, + "Freight": "14.0100", + "ShipName": "Bottom-Dollar Markets", + "ShipAddress": "23 Tsawassen Blvd.", + "ShipCity": "Tsawassen", + "ShipRegion": "BC", + "ShipPostalCode": "T2F 8M4", + "ShipCountry": "Canada" + }, { + "OrderID": 10983, + "CustomerID": "SAVEA", + "EmployeeID": 2, + "OrderDate": "1998-03-27T00:00:00", + "RequiredDate": "1998-04-24T00:00:00", + "ShippedDate": "1998-04-06T00:00:00", + "ShipVia": 2, + "Freight": "657.5400", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10985, + "CustomerID": "HUNGO", + "EmployeeID": 2, + "OrderDate": "1998-03-30T00:00:00", + "RequiredDate": "1998-04-27T00:00:00", + "ShippedDate": "1998-04-02T00:00:00", + "ShipVia": 1, + "Freight": "91.5100", + "ShipName": "Hungry Owl All-Night Grocers", + "ShipAddress": "8 Johnstown Road", + "ShipCity": "Cork", + "ShipRegion": "Co. Cork", + "ShipPostalCode": null, + "ShipCountry": "Ireland" + }, { + "OrderID": 10989, + "CustomerID": "QUEDE", + "EmployeeID": 2, + "OrderDate": "1998-03-31T00:00:00", + "RequiredDate": "1998-04-28T00:00:00", + "ShippedDate": "1998-04-02T00:00:00", + "ShipVia": 1, + "Freight": "34.7600", + "ShipName": "Que Del\u00edcia", + "ShipAddress": "Rua da Panificadora, 12", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "02389-673", + "ShipCountry": "Brazil" + }, { + "OrderID": 10990, + "CustomerID": "ERNSH", + "EmployeeID": 2, + "OrderDate": "1998-04-01T00:00:00", + "RequiredDate": "1998-05-13T00:00:00", + "ShippedDate": "1998-04-07T00:00:00", + "ShipVia": 3, + "Freight": "117.6100", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10994, + "CustomerID": "VAFFE", + "EmployeeID": 2, + "OrderDate": "1998-04-02T00:00:00", + "RequiredDate": "1998-04-16T00:00:00", + "ShippedDate": "1998-04-09T00:00:00", + "ShipVia": 3, + "Freight": "65.5300", + "ShipName": "Vaffeljernet", + "ShipAddress": "Smagsloget 45", + "ShipCity": "\u00c5rhus", + "ShipRegion": null, + "ShipPostalCode": "8200", + "ShipCountry": "Denmark" + }, { + "OrderID": 11000, + "CustomerID": "RATTC", + "EmployeeID": 2, + "OrderDate": "1998-04-06T00:00:00", + "RequiredDate": "1998-05-04T00:00:00", + "ShippedDate": "1998-04-14T00:00:00", + "ShipVia": 3, + "Freight": "55.1200", + "ShipName": "Rattlesnake Canyon Grocery", + "ShipAddress": "2817 Milton Dr.", + "ShipCity": "Albuquerque", + "ShipRegion": "NM", + "ShipPostalCode": "87110", + "ShipCountry": "USA" + }, { + "OrderID": 11001, + "CustomerID": "FOLKO", + "EmployeeID": 2, + "OrderDate": "1998-04-06T00:00:00", + "RequiredDate": "1998-05-04T00:00:00", + "ShippedDate": "1998-04-14T00:00:00", + "ShipVia": 2, + "Freight": "197.3000", + "ShipName": "Folk och f\u00e4 HB", + "ShipAddress": "\u00c5kergatan 24", + "ShipCity": "Br\u00e4cke", + "ShipRegion": null, + "ShipPostalCode": "S-844 67", + "ShipCountry": "Sweden" + }, { + "OrderID": 11005, + "CustomerID": "WILMK", + "EmployeeID": 2, + "OrderDate": "1998-04-07T00:00:00", + "RequiredDate": "1998-05-05T00:00:00", + "ShippedDate": "1998-04-10T00:00:00", + "ShipVia": 1, + "Freight": "0.7500", + "ShipName": "Wilman Kala", + "ShipAddress": "Keskuskatu 45", + "ShipCity": "Helsinki", + "ShipRegion": null, + "ShipPostalCode": "21240", + "ShipCountry": "Finland" + }, { + "OrderID": 11009, + "CustomerID": "GODOS", + "EmployeeID": 2, + "OrderDate": "1998-04-08T00:00:00", + "RequiredDate": "1998-05-06T00:00:00", + "ShippedDate": "1998-04-10T00:00:00", + "ShipVia": 1, + "Freight": "59.1100", + "ShipName": "Godos Cocina T\u00edpica", + "ShipAddress": "C/ Romero, 33", + "ShipCity": "Sevilla", + "ShipRegion": null, + "ShipPostalCode": "41101", + "ShipCountry": "Spain" + }, { + "OrderID": 11010, + "CustomerID": "REGGC", + "EmployeeID": 2, + "OrderDate": "1998-04-09T00:00:00", + "RequiredDate": "1998-05-07T00:00:00", + "ShippedDate": "1998-04-21T00:00:00", + "ShipVia": 2, + "Freight": "28.7100", + "ShipName": "Reggiani Caseifici", + "ShipAddress": "Strada Provinciale 124", + "ShipCity": "Reggio Emilia", + "ShipRegion": null, + "ShipPostalCode": "42100", + "ShipCountry": "Italy" + }, { + "OrderID": 11013, + "CustomerID": "ROMEY", + "EmployeeID": 2, + "OrderDate": "1998-04-09T00:00:00", + "RequiredDate": "1998-05-07T00:00:00", + "ShippedDate": "1998-04-10T00:00:00", + "ShipVia": 1, + "Freight": "32.9900", + "ShipName": "Romero y tomillo", + "ShipAddress": "Gran V\u00eda, 1", + "ShipCity": "Madrid", + "ShipRegion": null, + "ShipPostalCode": "28001", + "ShipCountry": "Spain" + }, { + "OrderID": 11014, + "CustomerID": "LINOD", + "EmployeeID": 2, + "OrderDate": "1998-04-10T00:00:00", + "RequiredDate": "1998-05-08T00:00:00", + "ShippedDate": "1998-04-15T00:00:00", + "ShipVia": 3, + "Freight": "23.6000", + "ShipName": "LINO-Delicateses", + "ShipAddress": "Ave. 5 de Mayo Porlamar", + "ShipCity": "I. de Margarita", + "ShipRegion": "Nueva Esparta", + "ShipPostalCode": "4980", + "ShipCountry": "Venezuela" + }, { + "OrderID": 11015, + "CustomerID": "SANTG", + "EmployeeID": 2, + "OrderDate": "1998-04-10T00:00:00", + "RequiredDate": "1998-04-24T00:00:00", + "ShippedDate": "1998-04-20T00:00:00", + "ShipVia": 2, + "Freight": "4.6200", + "ShipName": "Sant\u00e9 Gourmet", + "ShipAddress": "Erling Skakkes gate 78", + "ShipCity": "Stavern", + "ShipRegion": null, + "ShipPostalCode": "4110", + "ShipCountry": "Norway" + }, { + "OrderID": 11020, + "CustomerID": "OTTIK", + "EmployeeID": 2, + "OrderDate": "1998-04-14T00:00:00", + "RequiredDate": "1998-05-12T00:00:00", + "ShippedDate": "1998-04-16T00:00:00", + "ShipVia": 2, + "Freight": "43.3000", + "ShipName": "Ottilies K\u00e4seladen", + "ShipAddress": "Mehrheimerstr. 369", + "ShipCity": "K\u00f6ln", + "ShipRegion": null, + "ShipPostalCode": "50739", + "ShipCountry": "Germany" + }, { + "OrderID": 11028, + "CustomerID": "KOENE", + "EmployeeID": 2, + "OrderDate": "1998-04-16T00:00:00", + "RequiredDate": "1998-05-14T00:00:00", + "ShippedDate": "1998-04-22T00:00:00", + "ShipVia": 1, + "Freight": "29.5900", + "ShipName": "K\u00f6niglich Essen", + "ShipAddress": "Maubelstr. 90", + "ShipCity": "Brandenburg", + "ShipRegion": null, + "ShipPostalCode": "14776", + "ShipCountry": "Germany" + }, { + "OrderID": 11032, + "CustomerID": "WHITC", + "EmployeeID": 2, + "OrderDate": "1998-04-17T00:00:00", + "RequiredDate": "1998-05-15T00:00:00", + "ShippedDate": "1998-04-23T00:00:00", + "ShipVia": 3, + "Freight": "606.1900", + "ShipName": "White Clover Markets", + "ShipAddress": "1029 - 12th Ave. S.", + "ShipCity": "Seattle", + "ShipRegion": "WA", + "ShipPostalCode": "98124", + "ShipCountry": "USA" + }, { + "OrderID": 11035, + "CustomerID": "SUPRD", + "EmployeeID": 2, + "OrderDate": "1998-04-20T00:00:00", + "RequiredDate": "1998-05-18T00:00:00", + "ShippedDate": "1998-04-24T00:00:00", + "ShipVia": 2, + "Freight": "0.1700", + "ShipName": "Supr\u00eames d\u00e9lices", + "ShipAddress": "Boulevard Tirou, 255", + "ShipCity": "Charleroi", + "ShipRegion": null, + "ShipPostalCode": "B-6000", + "ShipCountry": "Belgium" + }, { + "OrderID": 11042, + "CustomerID": "COMMI", + "EmployeeID": 2, + "OrderDate": "1998-04-22T00:00:00", + "RequiredDate": "1998-05-06T00:00:00", + "ShippedDate": "1998-05-01T00:00:00", + "ShipVia": 1, + "Freight": "29.9900", + "ShipName": "Com\u00e9rcio Mineiro", + "ShipAddress": "Av. dos Lus\u00edadas, 23", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05432-043", + "ShipCountry": "Brazil" + }, { + "OrderID": 11053, + "CustomerID": "PICCO", + "EmployeeID": 2, + "OrderDate": "1998-04-27T00:00:00", + "RequiredDate": "1998-05-25T00:00:00", + "ShippedDate": "1998-04-29T00:00:00", + "ShipVia": 2, + "Freight": "53.0500", + "ShipName": "Piccolo und mehr", + "ShipAddress": "Geislweg 14", + "ShipCity": "Salzburg", + "ShipRegion": null, + "ShipPostalCode": "5020", + "ShipCountry": "Austria" + }, { + "OrderID": 11059, + "CustomerID": "RICAR", + "EmployeeID": 2, + "OrderDate": "1998-04-29T00:00:00", + "RequiredDate": "1998-06-10T00:00:00", + "ShippedDate": null, + "ShipVia": 2, + "Freight": "85.8000", + "ShipName": "Ricardo Adocicados", + "ShipAddress": "Av. Copacabana, 267", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "02389-890", + "ShipCountry": "Brazil" + }, { + "OrderID": 11060, + "CustomerID": "FRANS", + "EmployeeID": 2, + "OrderDate": "1998-04-30T00:00:00", + "RequiredDate": "1998-05-28T00:00:00", + "ShippedDate": "1998-05-04T00:00:00", + "ShipVia": 2, + "Freight": "10.9800", + "ShipName": "Franchi S.p.A.", + "ShipAddress": "Via Monte Bianco 34", + "ShipCity": "Torino", + "ShipRegion": null, + "ShipPostalCode": "10100", + "ShipCountry": "Italy" + }, { + "OrderID": 11070, + "CustomerID": "LEHMS", + "EmployeeID": 2, + "OrderDate": "1998-05-05T00:00:00", + "RequiredDate": "1998-06-02T00:00:00", + "ShippedDate": null, + "ShipVia": 1, + "Freight": "136.0000", + "ShipName": "Lehmanns Marktstand", + "ShipAddress": "Magazinweg 7", + "ShipCity": "Frankfurt a.M.", + "ShipRegion": null, + "ShipPostalCode": "60528", + "ShipCountry": "Germany" + }, { + "OrderID": 11073, + "CustomerID": "PERIC", + "EmployeeID": 2, + "OrderDate": "1998-05-05T00:00:00", + "RequiredDate": "1998-06-02T00:00:00", + "ShippedDate": null, + "ShipVia": 2, + "Freight": "24.9500", + "ShipName": "Pericles Comidas cl\u00e1sicas", + "ShipAddress": "Calle Dr. Jorge Cash 321", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05033", + "ShipCountry": "Mexico" + }], + "EmployeeID": 2, + "LastName": "Fuller", + "FirstName": "Andrew", + "Title": "Vice President, Sales", + "TitleOfCourtesy": "Dr.", + "BirthDate": "1952-02-19T00:00:00", + "HireDate": "1992-08-14T00:00:00", + "Address": "908 W. Capital Way", + "City": "Tacoma", + "Region": "WA", + "PostalCode": "98401", + "Country": "USA", + "HomePhone": "(206) 555-9482", + "Extension": "3457", + "Notes": "Andrew received his BTS commercial in 1974 and a Ph.D. in international marketing from the University of Dallas in 1981. He is fluent in French and Italian and reads German. He joined the company as a sales representative, was promoted to sales manager in January 1992 and to vice president of sales in March 1993. Andrew is a member of the Sales Management Roundtable, the Seattle Chamber of Commerce, and the Pacific Rim Importers Association.", + "ReportsTo": null, + "PhotoPath": "http://accweb/emmployees/fuller.bmp" + }, { + "Orders": [{ + "OrderID": 10251, + "CustomerID": "VICTE", + "EmployeeID": 3, + "OrderDate": "1996-07-08T00:00:00", + "RequiredDate": "1996-08-05T00:00:00", + "ShippedDate": "1996-07-15T00:00:00", + "ShipVia": 1, + "Freight": "41.3400", + "ShipName": "Victuailles en stock", + "ShipAddress": "2, rue du Commerce", + "ShipCity": "Lyon", + "ShipRegion": null, + "ShipPostalCode": "69004", + "ShipCountry": "France" + }, { + "OrderID": 10253, + "CustomerID": "HANAR", + "EmployeeID": 3, + "OrderDate": "1996-07-10T00:00:00", + "RequiredDate": "1996-07-24T00:00:00", + "ShippedDate": "1996-07-16T00:00:00", + "ShipVia": 2, + "Freight": "58.1700", + "ShipName": "Hanari Carnes", + "ShipAddress": "Rua do Pa\u00e7o, 67", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "05454-876", + "ShipCountry": "Brazil" + }, { + "OrderID": 10256, + "CustomerID": "WELLI", + "EmployeeID": 3, + "OrderDate": "1996-07-15T00:00:00", + "RequiredDate": "1996-08-12T00:00:00", + "ShippedDate": "1996-07-17T00:00:00", + "ShipVia": 2, + "Freight": "13.9700", + "ShipName": "Wellington Importadora", + "ShipAddress": "Rua do Mercado, 12", + "ShipCity": "Resende", + "ShipRegion": "SP", + "ShipPostalCode": "08737-363", + "ShipCountry": "Brazil" + }, { + "OrderID": 10266, + "CustomerID": "WARTH", + "EmployeeID": 3, + "OrderDate": "1996-07-26T00:00:00", + "RequiredDate": "1996-09-06T00:00:00", + "ShippedDate": "1996-07-31T00:00:00", + "ShipVia": 3, + "Freight": "25.7300", + "ShipName": "Wartian Herkku", + "ShipAddress": "Torikatu 38", + "ShipCity": "Oulu", + "ShipRegion": null, + "ShipPostalCode": "90110", + "ShipCountry": "Finland" + }, { + "OrderID": 10273, + "CustomerID": "QUICK", + "EmployeeID": 3, + "OrderDate": "1996-08-05T00:00:00", + "RequiredDate": "1996-09-02T00:00:00", + "ShippedDate": "1996-08-12T00:00:00", + "ShipVia": 3, + "Freight": "76.0700", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10283, + "CustomerID": "LILAS", + "EmployeeID": 3, + "OrderDate": "1996-08-16T00:00:00", + "RequiredDate": "1996-09-13T00:00:00", + "ShippedDate": "1996-08-23T00:00:00", + "ShipVia": 3, + "Freight": "84.8100", + "ShipName": "LILA-Supermercado", + "ShipAddress": "Carrera 52 con Ave. Bol\u00edvar #65-98 Llano Largo", + "ShipCity": "Barquisimeto", + "ShipRegion": "Lara", + "ShipPostalCode": "3508", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10309, + "CustomerID": "HUNGO", + "EmployeeID": 3, + "OrderDate": "1996-09-19T00:00:00", + "RequiredDate": "1996-10-17T00:00:00", + "ShippedDate": "1996-10-23T00:00:00", + "ShipVia": 1, + "Freight": "47.3000", + "ShipName": "Hungry Owl All-Night Grocers", + "ShipAddress": "8 Johnstown Road", + "ShipCity": "Cork", + "ShipRegion": "Co. Cork", + "ShipPostalCode": null, + "ShipCountry": "Ireland" + }, { + "OrderID": 10321, + "CustomerID": "ISLAT", + "EmployeeID": 3, + "OrderDate": "1996-10-03T00:00:00", + "RequiredDate": "1996-10-31T00:00:00", + "ShippedDate": "1996-10-11T00:00:00", + "ShipVia": 2, + "Freight": "3.4300", + "ShipName": "Island Trading", + "ShipAddress": "Garden House Crowther Way", + "ShipCity": "Cowes", + "ShipRegion": "Isle of Wight", + "ShipPostalCode": "PO31 7PJ", + "ShipCountry": "UK" + }, { + "OrderID": 10330, + "CustomerID": "LILAS", + "EmployeeID": 3, + "OrderDate": "1996-10-16T00:00:00", + "RequiredDate": "1996-11-13T00:00:00", + "ShippedDate": "1996-10-28T00:00:00", + "ShipVia": 1, + "Freight": "12.7500", + "ShipName": "LILA-Supermercado", + "ShipAddress": "Carrera 52 con Ave. Bol\u00edvar #65-98 Llano Largo", + "ShipCity": "Barquisimeto", + "ShipRegion": "Lara", + "ShipPostalCode": "3508", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10332, + "CustomerID": "MEREP", + "EmployeeID": 3, + "OrderDate": "1996-10-17T00:00:00", + "RequiredDate": "1996-11-28T00:00:00", + "ShippedDate": "1996-10-21T00:00:00", + "ShipVia": 2, + "Freight": "52.8400", + "ShipName": "M\u00e8re Paillarde", + "ShipAddress": "43 rue St. Laurent", + "ShipCity": "Montr\u00e9al", + "ShipRegion": "Qu\u00e9bec", + "ShipPostalCode": "H1J 1C3", + "ShipCountry": "Canada" + }, { + "OrderID": 10346, + "CustomerID": "RATTC", + "EmployeeID": 3, + "OrderDate": "1996-11-05T00:00:00", + "RequiredDate": "1996-12-17T00:00:00", + "ShippedDate": "1996-11-08T00:00:00", + "ShipVia": 3, + "Freight": "142.0800", + "ShipName": "Rattlesnake Canyon Grocery", + "ShipAddress": "2817 Milton Dr.", + "ShipCity": "Albuquerque", + "ShipRegion": "NM", + "ShipPostalCode": "87110", + "ShipCountry": "USA" + }, { + "OrderID": 10352, + "CustomerID": "FURIB", + "EmployeeID": 3, + "OrderDate": "1996-11-12T00:00:00", + "RequiredDate": "1996-11-26T00:00:00", + "ShippedDate": "1996-11-18T00:00:00", + "ShipVia": 3, + "Freight": "1.3000", + "ShipName": "Furia Bacalhau e Frutos do Mar", + "ShipAddress": "Jardim das rosas n. 32", + "ShipCity": "Lisboa", + "ShipRegion": null, + "ShipPostalCode": "1675", + "ShipCountry": "Portugal" + }, { + "OrderID": 10362, + "CustomerID": "BONAP", + "EmployeeID": 3, + "OrderDate": "1996-11-25T00:00:00", + "RequiredDate": "1996-12-23T00:00:00", + "ShippedDate": "1996-11-28T00:00:00", + "ShipVia": 1, + "Freight": "96.0400", + "ShipName": "Bon app'", + "ShipAddress": "12, rue des Bouchers", + "ShipCity": "Marseille", + "ShipRegion": null, + "ShipPostalCode": "13008", + "ShipCountry": "France" + }, { + "OrderID": 10365, + "CustomerID": "ANTON", + "EmployeeID": 3, + "OrderDate": "1996-11-27T00:00:00", + "RequiredDate": "1996-12-25T00:00:00", + "ShippedDate": "1996-12-02T00:00:00", + "ShipVia": 2, + "Freight": "22.0000", + "ShipName": "Antonio Moreno Taquer\u00eda", + "ShipAddress": "Mataderos 2312", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05023", + "ShipCountry": "Mexico" + }, { + "OrderID": 10375, + "CustomerID": "HUNGC", + "EmployeeID": 3, + "OrderDate": "1996-12-06T00:00:00", + "RequiredDate": "1997-01-03T00:00:00", + "ShippedDate": "1996-12-09T00:00:00", + "ShipVia": 2, + "Freight": "20.1200", + "ShipName": "Hungry Coyote Import Store", + "ShipAddress": "City Center Plaza 516 Main St.", + "ShipCity": "Elgin", + "ShipRegion": "OR", + "ShipPostalCode": "97827", + "ShipCountry": "USA" + }, { + "OrderID": 10381, + "CustomerID": "LILAS", + "EmployeeID": 3, + "OrderDate": "1996-12-12T00:00:00", + "RequiredDate": "1997-01-09T00:00:00", + "ShippedDate": "1996-12-13T00:00:00", + "ShipVia": 3, + "Freight": "7.9900", + "ShipName": "LILA-Supermercado", + "ShipAddress": "Carrera 52 con Ave. Bol\u00edvar #65-98 Llano Largo", + "ShipCity": "Barquisimeto", + "ShipRegion": "Lara", + "ShipPostalCode": "3508", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10384, + "CustomerID": "BERGS", + "EmployeeID": 3, + "OrderDate": "1996-12-16T00:00:00", + "RequiredDate": "1997-01-13T00:00:00", + "ShippedDate": "1996-12-20T00:00:00", + "ShipVia": 3, + "Freight": "168.6400", + "ShipName": "Berglunds snabbk\u00f6p", + "ShipAddress": "Berguvsv\u00e4gen 8", + "ShipCity": "Lule\u00e5", + "ShipRegion": null, + "ShipPostalCode": "S-958 22", + "ShipCountry": "Sweden" + }, { + "OrderID": 10391, + "CustomerID": "DRACD", + "EmployeeID": 3, + "OrderDate": "1996-12-23T00:00:00", + "RequiredDate": "1997-01-20T00:00:00", + "ShippedDate": "1996-12-31T00:00:00", + "ShipVia": 3, + "Freight": "5.4500", + "ShipName": "Drachenblut Delikatessen", + "ShipAddress": "Walserweg 21", + "ShipCity": "Aachen", + "ShipRegion": null, + "ShipPostalCode": "52066", + "ShipCountry": "Germany" + }, { + "OrderID": 10409, + "CustomerID": "OCEAN", + "EmployeeID": 3, + "OrderDate": "1997-01-09T00:00:00", + "RequiredDate": "1997-02-06T00:00:00", + "ShippedDate": "1997-01-14T00:00:00", + "ShipVia": 1, + "Freight": "29.8300", + "ShipName": "Oc\u00e9ano Atl\u00e1ntico Ltda.", + "ShipAddress": "Ing. Gustavo Moncada 8585 Piso 20-A", + "ShipCity": "Buenos Aires", + "ShipRegion": null, + "ShipPostalCode": "1010", + "ShipCountry": "Argentina" + }, { + "OrderID": 10410, + "CustomerID": "BOTTM", + "EmployeeID": 3, + "OrderDate": "1997-01-10T00:00:00", + "RequiredDate": "1997-02-07T00:00:00", + "ShippedDate": "1997-01-15T00:00:00", + "ShipVia": 3, + "Freight": "2.4000", + "ShipName": "Bottom-Dollar Markets", + "ShipAddress": "23 Tsawassen Blvd.", + "ShipCity": "Tsawassen", + "ShipRegion": "BC", + "ShipPostalCode": "T2F 8M4", + "ShipCountry": "Canada" + }, { + "OrderID": 10413, + "CustomerID": "LAMAI", + "EmployeeID": 3, + "OrderDate": "1997-01-14T00:00:00", + "RequiredDate": "1997-02-11T00:00:00", + "ShippedDate": "1997-01-16T00:00:00", + "ShipVia": 2, + "Freight": "95.6600", + "ShipName": "La maison d'Asie", + "ShipAddress": "1 rue Alsace-Lorraine", + "ShipCity": "Toulouse", + "ShipRegion": null, + "ShipPostalCode": "31000", + "ShipCountry": "France" + }, { + "OrderID": 10415, + "CustomerID": "HUNGC", + "EmployeeID": 3, + "OrderDate": "1997-01-15T00:00:00", + "RequiredDate": "1997-02-12T00:00:00", + "ShippedDate": "1997-01-24T00:00:00", + "ShipVia": 1, + "Freight": "0.2000", + "ShipName": "Hungry Coyote Import Store", + "ShipAddress": "City Center Plaza 516 Main St.", + "ShipCity": "Elgin", + "ShipRegion": "OR", + "ShipPostalCode": "97827", + "ShipCountry": "USA" + }, { + "OrderID": 10420, + "CustomerID": "WELLI", + "EmployeeID": 3, + "OrderDate": "1997-01-21T00:00:00", + "RequiredDate": "1997-02-18T00:00:00", + "ShippedDate": "1997-01-27T00:00:00", + "ShipVia": 1, + "Freight": "44.1200", + "ShipName": "Wellington Importadora", + "ShipAddress": "Rua do Mercado, 12", + "ShipCity": "Resende", + "ShipRegion": "SP", + "ShipPostalCode": "08737-363", + "ShipCountry": "Brazil" + }, { + "OrderID": 10429, + "CustomerID": "HUNGO", + "EmployeeID": 3, + "OrderDate": "1997-01-29T00:00:00", + "RequiredDate": "1997-03-12T00:00:00", + "ShippedDate": "1997-02-07T00:00:00", + "ShipVia": 2, + "Freight": "56.6300", + "ShipName": "Hungry Owl All-Night Grocers", + "ShipAddress": "8 Johnstown Road", + "ShipCity": "Cork", + "ShipRegion": "Co. Cork", + "ShipPostalCode": null, + "ShipCountry": "Ireland" + }, { + "OrderID": 10432, + "CustomerID": "SPLIR", + "EmployeeID": 3, + "OrderDate": "1997-01-31T00:00:00", + "RequiredDate": "1997-02-14T00:00:00", + "ShippedDate": "1997-02-07T00:00:00", + "ShipVia": 2, + "Freight": "4.3400", + "ShipName": "Split Rail Beer & Ale", + "ShipAddress": "P.O. Box 555", + "ShipCity": "Lander", + "ShipRegion": "WY", + "ShipPostalCode": "82520", + "ShipCountry": "USA" + }, { + "OrderID": 10433, + "CustomerID": "PRINI", + "EmployeeID": 3, + "OrderDate": "1997-02-03T00:00:00", + "RequiredDate": "1997-03-03T00:00:00", + "ShippedDate": "1997-03-04T00:00:00", + "ShipVia": 3, + "Freight": "73.8300", + "ShipName": "Princesa Isabel Vinhos", + "ShipAddress": "Estrada da sa\u00fade n. 58", + "ShipCity": "Lisboa", + "ShipRegion": null, + "ShipPostalCode": "1756", + "ShipCountry": "Portugal" + }, { + "OrderID": 10434, + "CustomerID": "FOLKO", + "EmployeeID": 3, + "OrderDate": "1997-02-03T00:00:00", + "RequiredDate": "1997-03-03T00:00:00", + "ShippedDate": "1997-02-13T00:00:00", + "ShipVia": 2, + "Freight": "17.9200", + "ShipName": "Folk och f\u00e4 HB", + "ShipAddress": "\u00c5kergatan 24", + "ShipCity": "Br\u00e4cke", + "ShipRegion": null, + "ShipPostalCode": "S-844 67", + "ShipCountry": "Sweden" + }, { + "OrderID": 10436, + "CustomerID": "BLONP", + "EmployeeID": 3, + "OrderDate": "1997-02-05T00:00:00", + "RequiredDate": "1997-03-05T00:00:00", + "ShippedDate": "1997-02-11T00:00:00", + "ShipVia": 2, + "Freight": "156.6600", + "ShipName": "Blondel p\u00e8re et fils", + "ShipAddress": "24, place Kl\u00e9ber", + "ShipCity": "Strasbourg", + "ShipRegion": null, + "ShipPostalCode": "67000", + "ShipCountry": "France" + }, { + "OrderID": 10438, + "CustomerID": "TOMSP", + "EmployeeID": 3, + "OrderDate": "1997-02-06T00:00:00", + "RequiredDate": "1997-03-06T00:00:00", + "ShippedDate": "1997-02-14T00:00:00", + "ShipVia": 2, + "Freight": "8.2400", + "ShipName": "Toms Spezialit\u00e4ten", + "ShipAddress": "Luisenstr. 48", + "ShipCity": "M\u00fcnster", + "ShipRegion": null, + "ShipPostalCode": "44087", + "ShipCountry": "Germany" + }, { + "OrderID": 10441, + "CustomerID": "OLDWO", + "EmployeeID": 3, + "OrderDate": "1997-02-10T00:00:00", + "RequiredDate": "1997-03-24T00:00:00", + "ShippedDate": "1997-03-14T00:00:00", + "ShipVia": 2, + "Freight": "73.0200", + "ShipName": "Old World Delicatessen", + "ShipAddress": "2743 Bering St.", + "ShipCity": "Anchorage", + "ShipRegion": "AK", + "ShipPostalCode": "99508", + "ShipCountry": "USA" + }, { + "OrderID": 10442, + "CustomerID": "ERNSH", + "EmployeeID": 3, + "OrderDate": "1997-02-11T00:00:00", + "RequiredDate": "1997-03-11T00:00:00", + "ShippedDate": "1997-02-18T00:00:00", + "ShipVia": 2, + "Freight": "47.9400", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10444, + "CustomerID": "BERGS", + "EmployeeID": 3, + "OrderDate": "1997-02-12T00:00:00", + "RequiredDate": "1997-03-12T00:00:00", + "ShippedDate": "1997-02-21T00:00:00", + "ShipVia": 3, + "Freight": "3.5000", + "ShipName": "Berglunds snabbk\u00f6p", + "ShipAddress": "Berguvsv\u00e4gen 8", + "ShipCity": "Lule\u00e5", + "ShipRegion": null, + "ShipPostalCode": "S-958 22", + "ShipCountry": "Sweden" + }, { + "OrderID": 10445, + "CustomerID": "BERGS", + "EmployeeID": 3, + "OrderDate": "1997-02-13T00:00:00", + "RequiredDate": "1997-03-13T00:00:00", + "ShippedDate": "1997-02-20T00:00:00", + "ShipVia": 1, + "Freight": "9.3000", + "ShipName": "Berglunds snabbk\u00f6p", + "ShipAddress": "Berguvsv\u00e4gen 8", + "ShipCity": "Lule\u00e5", + "ShipRegion": null, + "ShipPostalCode": "S-958 22", + "ShipCountry": "Sweden" + }, { + "OrderID": 10449, + "CustomerID": "BLONP", + "EmployeeID": 3, + "OrderDate": "1997-02-18T00:00:00", + "RequiredDate": "1997-03-18T00:00:00", + "ShippedDate": "1997-02-27T00:00:00", + "ShipVia": 2, + "Freight": "53.3000", + "ShipName": "Blondel p\u00e8re et fils", + "ShipAddress": "24, place Kl\u00e9ber", + "ShipCity": "Strasbourg", + "ShipRegion": null, + "ShipPostalCode": "67000", + "ShipCountry": "France" + }, { + "OrderID": 10468, + "CustomerID": "KOENE", + "EmployeeID": 3, + "OrderDate": "1997-03-07T00:00:00", + "RequiredDate": "1997-04-04T00:00:00", + "ShippedDate": "1997-03-12T00:00:00", + "ShipVia": 3, + "Freight": "44.1200", + "ShipName": "K\u00f6niglich Essen", + "ShipAddress": "Maubelstr. 90", + "ShipCity": "Brandenburg", + "ShipRegion": null, + "ShipPostalCode": "14776", + "ShipCountry": "Germany" + }, { + "OrderID": 10479, + "CustomerID": "RATTC", + "EmployeeID": 3, + "OrderDate": "1997-03-19T00:00:00", + "RequiredDate": "1997-04-16T00:00:00", + "ShippedDate": "1997-03-21T00:00:00", + "ShipVia": 3, + "Freight": "708.9500", + "ShipName": "Rattlesnake Canyon Grocery", + "ShipAddress": "2817 Milton Dr.", + "ShipCity": "Albuquerque", + "ShipRegion": "NM", + "ShipPostalCode": "87110", + "ShipCountry": "USA" + }, { + "OrderID": 10484, + "CustomerID": "BSBEV", + "EmployeeID": 3, + "OrderDate": "1997-03-24T00:00:00", + "RequiredDate": "1997-04-21T00:00:00", + "ShippedDate": "1997-04-01T00:00:00", + "ShipVia": 3, + "Freight": "6.8800", + "ShipName": "B's Beverages", + "ShipAddress": "Fauntleroy Circus", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "EC2 5NT", + "ShipCountry": "UK" + }, { + "OrderID": 10492, + "CustomerID": "BOTTM", + "EmployeeID": 3, + "OrderDate": "1997-04-01T00:00:00", + "RequiredDate": "1997-04-29T00:00:00", + "ShippedDate": "1997-04-11T00:00:00", + "ShipVia": 1, + "Freight": "62.8900", + "ShipName": "Bottom-Dollar Markets", + "ShipAddress": "23 Tsawassen Blvd.", + "ShipCity": "Tsawassen", + "ShipRegion": "BC", + "ShipPostalCode": "T2F 8M4", + "ShipCountry": "Canada" + }, { + "OrderID": 10495, + "CustomerID": "LAUGB", + "EmployeeID": 3, + "OrderDate": "1997-04-03T00:00:00", + "RequiredDate": "1997-05-01T00:00:00", + "ShippedDate": "1997-04-11T00:00:00", + "ShipVia": 3, + "Freight": "4.6500", + "ShipName": "Laughing Bacchus Wine Cellars", + "ShipAddress": "2319 Elm St.", + "ShipCity": "Vancouver", + "ShipRegion": "BC", + "ShipPostalCode": "V3F 2K1", + "ShipCountry": "Canada" + }, { + "OrderID": 10505, + "CustomerID": "MEREP", + "EmployeeID": 3, + "OrderDate": "1997-04-14T00:00:00", + "RequiredDate": "1997-05-12T00:00:00", + "ShippedDate": "1997-04-21T00:00:00", + "ShipVia": 3, + "Freight": "7.1300", + "ShipName": "M\u00e8re Paillarde", + "ShipAddress": "43 rue St. Laurent", + "ShipCity": "Montr\u00e9al", + "ShipRegion": "Qu\u00e9bec", + "ShipPostalCode": "H1J 1C3", + "ShipCountry": "Canada" + }, { + "OrderID": 10514, + "CustomerID": "ERNSH", + "EmployeeID": 3, + "OrderDate": "1997-04-22T00:00:00", + "RequiredDate": "1997-05-20T00:00:00", + "ShippedDate": "1997-05-16T00:00:00", + "ShipVia": 2, + "Freight": "789.9500", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10517, + "CustomerID": "NORTS", + "EmployeeID": 3, + "OrderDate": "1997-04-24T00:00:00", + "RequiredDate": "1997-05-22T00:00:00", + "ShippedDate": "1997-04-29T00:00:00", + "ShipVia": 3, + "Freight": "32.0700", + "ShipName": "North/South", + "ShipAddress": "South House 300 Queensbridge", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "SW7 1RZ", + "ShipCountry": "UK" + }, { + "OrderID": 10530, + "CustomerID": "PICCO", + "EmployeeID": 3, + "OrderDate": "1997-05-08T00:00:00", + "RequiredDate": "1997-06-05T00:00:00", + "ShippedDate": "1997-05-12T00:00:00", + "ShipVia": 2, + "Freight": "339.2200", + "ShipName": "Piccolo und mehr", + "ShipAddress": "Geislweg 14", + "ShipCity": "Salzburg", + "ShipRegion": null, + "ShipPostalCode": "5020", + "ShipCountry": "Austria" + }, { + "OrderID": 10536, + "CustomerID": "LEHMS", + "EmployeeID": 3, + "OrderDate": "1997-05-14T00:00:00", + "RequiredDate": "1997-06-11T00:00:00", + "ShippedDate": "1997-06-06T00:00:00", + "ShipVia": 2, + "Freight": "58.8800", + "ShipName": "Lehmanns Marktstand", + "ShipAddress": "Magazinweg 7", + "ShipCity": "Frankfurt a.M.", + "ShipRegion": null, + "ShipPostalCode": "60528", + "ShipCountry": "Germany" + }, { + "OrderID": 10540, + "CustomerID": "QUICK", + "EmployeeID": 3, + "OrderDate": "1997-05-19T00:00:00", + "RequiredDate": "1997-06-16T00:00:00", + "ShippedDate": "1997-06-13T00:00:00", + "ShipVia": 3, + "Freight": "1007.6400", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10547, + "CustomerID": "SEVES", + "EmployeeID": 3, + "OrderDate": "1997-05-23T00:00:00", + "RequiredDate": "1997-06-20T00:00:00", + "ShippedDate": "1997-06-02T00:00:00", + "ShipVia": 2, + "Freight": "178.4300", + "ShipName": "Seven Seas Imports", + "ShipAddress": "90 Wadhurst Rd.", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "OX15 4NB", + "ShipCountry": "UK" + }, { + "OrderID": 10548, + "CustomerID": "TOMSP", + "EmployeeID": 3, + "OrderDate": "1997-05-26T00:00:00", + "RequiredDate": "1997-06-23T00:00:00", + "ShippedDate": "1997-06-02T00:00:00", + "ShipVia": 2, + "Freight": "1.4300", + "ShipName": "Toms Spezialit\u00e4ten", + "ShipAddress": "Luisenstr. 48", + "ShipCity": "M\u00fcnster", + "ShipRegion": null, + "ShipPostalCode": "44087", + "ShipCountry": "Germany" + }, { + "OrderID": 10568, + "CustomerID": "GALED", + "EmployeeID": 3, + "OrderDate": "1997-06-13T00:00:00", + "RequiredDate": "1997-07-11T00:00:00", + "ShippedDate": "1997-07-09T00:00:00", + "ShipVia": 3, + "Freight": "6.5400", + "ShipName": "Galer\u00eda del gastron\u00f3mo", + "ShipAddress": "Rambla de Catalu\u00f1a, 23", + "ShipCity": "Barcelona", + "ShipRegion": null, + "ShipPostalCode": "8022", + "ShipCountry": "Spain" + }, { + "OrderID": 10570, + "CustomerID": "MEREP", + "EmployeeID": 3, + "OrderDate": "1997-06-17T00:00:00", + "RequiredDate": "1997-07-15T00:00:00", + "ShippedDate": "1997-06-19T00:00:00", + "ShipVia": 3, + "Freight": "188.9900", + "ShipName": "M\u00e8re Paillarde", + "ShipAddress": "43 rue St. Laurent", + "ShipCity": "Montr\u00e9al", + "ShipRegion": "Qu\u00e9bec", + "ShipPostalCode": "H1J 1C3", + "ShipCountry": "Canada" + }, { + "OrderID": 10572, + "CustomerID": "BERGS", + "EmployeeID": 3, + "OrderDate": "1997-06-18T00:00:00", + "RequiredDate": "1997-07-16T00:00:00", + "ShippedDate": "1997-06-25T00:00:00", + "ShipVia": 2, + "Freight": "116.4300", + "ShipName": "Berglunds snabbk\u00f6p", + "ShipAddress": "Berguvsv\u00e4gen 8", + "ShipCity": "Lule\u00e5", + "ShipRegion": null, + "ShipPostalCode": "S-958 22", + "ShipCountry": "Sweden" + }, { + "OrderID": 10576, + "CustomerID": "TORTU", + "EmployeeID": 3, + "OrderDate": "1997-06-23T00:00:00", + "RequiredDate": "1997-07-07T00:00:00", + "ShippedDate": "1997-06-30T00:00:00", + "ShipVia": 3, + "Freight": "18.5600", + "ShipName": "Tortuga Restaurante", + "ShipAddress": "Avda. Azteca 123", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05033", + "ShipCountry": "Mexico" + }, { + "OrderID": 10581, + "CustomerID": "FAMIA", + "EmployeeID": 3, + "OrderDate": "1997-06-26T00:00:00", + "RequiredDate": "1997-07-24T00:00:00", + "ShippedDate": "1997-07-02T00:00:00", + "ShipVia": 1, + "Freight": "3.0100", + "ShipName": "Familia Arquibaldo", + "ShipAddress": "Rua Or\u00f3s, 92", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05442-030", + "ShipCountry": "Brazil" + }, { + "OrderID": 10582, + "CustomerID": "BLAUS", + "EmployeeID": 3, + "OrderDate": "1997-06-27T00:00:00", + "RequiredDate": "1997-07-25T00:00:00", + "ShippedDate": "1997-07-14T00:00:00", + "ShipVia": 2, + "Freight": "27.7100", + "ShipName": "Blauer See Delikatessen", + "ShipAddress": "Forsterstr. 57", + "ShipCity": "Mannheim", + "ShipRegion": null, + "ShipPostalCode": "68306", + "ShipCountry": "Germany" + }, { + "OrderID": 10592, + "CustomerID": "LEHMS", + "EmployeeID": 3, + "OrderDate": "1997-07-08T00:00:00", + "RequiredDate": "1997-08-05T00:00:00", + "ShippedDate": "1997-07-16T00:00:00", + "ShipVia": 1, + "Freight": "32.1000", + "ShipName": "Lehmanns Marktstand", + "ShipAddress": "Magazinweg 7", + "ShipCity": "Frankfurt a.M.", + "ShipRegion": null, + "ShipPostalCode": "60528", + "ShipCountry": "Germany" + }, { + "OrderID": 10594, + "CustomerID": "OLDWO", + "EmployeeID": 3, + "OrderDate": "1997-07-09T00:00:00", + "RequiredDate": "1997-08-06T00:00:00", + "ShippedDate": "1997-07-16T00:00:00", + "ShipVia": 2, + "Freight": "5.2400", + "ShipName": "Old World Delicatessen", + "ShipAddress": "2743 Bering St.", + "ShipCity": "Anchorage", + "ShipRegion": "AK", + "ShipPostalCode": "99508", + "ShipCountry": "USA" + }, { + "OrderID": 10619, + "CustomerID": "MEREP", + "EmployeeID": 3, + "OrderDate": "1997-08-04T00:00:00", + "RequiredDate": "1997-09-01T00:00:00", + "ShippedDate": "1997-08-07T00:00:00", + "ShipVia": 3, + "Freight": "91.0500", + "ShipName": "M\u00e8re Paillarde", + "ShipAddress": "43 rue St. Laurent", + "ShipCity": "Montr\u00e9al", + "ShipRegion": "Qu\u00e9bec", + "ShipPostalCode": "H1J 1C3", + "ShipCountry": "Canada" + }, { + "OrderID": 10625, + "CustomerID": "ANATR", + "EmployeeID": 3, + "OrderDate": "1997-08-08T00:00:00", + "RequiredDate": "1997-09-05T00:00:00", + "ShippedDate": "1997-08-14T00:00:00", + "ShipVia": 1, + "Freight": "43.9000", + "ShipName": "Ana Trujillo Emparedados y helados", + "ShipAddress": "Avda. de la Constituci\u00f3n 2222", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05021", + "ShipCountry": "Mexico" + }, { + "OrderID": 10638, + "CustomerID": "LINOD", + "EmployeeID": 3, + "OrderDate": "1997-08-20T00:00:00", + "RequiredDate": "1997-09-17T00:00:00", + "ShippedDate": "1997-09-01T00:00:00", + "ShipVia": 1, + "Freight": "158.4400", + "ShipName": "LINO-Delicateses", + "ShipAddress": "Ave. 5 de Mayo Porlamar", + "ShipCity": "I. de Margarita", + "ShipRegion": "Nueva Esparta", + "ShipPostalCode": "4980", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10644, + "CustomerID": "WELLI", + "EmployeeID": 3, + "OrderDate": "1997-08-25T00:00:00", + "RequiredDate": "1997-09-22T00:00:00", + "ShippedDate": "1997-09-01T00:00:00", + "ShipVia": 2, + "Freight": "0.1400", + "ShipName": "Wellington Importadora", + "ShipAddress": "Rua do Mercado, 12", + "ShipCity": "Resende", + "ShipRegion": "SP", + "ShipPostalCode": "08737-363", + "ShipCountry": "Brazil" + }, { + "OrderID": 10662, + "CustomerID": "LONEP", + "EmployeeID": 3, + "OrderDate": "1997-09-09T00:00:00", + "RequiredDate": "1997-10-07T00:00:00", + "ShippedDate": "1997-09-18T00:00:00", + "ShipVia": 2, + "Freight": "1.2800", + "ShipName": "Lonesome Pine Restaurant", + "ShipAddress": "89 Chiaroscuro Rd.", + "ShipCity": "Portland", + "ShipRegion": "OR", + "ShipPostalCode": "97219", + "ShipCountry": "USA" + }, { + "OrderID": 10681, + "CustomerID": "GREAL", + "EmployeeID": 3, + "OrderDate": "1997-09-25T00:00:00", + "RequiredDate": "1997-10-23T00:00:00", + "ShippedDate": "1997-09-30T00:00:00", + "ShipVia": 3, + "Freight": "76.1300", + "ShipName": "Great Lakes Food Market", + "ShipAddress": "2732 Baker Blvd.", + "ShipCity": "Eugene", + "ShipRegion": "OR", + "ShipPostalCode": "97403", + "ShipCountry": "USA" + }, { + "OrderID": 10682, + "CustomerID": "ANTON", + "EmployeeID": 3, + "OrderDate": "1997-09-25T00:00:00", + "RequiredDate": "1997-10-23T00:00:00", + "ShippedDate": "1997-10-01T00:00:00", + "ShipVia": 2, + "Freight": "36.1300", + "ShipName": "Antonio Moreno Taquer\u00eda", + "ShipAddress": "Mataderos 2312", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05023", + "ShipCountry": "Mexico" + }, { + "OrderID": 10684, + "CustomerID": "OTTIK", + "EmployeeID": 3, + "OrderDate": "1997-09-26T00:00:00", + "RequiredDate": "1997-10-24T00:00:00", + "ShippedDate": "1997-09-30T00:00:00", + "ShipVia": 1, + "Freight": "145.6300", + "ShipName": "Ottilies K\u00e4seladen", + "ShipAddress": "Mehrheimerstr. 369", + "ShipCity": "K\u00f6ln", + "ShipRegion": null, + "ShipPostalCode": "50739", + "ShipCountry": "Germany" + }, { + "OrderID": 10693, + "CustomerID": "WHITC", + "EmployeeID": 3, + "OrderDate": "1997-10-06T00:00:00", + "RequiredDate": "1997-10-20T00:00:00", + "ShippedDate": "1997-10-10T00:00:00", + "ShipVia": 3, + "Freight": "139.3400", + "ShipName": "White Clover Markets", + "ShipAddress": "1029 - 12th Ave. S.", + "ShipCity": "Seattle", + "ShipRegion": "WA", + "ShipPostalCode": "98124", + "ShipCountry": "USA" + }, { + "OrderID": 10697, + "CustomerID": "LINOD", + "EmployeeID": 3, + "OrderDate": "1997-10-08T00:00:00", + "RequiredDate": "1997-11-05T00:00:00", + "ShippedDate": "1997-10-14T00:00:00", + "ShipVia": 1, + "Freight": "45.5200", + "ShipName": "LINO-Delicateses", + "ShipAddress": "Ave. 5 de Mayo Porlamar", + "ShipCity": "I. de Margarita", + "ShipRegion": "Nueva Esparta", + "ShipPostalCode": "4980", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10699, + "CustomerID": "MORGK", + "EmployeeID": 3, + "OrderDate": "1997-10-09T00:00:00", + "RequiredDate": "1997-11-06T00:00:00", + "ShippedDate": "1997-10-13T00:00:00", + "ShipVia": 3, + "Freight": "0.5800", + "ShipName": "Morgenstern Gesundkost", + "ShipAddress": "Heerstr. 22", + "ShipCity": "Leipzig", + "ShipRegion": null, + "ShipPostalCode": "04179", + "ShipCountry": "Germany" + }, { + "OrderID": 10700, + "CustomerID": "SAVEA", + "EmployeeID": 3, + "OrderDate": "1997-10-10T00:00:00", + "RequiredDate": "1997-11-07T00:00:00", + "ShippedDate": "1997-10-16T00:00:00", + "ShipVia": 1, + "Freight": "65.1000", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10712, + "CustomerID": "HUNGO", + "EmployeeID": 3, + "OrderDate": "1997-10-21T00:00:00", + "RequiredDate": "1997-11-18T00:00:00", + "ShippedDate": "1997-10-31T00:00:00", + "ShipVia": 1, + "Freight": "89.9300", + "ShipName": "Hungry Owl All-Night Grocers", + "ShipAddress": "8 Johnstown Road", + "ShipCity": "Cork", + "ShipRegion": "Co. Cork", + "ShipPostalCode": null, + "ShipCountry": "Ireland" + }, { + "OrderID": 10715, + "CustomerID": "BONAP", + "EmployeeID": 3, + "OrderDate": "1997-10-23T00:00:00", + "RequiredDate": "1997-11-06T00:00:00", + "ShippedDate": "1997-10-29T00:00:00", + "ShipVia": 1, + "Freight": "63.2000", + "ShipName": "Bon app'", + "ShipAddress": "12, rue des Bouchers", + "ShipCity": "Marseille", + "ShipRegion": null, + "ShipPostalCode": "13008", + "ShipCountry": "France" + }, { + "OrderID": 10723, + "CustomerID": "WHITC", + "EmployeeID": 3, + "OrderDate": "1997-10-30T00:00:00", + "RequiredDate": "1997-11-27T00:00:00", + "ShippedDate": "1997-11-25T00:00:00", + "ShipVia": 1, + "Freight": "21.7200", + "ShipName": "White Clover Markets", + "ShipAddress": "1029 - 12th Ave. S.", + "ShipCity": "Seattle", + "ShipRegion": "WA", + "ShipPostalCode": "98124", + "ShipCountry": "USA" + }, { + "OrderID": 10732, + "CustomerID": "BONAP", + "EmployeeID": 3, + "OrderDate": "1997-11-06T00:00:00", + "RequiredDate": "1997-12-04T00:00:00", + "ShippedDate": "1997-11-07T00:00:00", + "ShipVia": 1, + "Freight": "16.9700", + "ShipName": "Bon app'", + "ShipAddress": "12, rue des Bouchers", + "ShipCity": "Marseille", + "ShipRegion": null, + "ShipPostalCode": "13008", + "ShipCountry": "France" + }, { + "OrderID": 10739, + "CustomerID": "VINET", + "EmployeeID": 3, + "OrderDate": "1997-11-12T00:00:00", + "RequiredDate": "1997-12-10T00:00:00", + "ShippedDate": "1997-11-17T00:00:00", + "ShipVia": 3, + "Freight": "11.0800", + "ShipName": "Vins et alcools Chevalier", + "ShipAddress": "59 rue de l'Abbaye", + "ShipCity": "Reims", + "ShipRegion": null, + "ShipPostalCode": "51100", + "ShipCountry": "France" + }, { + "OrderID": 10742, + "CustomerID": "BOTTM", + "EmployeeID": 3, + "OrderDate": "1997-11-14T00:00:00", + "RequiredDate": "1997-12-12T00:00:00", + "ShippedDate": "1997-11-18T00:00:00", + "ShipVia": 3, + "Freight": "243.7300", + "ShipName": "Bottom-Dollar Markets", + "ShipAddress": "23 Tsawassen Blvd.", + "ShipCity": "Tsawassen", + "ShipRegion": "BC", + "ShipPostalCode": "T2F 8M4", + "ShipCountry": "Canada" + }, { + "OrderID": 10748, + "CustomerID": "SAVEA", + "EmployeeID": 3, + "OrderDate": "1997-11-20T00:00:00", + "RequiredDate": "1997-12-18T00:00:00", + "ShippedDate": "1997-11-28T00:00:00", + "ShipVia": 1, + "Freight": "232.5500", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10751, + "CustomerID": "RICSU", + "EmployeeID": 3, + "OrderDate": "1997-11-24T00:00:00", + "RequiredDate": "1997-12-22T00:00:00", + "ShippedDate": "1997-12-03T00:00:00", + "ShipVia": 3, + "Freight": "130.7900", + "ShipName": "Richter Supermarkt", + "ShipAddress": "Starenweg 5", + "ShipCity": "Gen\u00e8ve", + "ShipRegion": null, + "ShipPostalCode": "1204", + "ShipCountry": "Switzerland" + }, { + "OrderID": 10753, + "CustomerID": "FRANS", + "EmployeeID": 3, + "OrderDate": "1997-11-25T00:00:00", + "RequiredDate": "1997-12-23T00:00:00", + "ShippedDate": "1997-11-27T00:00:00", + "ShipVia": 1, + "Freight": "7.7000", + "ShipName": "Franchi S.p.A.", + "ShipAddress": "Via Monte Bianco 34", + "ShipCity": "Torino", + "ShipRegion": null, + "ShipPostalCode": "10100", + "ShipCountry": "Italy" + }, { + "OrderID": 10758, + "CustomerID": "RICSU", + "EmployeeID": 3, + "OrderDate": "1997-11-28T00:00:00", + "RequiredDate": "1997-12-26T00:00:00", + "ShippedDate": "1997-12-04T00:00:00", + "ShipVia": 3, + "Freight": "138.1700", + "ShipName": "Richter Supermarkt", + "ShipAddress": "Starenweg 5", + "ShipCity": "Gen\u00e8ve", + "ShipRegion": null, + "ShipPostalCode": "1204", + "ShipCountry": "Switzerland" + }, { + "OrderID": 10759, + "CustomerID": "ANATR", + "EmployeeID": 3, + "OrderDate": "1997-11-28T00:00:00", + "RequiredDate": "1997-12-26T00:00:00", + "ShippedDate": "1997-12-12T00:00:00", + "ShipVia": 3, + "Freight": "11.9900", + "ShipName": "Ana Trujillo Emparedados y helados", + "ShipAddress": "Avda. de la Constituci\u00f3n 2222", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05021", + "ShipCountry": "Mexico" + }, { + "OrderID": 10762, + "CustomerID": "FOLKO", + "EmployeeID": 3, + "OrderDate": "1997-12-02T00:00:00", + "RequiredDate": "1997-12-30T00:00:00", + "ShippedDate": "1997-12-09T00:00:00", + "ShipVia": 1, + "Freight": "328.7400", + "ShipName": "Folk och f\u00e4 HB", + "ShipAddress": "\u00c5kergatan 24", + "ShipCity": "Br\u00e4cke", + "ShipRegion": null, + "ShipPostalCode": "S-844 67", + "ShipCountry": "Sweden" + }, { + "OrderID": 10763, + "CustomerID": "FOLIG", + "EmployeeID": 3, + "OrderDate": "1997-12-03T00:00:00", + "RequiredDate": "1997-12-31T00:00:00", + "ShippedDate": "1997-12-08T00:00:00", + "ShipVia": 3, + "Freight": "37.3500", + "ShipName": "Folies gourmandes", + "ShipAddress": "184, chauss\u00e9e de Tournai", + "ShipCity": "Lille", + "ShipRegion": null, + "ShipPostalCode": "59000", + "ShipCountry": "France" + }, { + "OrderID": 10765, + "CustomerID": "QUICK", + "EmployeeID": 3, + "OrderDate": "1997-12-04T00:00:00", + "RequiredDate": "1998-01-01T00:00:00", + "ShippedDate": "1997-12-09T00:00:00", + "ShipVia": 3, + "Freight": "42.7400", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10768, + "CustomerID": "AROUT", + "EmployeeID": 3, + "OrderDate": "1997-12-08T00:00:00", + "RequiredDate": "1998-01-05T00:00:00", + "ShippedDate": "1997-12-15T00:00:00", + "ShipVia": 2, + "Freight": "146.3200", + "ShipName": "Around the Horn", + "ShipAddress": "Brook Farm Stratford St. Mary", + "ShipCity": "Colchester", + "ShipRegion": "Essex", + "ShipPostalCode": "CO7 6JX", + "ShipCountry": "UK" + }, { + "OrderID": 10769, + "CustomerID": "VAFFE", + "EmployeeID": 3, + "OrderDate": "1997-12-08T00:00:00", + "RequiredDate": "1998-01-05T00:00:00", + "ShippedDate": "1997-12-12T00:00:00", + "ShipVia": 1, + "Freight": "65.0600", + "ShipName": "Vaffeljernet", + "ShipAddress": "Smagsloget 45", + "ShipCity": "\u00c5rhus", + "ShipRegion": null, + "ShipPostalCode": "8200", + "ShipCountry": "Denmark" + }, { + "OrderID": 10772, + "CustomerID": "LEHMS", + "EmployeeID": 3, + "OrderDate": "1997-12-10T00:00:00", + "RequiredDate": "1998-01-07T00:00:00", + "ShippedDate": "1997-12-19T00:00:00", + "ShipVia": 2, + "Freight": "91.2800", + "ShipName": "Lehmanns Marktstand", + "ShipAddress": "Magazinweg 7", + "ShipCity": "Frankfurt a.M.", + "ShipRegion": null, + "ShipPostalCode": "60528", + "ShipCountry": "Germany" + }, { + "OrderID": 10778, + "CustomerID": "BERGS", + "EmployeeID": 3, + "OrderDate": "1997-12-16T00:00:00", + "RequiredDate": "1998-01-13T00:00:00", + "ShippedDate": "1997-12-24T00:00:00", + "ShipVia": 1, + "Freight": "6.7900", + "ShipName": "Berglunds snabbk\u00f6p", + "ShipAddress": "Berguvsv\u00e4gen 8", + "ShipCity": "Lule\u00e5", + "ShipRegion": null, + "ShipPostalCode": "S-958 22", + "ShipCountry": "Sweden" + }, { + "OrderID": 10779, + "CustomerID": "MORGK", + "EmployeeID": 3, + "OrderDate": "1997-12-16T00:00:00", + "RequiredDate": "1998-01-13T00:00:00", + "ShippedDate": "1998-01-14T00:00:00", + "ShipVia": 2, + "Freight": "58.1300", + "ShipName": "Morgenstern Gesundkost", + "ShipAddress": "Heerstr. 22", + "ShipCity": "Leipzig", + "ShipRegion": null, + "ShipPostalCode": "04179", + "ShipCountry": "Germany" + }, { + "OrderID": 10793, + "CustomerID": "AROUT", + "EmployeeID": 3, + "OrderDate": "1997-12-24T00:00:00", + "RequiredDate": "1998-01-21T00:00:00", + "ShippedDate": "1998-01-08T00:00:00", + "ShipVia": 3, + "Freight": "4.5200", + "ShipName": "Around the Horn", + "ShipAddress": "Brook Farm Stratford St. Mary", + "ShipCity": "Colchester", + "ShipRegion": "Essex", + "ShipPostalCode": "CO7 6JX", + "ShipCountry": "UK" + }, { + "OrderID": 10796, + "CustomerID": "HILAA", + "EmployeeID": 3, + "OrderDate": "1997-12-25T00:00:00", + "RequiredDate": "1998-01-22T00:00:00", + "ShippedDate": "1998-01-14T00:00:00", + "ShipVia": 1, + "Freight": "26.5200", + "ShipName": "HILARION-Abastos", + "ShipAddress": "Carrera 22 con Ave. Carlos Soublette #8-35", + "ShipCity": "San Crist\u00f3bal", + "ShipRegion": "T\u00e1chira", + "ShipPostalCode": "5022", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10806, + "CustomerID": "VICTE", + "EmployeeID": 3, + "OrderDate": "1997-12-31T00:00:00", + "RequiredDate": "1998-01-28T00:00:00", + "ShippedDate": "1998-01-05T00:00:00", + "ShipVia": 2, + "Freight": "22.1100", + "ShipName": "Victuailles en stock", + "ShipAddress": "2, rue du Commerce", + "ShipCity": "Lyon", + "ShipRegion": null, + "ShipPostalCode": "69004", + "ShipCountry": "France" + }, { + "OrderID": 10814, + "CustomerID": "VICTE", + "EmployeeID": 3, + "OrderDate": "1998-01-05T00:00:00", + "RequiredDate": "1998-02-02T00:00:00", + "ShippedDate": "1998-01-14T00:00:00", + "ShipVia": 3, + "Freight": "130.9400", + "ShipName": "Victuailles en stock", + "ShipAddress": "2, rue du Commerce", + "ShipCity": "Lyon", + "ShipRegion": null, + "ShipPostalCode": "69004", + "ShipCountry": "France" + }, { + "OrderID": 10817, + "CustomerID": "KOENE", + "EmployeeID": 3, + "OrderDate": "1998-01-06T00:00:00", + "RequiredDate": "1998-01-20T00:00:00", + "ShippedDate": "1998-01-13T00:00:00", + "ShipVia": 2, + "Freight": "306.0700", + "ShipName": "K\u00f6niglich Essen", + "ShipAddress": "Maubelstr. 90", + "ShipCity": "Brandenburg", + "ShipRegion": null, + "ShipPostalCode": "14776", + "ShipCountry": "Germany" + }, { + "OrderID": 10820, + "CustomerID": "RATTC", + "EmployeeID": 3, + "OrderDate": "1998-01-07T00:00:00", + "RequiredDate": "1998-02-04T00:00:00", + "ShippedDate": "1998-01-13T00:00:00", + "ShipVia": 2, + "Freight": "37.5200", + "ShipName": "Rattlesnake Canyon Grocery", + "ShipAddress": "2817 Milton Dr.", + "ShipCity": "Albuquerque", + "ShipRegion": "NM", + "ShipPostalCode": "87110", + "ShipCountry": "USA" + }, { + "OrderID": 10831, + "CustomerID": "SANTG", + "EmployeeID": 3, + "OrderDate": "1998-01-14T00:00:00", + "RequiredDate": "1998-02-11T00:00:00", + "ShippedDate": "1998-01-23T00:00:00", + "ShipVia": 2, + "Freight": "72.1900", + "ShipName": "Sant\u00e9 Gourmet", + "ShipAddress": "Erling Skakkes gate 78", + "ShipCity": "Stavern", + "ShipRegion": null, + "ShipPostalCode": "4110", + "ShipCountry": "Norway" + }, { + "OrderID": 10838, + "CustomerID": "LINOD", + "EmployeeID": 3, + "OrderDate": "1998-01-19T00:00:00", + "RequiredDate": "1998-02-16T00:00:00", + "ShippedDate": "1998-01-23T00:00:00", + "ShipVia": 3, + "Freight": "59.2800", + "ShipName": "LINO-Delicateses", + "ShipAddress": "Ave. 5 de Mayo Porlamar", + "ShipCity": "I. de Margarita", + "ShipRegion": "Nueva Esparta", + "ShipPostalCode": "4980", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10839, + "CustomerID": "TRADH", + "EmployeeID": 3, + "OrderDate": "1998-01-19T00:00:00", + "RequiredDate": "1998-02-16T00:00:00", + "ShippedDate": "1998-01-22T00:00:00", + "ShipVia": 3, + "Freight": "35.4300", + "ShipName": "Tradi\u00e7ao Hipermercados", + "ShipAddress": "Av. In\u00eas de Castro, 414", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05634-030", + "ShipCountry": "Brazil" + }, { + "OrderID": 10854, + "CustomerID": "ERNSH", + "EmployeeID": 3, + "OrderDate": "1998-01-27T00:00:00", + "RequiredDate": "1998-02-24T00:00:00", + "ShippedDate": "1998-02-05T00:00:00", + "ShipVia": 2, + "Freight": "100.2200", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10855, + "CustomerID": "OLDWO", + "EmployeeID": 3, + "OrderDate": "1998-01-27T00:00:00", + "RequiredDate": "1998-02-24T00:00:00", + "ShippedDate": "1998-02-04T00:00:00", + "ShipVia": 1, + "Freight": "170.9700", + "ShipName": "Old World Delicatessen", + "ShipAddress": "2743 Bering St.", + "ShipCity": "Anchorage", + "ShipRegion": "AK", + "ShipPostalCode": "99508", + "ShipCountry": "USA" + }, { + "OrderID": 10856, + "CustomerID": "ANTON", + "EmployeeID": 3, + "OrderDate": "1998-01-28T00:00:00", + "RequiredDate": "1998-02-25T00:00:00", + "ShippedDate": "1998-02-10T00:00:00", + "ShipVia": 2, + "Freight": "58.4300", + "ShipName": "Antonio Moreno Taquer\u00eda", + "ShipAddress": "Mataderos 2312", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05023", + "ShipCountry": "Mexico" + }, { + "OrderID": 10860, + "CustomerID": "FRANR", + "EmployeeID": 3, + "OrderDate": "1998-01-29T00:00:00", + "RequiredDate": "1998-02-26T00:00:00", + "ShippedDate": "1998-02-04T00:00:00", + "ShipVia": 3, + "Freight": "19.2600", + "ShipName": "France restauration", + "ShipAddress": "54, rue Royale", + "ShipCity": "Nantes", + "ShipRegion": null, + "ShipPostalCode": "44000", + "ShipCountry": "France" + }, { + "OrderID": 10879, + "CustomerID": "WILMK", + "EmployeeID": 3, + "OrderDate": "1998-02-10T00:00:00", + "RequiredDate": "1998-03-10T00:00:00", + "ShippedDate": "1998-02-12T00:00:00", + "ShipVia": 3, + "Freight": "8.5000", + "ShipName": "Wilman Kala", + "ShipAddress": "Keskuskatu 45", + "ShipCity": "Helsinki", + "ShipRegion": null, + "ShipPostalCode": "21240", + "ShipCountry": "Finland" + }, { + "OrderID": 10895, + "CustomerID": "ERNSH", + "EmployeeID": 3, + "OrderDate": "1998-02-18T00:00:00", + "RequiredDate": "1998-03-18T00:00:00", + "ShippedDate": "1998-02-23T00:00:00", + "ShipVia": 1, + "Freight": "162.7500", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10897, + "CustomerID": "HUNGO", + "EmployeeID": 3, + "OrderDate": "1998-02-19T00:00:00", + "RequiredDate": "1998-03-19T00:00:00", + "ShippedDate": "1998-02-25T00:00:00", + "ShipVia": 2, + "Freight": "603.5400", + "ShipName": "Hungry Owl All-Night Grocers", + "ShipAddress": "8 Johnstown Road", + "ShipCity": "Cork", + "ShipRegion": "Co. Cork", + "ShipPostalCode": null, + "ShipCountry": "Ireland" + }, { + "OrderID": 10903, + "CustomerID": "HANAR", + "EmployeeID": 3, + "OrderDate": "1998-02-24T00:00:00", + "RequiredDate": "1998-03-24T00:00:00", + "ShippedDate": "1998-03-04T00:00:00", + "ShipVia": 3, + "Freight": "36.7100", + "ShipName": "Hanari Carnes", + "ShipAddress": "Rua do Pa\u00e7o, 67", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "05454-876", + "ShipCountry": "Brazil" + }, { + "OrderID": 10904, + "CustomerID": "WHITC", + "EmployeeID": 3, + "OrderDate": "1998-02-24T00:00:00", + "RequiredDate": "1998-03-24T00:00:00", + "ShippedDate": "1998-02-27T00:00:00", + "ShipVia": 3, + "Freight": "162.9500", + "ShipName": "White Clover Markets", + "ShipAddress": "1029 - 12th Ave. S.", + "ShipCity": "Seattle", + "ShipRegion": "WA", + "ShipPostalCode": "98124", + "ShipCountry": "USA" + }, { + "OrderID": 10911, + "CustomerID": "GODOS", + "EmployeeID": 3, + "OrderDate": "1998-02-26T00:00:00", + "RequiredDate": "1998-03-26T00:00:00", + "ShippedDate": "1998-03-05T00:00:00", + "ShipVia": 1, + "Freight": "38.1900", + "ShipName": "Godos Cocina T\u00edpica", + "ShipAddress": "C/ Romero, 33", + "ShipCity": "Sevilla", + "ShipRegion": null, + "ShipPostalCode": "41101", + "ShipCountry": "Spain" + }, { + "OrderID": 10918, + "CustomerID": "BOTTM", + "EmployeeID": 3, + "OrderDate": "1998-03-02T00:00:00", + "RequiredDate": "1998-03-30T00:00:00", + "ShippedDate": "1998-03-11T00:00:00", + "ShipVia": 3, + "Freight": "48.8300", + "ShipName": "Bottom-Dollar Markets", + "ShipAddress": "23 Tsawassen Blvd.", + "ShipCity": "Tsawassen", + "ShipRegion": "BC", + "ShipPostalCode": "T2F 8M4", + "ShipCountry": "Canada" + }, { + "OrderID": 10924, + "CustomerID": "BERGS", + "EmployeeID": 3, + "OrderDate": "1998-03-04T00:00:00", + "RequiredDate": "1998-04-01T00:00:00", + "ShippedDate": "1998-04-08T00:00:00", + "ShipVia": 2, + "Freight": "151.5200", + "ShipName": "Berglunds snabbk\u00f6p", + "ShipAddress": "Berguvsv\u00e4gen 8", + "ShipCity": "Lule\u00e5", + "ShipRegion": null, + "ShipPostalCode": "S-958 22", + "ShipCountry": "Sweden" + }, { + "OrderID": 10925, + "CustomerID": "HANAR", + "EmployeeID": 3, + "OrderDate": "1998-03-04T00:00:00", + "RequiredDate": "1998-04-01T00:00:00", + "ShippedDate": "1998-03-13T00:00:00", + "ShipVia": 1, + "Freight": "2.2700", + "ShipName": "Hanari Carnes", + "ShipAddress": "Rua do Pa\u00e7o, 67", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "05454-876", + "ShipCountry": "Brazil" + }, { + "OrderID": 10934, + "CustomerID": "LEHMS", + "EmployeeID": 3, + "OrderDate": "1998-03-09T00:00:00", + "RequiredDate": "1998-04-06T00:00:00", + "ShippedDate": "1998-03-12T00:00:00", + "ShipVia": 3, + "Freight": "32.0100", + "ShipName": "Lehmanns Marktstand", + "ShipAddress": "Magazinweg 7", + "ShipCity": "Frankfurt a.M.", + "ShipRegion": null, + "ShipPostalCode": "60528", + "ShipCountry": "Germany" + }, { + "OrderID": 10936, + "CustomerID": "GREAL", + "EmployeeID": 3, + "OrderDate": "1998-03-09T00:00:00", + "RequiredDate": "1998-04-06T00:00:00", + "ShippedDate": "1998-03-18T00:00:00", + "ShipVia": 2, + "Freight": "33.6800", + "ShipName": "Great Lakes Food Market", + "ShipAddress": "2732 Baker Blvd.", + "ShipCity": "Eugene", + "ShipRegion": "OR", + "ShipPostalCode": "97403", + "ShipCountry": "USA" + }, { + "OrderID": 10938, + "CustomerID": "QUICK", + "EmployeeID": 3, + "OrderDate": "1998-03-10T00:00:00", + "RequiredDate": "1998-04-07T00:00:00", + "ShippedDate": "1998-03-16T00:00:00", + "ShipVia": 2, + "Freight": "31.8900", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10947, + "CustomerID": "BSBEV", + "EmployeeID": 3, + "OrderDate": "1998-03-13T00:00:00", + "RequiredDate": "1998-04-10T00:00:00", + "ShippedDate": "1998-03-16T00:00:00", + "ShipVia": 2, + "Freight": "3.2600", + "ShipName": "B's Beverages", + "ShipAddress": "Fauntleroy Circus", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "EC2 5NT", + "ShipCountry": "UK" + }, { + "OrderID": 10948, + "CustomerID": "GODOS", + "EmployeeID": 3, + "OrderDate": "1998-03-13T00:00:00", + "RequiredDate": "1998-04-10T00:00:00", + "ShippedDate": "1998-03-19T00:00:00", + "ShipVia": 3, + "Freight": "23.3900", + "ShipName": "Godos Cocina T\u00edpica", + "ShipAddress": "C/ Romero, 33", + "ShipCity": "Sevilla", + "ShipRegion": null, + "ShipPostalCode": "41101", + "ShipCountry": "Spain" + }, { + "OrderID": 10960, + "CustomerID": "HILAA", + "EmployeeID": 3, + "OrderDate": "1998-03-19T00:00:00", + "RequiredDate": "1998-04-02T00:00:00", + "ShippedDate": "1998-04-08T00:00:00", + "ShipVia": 1, + "Freight": "2.0800", + "ShipName": "HILARION-Abastos", + "ShipAddress": "Carrera 22 con Ave. Carlos Soublette #8-35", + "ShipCity": "San Crist\u00f3bal", + "ShipRegion": "T\u00e1chira", + "ShipPostalCode": "5022", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10964, + "CustomerID": "SPECD", + "EmployeeID": 3, + "OrderDate": "1998-03-20T00:00:00", + "RequiredDate": "1998-04-17T00:00:00", + "ShippedDate": "1998-03-24T00:00:00", + "ShipVia": 2, + "Freight": "87.3800", + "ShipName": "Sp\u00e9cialit\u00e9s du monde", + "ShipAddress": "25, rue Lauriston", + "ShipCity": "Paris", + "ShipRegion": null, + "ShipPostalCode": "75016", + "ShipCountry": "France" + }, { + "OrderID": 10974, + "CustomerID": "SPLIR", + "EmployeeID": 3, + "OrderDate": "1998-03-25T00:00:00", + "RequiredDate": "1998-04-08T00:00:00", + "ShippedDate": "1998-04-03T00:00:00", + "ShipVia": 3, + "Freight": "12.9600", + "ShipName": "Split Rail Beer & Ale", + "ShipAddress": "P.O. Box 555", + "ShipCity": "Lander", + "ShipRegion": "WY", + "ShipPostalCode": "82520", + "ShipCountry": "USA" + }, { + "OrderID": 10988, + "CustomerID": "RATTC", + "EmployeeID": 3, + "OrderDate": "1998-03-31T00:00:00", + "RequiredDate": "1998-04-28T00:00:00", + "ShippedDate": "1998-04-10T00:00:00", + "ShipVia": 2, + "Freight": "61.1400", + "ShipName": "Rattlesnake Canyon Grocery", + "ShipAddress": "2817 Milton Dr.", + "ShipCity": "Albuquerque", + "ShipRegion": "NM", + "ShipPostalCode": "87110", + "ShipCountry": "USA" + }, { + "OrderID": 11003, + "CustomerID": "THECR", + "EmployeeID": 3, + "OrderDate": "1998-04-06T00:00:00", + "RequiredDate": "1998-05-04T00:00:00", + "ShippedDate": "1998-04-08T00:00:00", + "ShipVia": 3, + "Freight": "14.9100", + "ShipName": "The Cracker Box", + "ShipAddress": "55 Grizzly Peak Rd.", + "ShipCity": "Butte", + "ShipRegion": "MT", + "ShipPostalCode": "59801", + "ShipCountry": "USA" + }, { + "OrderID": 11004, + "CustomerID": "MAISD", + "EmployeeID": 3, + "OrderDate": "1998-04-07T00:00:00", + "RequiredDate": "1998-05-05T00:00:00", + "ShippedDate": "1998-04-20T00:00:00", + "ShipVia": 1, + "Freight": "44.8400", + "ShipName": "Maison Dewey", + "ShipAddress": "Rue Joseph-Bens 532", + "ShipCity": "Bruxelles", + "ShipRegion": null, + "ShipPostalCode": "B-1180", + "ShipCountry": "Belgium" + }, { + "OrderID": 11006, + "CustomerID": "GREAL", + "EmployeeID": 3, + "OrderDate": "1998-04-07T00:00:00", + "RequiredDate": "1998-05-05T00:00:00", + "ShippedDate": "1998-04-15T00:00:00", + "ShipVia": 2, + "Freight": "25.1900", + "ShipName": "Great Lakes Food Market", + "ShipAddress": "2732 Baker Blvd.", + "ShipCity": "Eugene", + "ShipRegion": "OR", + "ShipPostalCode": "97403", + "ShipCountry": "USA" + }, { + "OrderID": 11011, + "CustomerID": "ALFKI", + "EmployeeID": 3, + "OrderDate": "1998-04-09T00:00:00", + "RequiredDate": "1998-05-07T00:00:00", + "ShippedDate": "1998-04-13T00:00:00", + "ShipVia": 1, + "Freight": "1.2100", + "ShipName": "Alfred's Futterkiste", + "ShipAddress": "Obere Str. 57", + "ShipCity": "Berlin", + "ShipRegion": null, + "ShipPostalCode": "12209", + "ShipCountry": "Germany" + }, { + "OrderID": 11021, + "CustomerID": "QUICK", + "EmployeeID": 3, + "OrderDate": "1998-04-14T00:00:00", + "RequiredDate": "1998-05-12T00:00:00", + "ShippedDate": "1998-04-21T00:00:00", + "ShipVia": 1, + "Freight": "297.1800", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 11041, + "CustomerID": "CHOPS", + "EmployeeID": 3, + "OrderDate": "1998-04-22T00:00:00", + "RequiredDate": "1998-05-20T00:00:00", + "ShippedDate": "1998-04-28T00:00:00", + "ShipVia": 2, + "Freight": "48.2200", + "ShipName": "Chop-suey Chinese", + "ShipAddress": "Hauptstr. 31", + "ShipCity": "Bern", + "ShipRegion": null, + "ShipPostalCode": "3012", + "ShipCountry": "Switzerland" + }, { + "OrderID": 11049, + "CustomerID": "GOURL", + "EmployeeID": 3, + "OrderDate": "1998-04-24T00:00:00", + "RequiredDate": "1998-05-22T00:00:00", + "ShippedDate": "1998-05-04T00:00:00", + "ShipVia": 1, + "Freight": "8.3400", + "ShipName": "Gourmet Lanchonetes", + "ShipAddress": "Av. Brasil, 442", + "ShipCity": "Campinas", + "ShipRegion": "SP", + "ShipPostalCode": "04876-786", + "ShipCountry": "Brazil" + }, { + "OrderID": 11052, + "CustomerID": "HANAR", + "EmployeeID": 3, + "OrderDate": "1998-04-27T00:00:00", + "RequiredDate": "1998-05-25T00:00:00", + "ShippedDate": "1998-05-01T00:00:00", + "ShipVia": 1, + "Freight": "67.2600", + "ShipName": "Hanari Carnes", + "ShipAddress": "Rua do Pa\u00e7o, 67", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "05454-876", + "ShipCountry": "Brazil" + }, { + "OrderID": 11057, + "CustomerID": "NORTS", + "EmployeeID": 3, + "OrderDate": "1998-04-29T00:00:00", + "RequiredDate": "1998-05-27T00:00:00", + "ShippedDate": "1998-05-01T00:00:00", + "ShipVia": 3, + "Freight": "4.1300", + "ShipName": "North/South", + "ShipAddress": "South House 300 Queensbridge", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "SW7 1RZ", + "ShipCountry": "UK" + }, { + "OrderID": 11063, + "CustomerID": "HUNGO", + "EmployeeID": 3, + "OrderDate": "1998-04-30T00:00:00", + "RequiredDate": "1998-05-28T00:00:00", + "ShippedDate": "1998-05-06T00:00:00", + "ShipVia": 2, + "Freight": "81.7300", + "ShipName": "Hungry Owl All-Night Grocers", + "ShipAddress": "8 Johnstown Road", + "ShipCity": "Cork", + "ShipRegion": "Co. Cork", + "ShipPostalCode": null, + "ShipCountry": "Ireland" + }], + "EmployeeID": 3, + "LastName": "Leverling", + "FirstName": "Janet", + "Title": "Sales Representative", + "TitleOfCourtesy": "Ms.", + "BirthDate": "1963-08-30T00:00:00", + "HireDate": "1992-04-01T00:00:00", + "Address": "722 Moss Bay Blvd.", + "City": "Kirkland", + "Region": "WA", + "PostalCode": "98033", + "Country": "USA", + "HomePhone": "(206) 555-3412", + "Extension": "3355", + "Notes": "Janet has a BS degree in chemistry from Boston College (1984). She has also completed a certificate program in food retailing management. Janet was hired as a sales associate in 1991 and promoted to sales representative in February 1992.", + "ReportsTo": 2, + "PhotoPath": "http://accweb/emmployees/leverling.bmp" + }, { + "Orders": [{ + "OrderID": 10250, + "CustomerID": "HANAR", + "EmployeeID": 4, + "OrderDate": "1996-07-08T00:00:00", + "RequiredDate": "1996-08-05T00:00:00", + "ShippedDate": "1996-07-12T00:00:00", + "ShipVia": 2, + "Freight": "65.8300", + "ShipName": "Hanari Carnes", + "ShipAddress": "Rua do Pa\u00e7o, 67", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "05454-876", + "ShipCountry": "Brazil" + }, { + "OrderID": 10252, + "CustomerID": "SUPRD", + "EmployeeID": 4, + "OrderDate": "1996-07-09T00:00:00", + "RequiredDate": "1996-08-06T00:00:00", + "ShippedDate": "1996-07-11T00:00:00", + "ShipVia": 2, + "Freight": "51.3000", + "ShipName": "Supr\u00eames d\u00e9lices", + "ShipAddress": "Boulevard Tirou, 255", + "ShipCity": "Charleroi", + "ShipRegion": null, + "ShipPostalCode": "B-6000", + "ShipCountry": "Belgium" + }, { + "OrderID": 10257, + "CustomerID": "HILAA", + "EmployeeID": 4, + "OrderDate": "1996-07-16T00:00:00", + "RequiredDate": "1996-08-13T00:00:00", + "ShippedDate": "1996-07-22T00:00:00", + "ShipVia": 3, + "Freight": "81.9100", + "ShipName": "HILARION-Abastos", + "ShipAddress": "Carrera 22 con Ave. Carlos Soublette #8-35", + "ShipCity": "San Crist\u00f3bal", + "ShipRegion": "T\u00e1chira", + "ShipPostalCode": "5022", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10259, + "CustomerID": "CENTC", + "EmployeeID": 4, + "OrderDate": "1996-07-18T00:00:00", + "RequiredDate": "1996-08-15T00:00:00", + "ShippedDate": "1996-07-25T00:00:00", + "ShipVia": 3, + "Freight": "3.2500", + "ShipName": "Centro comercial Moctezuma", + "ShipAddress": "Sierras de Granada 9993", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05022", + "ShipCountry": "Mexico" + }, { + "OrderID": 10260, + "CustomerID": "OTTIK", + "EmployeeID": 4, + "OrderDate": "1996-07-19T00:00:00", + "RequiredDate": "1996-08-16T00:00:00", + "ShippedDate": "1996-07-29T00:00:00", + "ShipVia": 1, + "Freight": "55.0900", + "ShipName": "Ottilies K\u00e4seladen", + "ShipAddress": "Mehrheimerstr. 369", + "ShipCity": "K\u00f6ln", + "ShipRegion": null, + "ShipPostalCode": "50739", + "ShipCountry": "Germany" + }, { + "OrderID": 10261, + "CustomerID": "QUEDE", + "EmployeeID": 4, + "OrderDate": "1996-07-19T00:00:00", + "RequiredDate": "1996-08-16T00:00:00", + "ShippedDate": "1996-07-30T00:00:00", + "ShipVia": 2, + "Freight": "3.0500", + "ShipName": "Que Del\u00edcia", + "ShipAddress": "Rua da Panificadora, 12", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "02389-673", + "ShipCountry": "Brazil" + }, { + "OrderID": 10267, + "CustomerID": "FRANK", + "EmployeeID": 4, + "OrderDate": "1996-07-29T00:00:00", + "RequiredDate": "1996-08-26T00:00:00", + "ShippedDate": "1996-08-06T00:00:00", + "ShipVia": 1, + "Freight": "208.5800", + "ShipName": "Frankenversand", + "ShipAddress": "Berliner Platz 43", + "ShipCity": "M\u00fcnchen", + "ShipRegion": null, + "ShipPostalCode": "80805", + "ShipCountry": "Germany" + }, { + "OrderID": 10281, + "CustomerID": "ROMEY", + "EmployeeID": 4, + "OrderDate": "1996-08-14T00:00:00", + "RequiredDate": "1996-08-28T00:00:00", + "ShippedDate": "1996-08-21T00:00:00", + "ShipVia": 1, + "Freight": "2.9400", + "ShipName": "Romero y tomillo", + "ShipAddress": "Gran V\u00eda, 1", + "ShipCity": "Madrid", + "ShipRegion": null, + "ShipPostalCode": "28001", + "ShipCountry": "Spain" + }, { + "OrderID": 10282, + "CustomerID": "ROMEY", + "EmployeeID": 4, + "OrderDate": "1996-08-15T00:00:00", + "RequiredDate": "1996-09-12T00:00:00", + "ShippedDate": "1996-08-21T00:00:00", + "ShipVia": 1, + "Freight": "12.6900", + "ShipName": "Romero y tomillo", + "ShipAddress": "Gran V\u00eda, 1", + "ShipCity": "Madrid", + "ShipRegion": null, + "ShipPostalCode": "28001", + "ShipCountry": "Spain" + }, { + "OrderID": 10284, + "CustomerID": "LEHMS", + "EmployeeID": 4, + "OrderDate": "1996-08-19T00:00:00", + "RequiredDate": "1996-09-16T00:00:00", + "ShippedDate": "1996-08-27T00:00:00", + "ShipVia": 1, + "Freight": "76.5600", + "ShipName": "Lehmanns Marktstand", + "ShipAddress": "Magazinweg 7", + "ShipCity": "Frankfurt a.M.", + "ShipRegion": null, + "ShipPostalCode": "60528", + "ShipCountry": "Germany" + }, { + "OrderID": 10288, + "CustomerID": "REGGC", + "EmployeeID": 4, + "OrderDate": "1996-08-23T00:00:00", + "RequiredDate": "1996-09-20T00:00:00", + "ShippedDate": "1996-09-03T00:00:00", + "ShipVia": 1, + "Freight": "7.4500", + "ShipName": "Reggiani Caseifici", + "ShipAddress": "Strada Provinciale 124", + "ShipCity": "Reggio Emilia", + "ShipRegion": null, + "ShipPostalCode": "42100", + "ShipCountry": "Italy" + }, { + "OrderID": 10294, + "CustomerID": "RATTC", + "EmployeeID": 4, + "OrderDate": "1996-08-30T00:00:00", + "RequiredDate": "1996-09-27T00:00:00", + "ShippedDate": "1996-09-05T00:00:00", + "ShipVia": 2, + "Freight": "147.2600", + "ShipName": "Rattlesnake Canyon Grocery", + "ShipAddress": "2817 Milton Dr.", + "ShipCity": "Albuquerque", + "ShipRegion": "NM", + "ShipPostalCode": "87110", + "ShipCountry": "USA" + }, { + "OrderID": 10299, + "CustomerID": "RICAR", + "EmployeeID": 4, + "OrderDate": "1996-09-06T00:00:00", + "RequiredDate": "1996-10-04T00:00:00", + "ShippedDate": "1996-09-13T00:00:00", + "ShipVia": 2, + "Freight": "29.7600", + "ShipName": "Ricardo Adocicados", + "ShipAddress": "Av. Copacabana, 267", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "02389-890", + "ShipCountry": "Brazil" + }, { + "OrderID": 10302, + "CustomerID": "SUPRD", + "EmployeeID": 4, + "OrderDate": "1996-09-10T00:00:00", + "RequiredDate": "1996-10-08T00:00:00", + "ShippedDate": "1996-10-09T00:00:00", + "ShipVia": 2, + "Freight": "6.2700", + "ShipName": "Supr\u00eames d\u00e9lices", + "ShipAddress": "Boulevard Tirou, 255", + "ShipCity": "Charleroi", + "ShipRegion": null, + "ShipPostalCode": "B-6000", + "ShipCountry": "Belgium" + }, { + "OrderID": 10315, + "CustomerID": "ISLAT", + "EmployeeID": 4, + "OrderDate": "1996-09-26T00:00:00", + "RequiredDate": "1996-10-24T00:00:00", + "ShippedDate": "1996-10-03T00:00:00", + "ShipVia": 2, + "Freight": "41.7600", + "ShipName": "Island Trading", + "ShipAddress": "Garden House Crowther Way", + "ShipCity": "Cowes", + "ShipRegion": "Isle of Wight", + "ShipPostalCode": "PO31 7PJ", + "ShipCountry": "UK" + }, { + "OrderID": 10323, + "CustomerID": "KOENE", + "EmployeeID": 4, + "OrderDate": "1996-10-07T00:00:00", + "RequiredDate": "1996-11-04T00:00:00", + "ShippedDate": "1996-10-14T00:00:00", + "ShipVia": 1, + "Freight": "4.8800", + "ShipName": "K\u00f6niglich Essen", + "ShipAddress": "Maubelstr. 90", + "ShipCity": "Brandenburg", + "ShipRegion": null, + "ShipPostalCode": "14776", + "ShipCountry": "Germany" + }, { + "OrderID": 10326, + "CustomerID": "BOLID", + "EmployeeID": 4, + "OrderDate": "1996-10-10T00:00:00", + "RequiredDate": "1996-11-07T00:00:00", + "ShippedDate": "1996-10-14T00:00:00", + "ShipVia": 2, + "Freight": "77.9200", + "ShipName": "B\u00f3lido Comidas preparadas", + "ShipAddress": "C/ Araquil, 67", + "ShipCity": "Madrid", + "ShipRegion": null, + "ShipPostalCode": "28023", + "ShipCountry": "Spain" + }, { + "OrderID": 10328, + "CustomerID": "FURIB", + "EmployeeID": 4, + "OrderDate": "1996-10-14T00:00:00", + "RequiredDate": "1996-11-11T00:00:00", + "ShippedDate": "1996-10-17T00:00:00", + "ShipVia": 3, + "Freight": "87.0300", + "ShipName": "Furia Bacalhau e Frutos do Mar", + "ShipAddress": "Jardim das rosas n. 32", + "ShipCity": "Lisboa", + "ShipRegion": null, + "ShipPostalCode": "1675", + "ShipCountry": "Portugal" + }, { + "OrderID": 10329, + "CustomerID": "SPLIR", + "EmployeeID": 4, + "OrderDate": "1996-10-15T00:00:00", + "RequiredDate": "1996-11-26T00:00:00", + "ShippedDate": "1996-10-23T00:00:00", + "ShipVia": 2, + "Freight": "191.6700", + "ShipName": "Split Rail Beer & Ale", + "ShipAddress": "P.O. Box 555", + "ShipCity": "Lander", + "ShipRegion": "WY", + "ShipPostalCode": "82520", + "ShipCountry": "USA" + }, { + "OrderID": 10337, + "CustomerID": "FRANK", + "EmployeeID": 4, + "OrderDate": "1996-10-24T00:00:00", + "RequiredDate": "1996-11-21T00:00:00", + "ShippedDate": "1996-10-29T00:00:00", + "ShipVia": 3, + "Freight": "108.2600", + "ShipName": "Frankenversand", + "ShipAddress": "Berliner Platz 43", + "ShipCity": "M\u00fcnchen", + "ShipRegion": null, + "ShipPostalCode": "80805", + "ShipCountry": "Germany" + }, { + "OrderID": 10338, + "CustomerID": "OLDWO", + "EmployeeID": 4, + "OrderDate": "1996-10-25T00:00:00", + "RequiredDate": "1996-11-22T00:00:00", + "ShippedDate": "1996-10-29T00:00:00", + "ShipVia": 3, + "Freight": "84.2100", + "ShipName": "Old World Delicatessen", + "ShipAddress": "2743 Bering St.", + "ShipCity": "Anchorage", + "ShipRegion": "AK", + "ShipPostalCode": "99508", + "ShipCountry": "USA" + }, { + "OrderID": 10342, + "CustomerID": "FRANK", + "EmployeeID": 4, + "OrderDate": "1996-10-30T00:00:00", + "RequiredDate": "1996-11-13T00:00:00", + "ShippedDate": "1996-11-04T00:00:00", + "ShipVia": 2, + "Freight": "54.8300", + "ShipName": "Frankenversand", + "ShipAddress": "Berliner Platz 43", + "ShipCity": "M\u00fcnchen", + "ShipRegion": null, + "ShipPostalCode": "80805", + "ShipCountry": "Germany" + }, { + "OrderID": 10343, + "CustomerID": "LEHMS", + "EmployeeID": 4, + "OrderDate": "1996-10-31T00:00:00", + "RequiredDate": "1996-11-28T00:00:00", + "ShippedDate": "1996-11-06T00:00:00", + "ShipVia": 1, + "Freight": "110.3700", + "ShipName": "Lehmanns Marktstand", + "ShipAddress": "Magazinweg 7", + "ShipCity": "Frankfurt a.M.", + "ShipRegion": null, + "ShipPostalCode": "60528", + "ShipCountry": "Germany" + }, { + "OrderID": 10344, + "CustomerID": "WHITC", + "EmployeeID": 4, + "OrderDate": "1996-11-01T00:00:00", + "RequiredDate": "1996-11-29T00:00:00", + "ShippedDate": "1996-11-05T00:00:00", + "ShipVia": 2, + "Freight": "23.2900", + "ShipName": "White Clover Markets", + "ShipAddress": "1029 - 12th Ave. S.", + "ShipCity": "Seattle", + "ShipRegion": "WA", + "ShipPostalCode": "98124", + "ShipCountry": "USA" + }, { + "OrderID": 10347, + "CustomerID": "FAMIA", + "EmployeeID": 4, + "OrderDate": "1996-11-06T00:00:00", + "RequiredDate": "1996-12-04T00:00:00", + "ShippedDate": "1996-11-08T00:00:00", + "ShipVia": 3, + "Freight": "3.1000", + "ShipName": "Familia Arquibaldo", + "ShipAddress": "Rua Or\u00f3s, 92", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05442-030", + "ShipCountry": "Brazil" + }, { + "OrderID": 10348, + "CustomerID": "WANDK", + "EmployeeID": 4, + "OrderDate": "1996-11-07T00:00:00", + "RequiredDate": "1996-12-05T00:00:00", + "ShippedDate": "1996-11-15T00:00:00", + "ShipVia": 2, + "Freight": "0.7800", + "ShipName": "Die Wandernde Kuh", + "ShipAddress": "Adenauerallee 900", + "ShipCity": "Stuttgart", + "ShipRegion": null, + "ShipPostalCode": "70563", + "ShipCountry": "Germany" + }, { + "OrderID": 10360, + "CustomerID": "BLONP", + "EmployeeID": 4, + "OrderDate": "1996-11-22T00:00:00", + "RequiredDate": "1996-12-20T00:00:00", + "ShippedDate": "1996-12-02T00:00:00", + "ShipVia": 3, + "Freight": "131.7000", + "ShipName": "Blondel p\u00e8re et fils", + "ShipAddress": "24, place Kl\u00e9ber", + "ShipCity": "Strasbourg", + "ShipRegion": null, + "ShipPostalCode": "67000", + "ShipCountry": "France" + }, { + "OrderID": 10363, + "CustomerID": "DRACD", + "EmployeeID": 4, + "OrderDate": "1996-11-26T00:00:00", + "RequiredDate": "1996-12-24T00:00:00", + "ShippedDate": "1996-12-04T00:00:00", + "ShipVia": 3, + "Freight": "30.5400", + "ShipName": "Drachenblut Delikatessen", + "ShipAddress": "Walserweg 21", + "ShipCity": "Aachen", + "ShipRegion": null, + "ShipPostalCode": "52066", + "ShipCountry": "Germany" + }, { + "OrderID": 10373, + "CustomerID": "HUNGO", + "EmployeeID": 4, + "OrderDate": "1996-12-05T00:00:00", + "RequiredDate": "1997-01-02T00:00:00", + "ShippedDate": "1996-12-11T00:00:00", + "ShipVia": 3, + "Freight": "124.1200", + "ShipName": "Hungry Owl All-Night Grocers", + "ShipAddress": "8 Johnstown Road", + "ShipCity": "Cork", + "ShipRegion": "Co. Cork", + "ShipPostalCode": null, + "ShipCountry": "Ireland" + }, { + "OrderID": 10382, + "CustomerID": "ERNSH", + "EmployeeID": 4, + "OrderDate": "1996-12-13T00:00:00", + "RequiredDate": "1997-01-10T00:00:00", + "ShippedDate": "1996-12-16T00:00:00", + "ShipVia": 1, + "Freight": "94.7700", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10389, + "CustomerID": "BOTTM", + "EmployeeID": 4, + "OrderDate": "1996-12-20T00:00:00", + "RequiredDate": "1997-01-17T00:00:00", + "ShippedDate": "1996-12-24T00:00:00", + "ShipVia": 2, + "Freight": "47.4200", + "ShipName": "Bottom-Dollar Markets", + "ShipAddress": "23 Tsawassen Blvd.", + "ShipCity": "Tsawassen", + "ShipRegion": "BC", + "ShipPostalCode": "T2F 8M4", + "ShipCountry": "Canada" + }, { + "OrderID": 10403, + "CustomerID": "ERNSH", + "EmployeeID": 4, + "OrderDate": "1997-01-03T00:00:00", + "RequiredDate": "1997-01-31T00:00:00", + "ShippedDate": "1997-01-09T00:00:00", + "ShipVia": 3, + "Freight": "73.7900", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10417, + "CustomerID": "SIMOB", + "EmployeeID": 4, + "OrderDate": "1997-01-16T00:00:00", + "RequiredDate": "1997-02-13T00:00:00", + "ShippedDate": "1997-01-28T00:00:00", + "ShipVia": 3, + "Freight": "70.2900", + "ShipName": "Simons bistro", + "ShipAddress": "Vinb\u00e6ltet 34", + "ShipCity": "Kobenhavn", + "ShipRegion": null, + "ShipPostalCode": "1734", + "ShipCountry": "Denmark" + }, { + "OrderID": 10418, + "CustomerID": "QUICK", + "EmployeeID": 4, + "OrderDate": "1997-01-17T00:00:00", + "RequiredDate": "1997-02-14T00:00:00", + "ShippedDate": "1997-01-24T00:00:00", + "ShipVia": 1, + "Freight": "17.5500", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10419, + "CustomerID": "RICSU", + "EmployeeID": 4, + "OrderDate": "1997-01-20T00:00:00", + "RequiredDate": "1997-02-17T00:00:00", + "ShippedDate": "1997-01-30T00:00:00", + "ShipVia": 2, + "Freight": "137.3500", + "ShipName": "Richter Supermarkt", + "ShipAddress": "Starenweg 5", + "ShipCity": "Gen\u00e8ve", + "ShipRegion": null, + "ShipPostalCode": "1204", + "ShipCountry": "Switzerland" + }, { + "OrderID": 10426, + "CustomerID": "GALED", + "EmployeeID": 4, + "OrderDate": "1997-01-27T00:00:00", + "RequiredDate": "1997-02-24T00:00:00", + "ShippedDate": "1997-02-06T00:00:00", + "ShipVia": 1, + "Freight": "18.6900", + "ShipName": "Galer\u00eda del gastron\u00f3mo", + "ShipAddress": "Rambla de Catalu\u00f1a, 23", + "ShipCity": "Barcelona", + "ShipRegion": null, + "ShipPostalCode": "8022", + "ShipCountry": "Spain" + }, { + "OrderID": 10427, + "CustomerID": "PICCO", + "EmployeeID": 4, + "OrderDate": "1997-01-27T00:00:00", + "RequiredDate": "1997-02-24T00:00:00", + "ShippedDate": "1997-03-03T00:00:00", + "ShipVia": 2, + "Freight": "31.2900", + "ShipName": "Piccolo und mehr", + "ShipAddress": "Geislweg 14", + "ShipCity": "Salzburg", + "ShipRegion": null, + "ShipPostalCode": "5020", + "ShipCountry": "Austria" + }, { + "OrderID": 10430, + "CustomerID": "ERNSH", + "EmployeeID": 4, + "OrderDate": "1997-01-30T00:00:00", + "RequiredDate": "1997-02-13T00:00:00", + "ShippedDate": "1997-02-03T00:00:00", + "ShipVia": 1, + "Freight": "458.7800", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10431, + "CustomerID": "BOTTM", + "EmployeeID": 4, + "OrderDate": "1997-01-30T00:00:00", + "RequiredDate": "1997-02-13T00:00:00", + "ShippedDate": "1997-02-07T00:00:00", + "ShipVia": 2, + "Freight": "44.1700", + "ShipName": "Bottom-Dollar Markets", + "ShipAddress": "23 Tsawassen Blvd.", + "ShipCity": "Tsawassen", + "ShipRegion": "BC", + "ShipPostalCode": "T2F 8M4", + "ShipCountry": "Canada" + }, { + "OrderID": 10440, + "CustomerID": "SAVEA", + "EmployeeID": 4, + "OrderDate": "1997-02-10T00:00:00", + "RequiredDate": "1997-03-10T00:00:00", + "ShippedDate": "1997-02-28T00:00:00", + "ShipVia": 2, + "Freight": "86.5300", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10447, + "CustomerID": "RICAR", + "EmployeeID": 4, + "OrderDate": "1997-02-14T00:00:00", + "RequiredDate": "1997-03-14T00:00:00", + "ShippedDate": "1997-03-07T00:00:00", + "ShipVia": 2, + "Freight": "68.6600", + "ShipName": "Ricardo Adocicados", + "ShipAddress": "Av. Copacabana, 267", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "02389-890", + "ShipCountry": "Brazil" + }, { + "OrderID": 10448, + "CustomerID": "RANCH", + "EmployeeID": 4, + "OrderDate": "1997-02-17T00:00:00", + "RequiredDate": "1997-03-17T00:00:00", + "ShippedDate": "1997-02-24T00:00:00", + "ShipVia": 2, + "Freight": "38.8200", + "ShipName": "Rancho grande", + "ShipAddress": "Av. del Libertador 900", + "ShipCity": "Buenos Aires", + "ShipRegion": null, + "ShipPostalCode": "1010", + "ShipCountry": "Argentina" + }, { + "OrderID": 10451, + "CustomerID": "QUICK", + "EmployeeID": 4, + "OrderDate": "1997-02-19T00:00:00", + "RequiredDate": "1997-03-05T00:00:00", + "ShippedDate": "1997-03-12T00:00:00", + "ShipVia": 3, + "Freight": "189.0900", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10454, + "CustomerID": "LAMAI", + "EmployeeID": 4, + "OrderDate": "1997-02-21T00:00:00", + "RequiredDate": "1997-03-21T00:00:00", + "ShippedDate": "1997-02-25T00:00:00", + "ShipVia": 3, + "Freight": "2.7400", + "ShipName": "La maison d'Asie", + "ShipAddress": "1 rue Alsace-Lorraine", + "ShipCity": "Toulouse", + "ShipRegion": null, + "ShipPostalCode": "31000", + "ShipCountry": "France" + }, { + "OrderID": 10459, + "CustomerID": "VICTE", + "EmployeeID": 4, + "OrderDate": "1997-02-27T00:00:00", + "RequiredDate": "1997-03-27T00:00:00", + "ShippedDate": "1997-02-28T00:00:00", + "ShipVia": 2, + "Freight": "25.0900", + "ShipName": "Victuailles en stock", + "ShipAddress": "2, rue du Commerce", + "ShipCity": "Lyon", + "ShipRegion": null, + "ShipPostalCode": "69004", + "ShipCountry": "France" + }, { + "OrderID": 10464, + "CustomerID": "FURIB", + "EmployeeID": 4, + "OrderDate": "1997-03-04T00:00:00", + "RequiredDate": "1997-04-01T00:00:00", + "ShippedDate": "1997-03-14T00:00:00", + "ShipVia": 2, + "Freight": "89.0000", + "ShipName": "Furia Bacalhau e Frutos do Mar", + "ShipAddress": "Jardim das rosas n. 32", + "ShipCity": "Lisboa", + "ShipRegion": null, + "ShipPostalCode": "1675", + "ShipCountry": "Portugal" + }, { + "OrderID": 10466, + "CustomerID": "COMMI", + "EmployeeID": 4, + "OrderDate": "1997-03-06T00:00:00", + "RequiredDate": "1997-04-03T00:00:00", + "ShippedDate": "1997-03-13T00:00:00", + "ShipVia": 1, + "Freight": "11.9300", + "ShipName": "Com\u00e9rcio Mineiro", + "ShipAddress": "Av. dos Lus\u00edadas, 23", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05432-043", + "ShipCountry": "Brazil" + }, { + "OrderID": 10470, + "CustomerID": "BONAP", + "EmployeeID": 4, + "OrderDate": "1997-03-11T00:00:00", + "RequiredDate": "1997-04-08T00:00:00", + "ShippedDate": "1997-03-14T00:00:00", + "ShipVia": 2, + "Freight": "64.5600", + "ShipName": "Bon app'", + "ShipAddress": "12, rue des Bouchers", + "ShipCity": "Marseille", + "ShipRegion": null, + "ShipPostalCode": "13008", + "ShipCountry": "France" + }, { + "OrderID": 10485, + "CustomerID": "LINOD", + "EmployeeID": 4, + "OrderDate": "1997-03-25T00:00:00", + "RequiredDate": "1997-04-08T00:00:00", + "ShippedDate": "1997-03-31T00:00:00", + "ShipVia": 2, + "Freight": "64.4500", + "ShipName": "LINO-Delicateses", + "ShipAddress": "Ave. 5 de Mayo Porlamar", + "ShipCity": "I. de Margarita", + "ShipRegion": "Nueva Esparta", + "ShipPostalCode": "4980", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10493, + "CustomerID": "LAMAI", + "EmployeeID": 4, + "OrderDate": "1997-04-02T00:00:00", + "RequiredDate": "1997-04-30T00:00:00", + "ShippedDate": "1997-04-10T00:00:00", + "ShipVia": 3, + "Freight": "10.6400", + "ShipName": "La maison d'Asie", + "ShipAddress": "1 rue Alsace-Lorraine", + "ShipCity": "Toulouse", + "ShipRegion": null, + "ShipPostalCode": "31000", + "ShipCountry": "France" + }, { + "OrderID": 10494, + "CustomerID": "COMMI", + "EmployeeID": 4, + "OrderDate": "1997-04-02T00:00:00", + "RequiredDate": "1997-04-30T00:00:00", + "ShippedDate": "1997-04-09T00:00:00", + "ShipVia": 2, + "Freight": "65.9900", + "ShipName": "Com\u00e9rcio Mineiro", + "ShipAddress": "Av. dos Lus\u00edadas, 23", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05432-043", + "ShipCountry": "Brazil" + }, { + "OrderID": 10499, + "CustomerID": "LILAS", + "EmployeeID": 4, + "OrderDate": "1997-04-08T00:00:00", + "RequiredDate": "1997-05-06T00:00:00", + "ShippedDate": "1997-04-16T00:00:00", + "ShipVia": 2, + "Freight": "102.0200", + "ShipName": "LILA-Supermercado", + "ShipAddress": "Carrera 52 con Ave. Bol\u00edvar #65-98 Llano Largo", + "ShipCity": "Barquisimeto", + "ShipRegion": "Lara", + "ShipPostalCode": "3508", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10504, + "CustomerID": "WHITC", + "EmployeeID": 4, + "OrderDate": "1997-04-11T00:00:00", + "RequiredDate": "1997-05-09T00:00:00", + "ShippedDate": "1997-04-18T00:00:00", + "ShipVia": 3, + "Freight": "59.1300", + "ShipName": "White Clover Markets", + "ShipAddress": "1029 - 12th Ave. S.", + "ShipCity": "Seattle", + "ShipRegion": "WA", + "ShipPostalCode": "98124", + "ShipCountry": "USA" + }, { + "OrderID": 10509, + "CustomerID": "BLAUS", + "EmployeeID": 4, + "OrderDate": "1997-04-17T00:00:00", + "RequiredDate": "1997-05-15T00:00:00", + "ShippedDate": "1997-04-29T00:00:00", + "ShipVia": 1, + "Freight": "0.1500", + "ShipName": "Blauer See Delikatessen", + "ShipAddress": "Forsterstr. 57", + "ShipCity": "Mannheim", + "ShipRegion": null, + "ShipPostalCode": "68306", + "ShipCountry": "Germany" + }, { + "OrderID": 10511, + "CustomerID": "BONAP", + "EmployeeID": 4, + "OrderDate": "1997-04-18T00:00:00", + "RequiredDate": "1997-05-16T00:00:00", + "ShippedDate": "1997-04-21T00:00:00", + "ShipVia": 3, + "Freight": "350.6400", + "ShipName": "Bon app'", + "ShipAddress": "12, rue des Bouchers", + "ShipCity": "Marseille", + "ShipRegion": null, + "ShipPostalCode": "13008", + "ShipCountry": "France" + }, { + "OrderID": 10518, + "CustomerID": "TORTU", + "EmployeeID": 4, + "OrderDate": "1997-04-25T00:00:00", + "RequiredDate": "1997-05-09T00:00:00", + "ShippedDate": "1997-05-05T00:00:00", + "ShipVia": 2, + "Freight": "218.1500", + "ShipName": "Tortuga Restaurante", + "ShipAddress": "Avda. Azteca 123", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05033", + "ShipCountry": "Mexico" + }, { + "OrderID": 10522, + "CustomerID": "LEHMS", + "EmployeeID": 4, + "OrderDate": "1997-04-30T00:00:00", + "RequiredDate": "1997-05-28T00:00:00", + "ShippedDate": "1997-05-06T00:00:00", + "ShipVia": 1, + "Freight": "45.3300", + "ShipName": "Lehmanns Marktstand", + "ShipAddress": "Magazinweg 7", + "ShipCity": "Frankfurt a.M.", + "ShipRegion": null, + "ShipPostalCode": "60528", + "ShipCountry": "Germany" + }, { + "OrderID": 10526, + "CustomerID": "WARTH", + "EmployeeID": 4, + "OrderDate": "1997-05-05T00:00:00", + "RequiredDate": "1997-06-02T00:00:00", + "ShippedDate": "1997-05-15T00:00:00", + "ShipVia": 2, + "Freight": "58.5900", + "ShipName": "Wartian Herkku", + "ShipAddress": "Torikatu 38", + "ShipCity": "Oulu", + "ShipRegion": null, + "ShipPostalCode": "90110", + "ShipCountry": "Finland" + }, { + "OrderID": 10535, + "CustomerID": "ANTON", + "EmployeeID": 4, + "OrderDate": "1997-05-13T00:00:00", + "RequiredDate": "1997-06-10T00:00:00", + "ShippedDate": "1997-05-21T00:00:00", + "ShipVia": 1, + "Freight": "15.6400", + "ShipName": "Antonio Moreno Taquer\u00eda", + "ShipAddress": "Mataderos 2312", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05023", + "ShipCountry": "Mexico" + }, { + "OrderID": 10544, + "CustomerID": "LONEP", + "EmployeeID": 4, + "OrderDate": "1997-05-21T00:00:00", + "RequiredDate": "1997-06-18T00:00:00", + "ShippedDate": "1997-05-30T00:00:00", + "ShipVia": 1, + "Freight": "24.9100", + "ShipName": "Lonesome Pine Restaurant", + "ShipAddress": "89 Chiaroscuro Rd.", + "ShipCity": "Portland", + "ShipRegion": "OR", + "ShipPostalCode": "97219", + "ShipCountry": "USA" + }, { + "OrderID": 10551, + "CustomerID": "FURIB", + "EmployeeID": 4, + "OrderDate": "1997-05-28T00:00:00", + "RequiredDate": "1997-07-09T00:00:00", + "ShippedDate": "1997-06-06T00:00:00", + "ShipVia": 3, + "Freight": "72.9500", + "ShipName": "Furia Bacalhau e Frutos do Mar", + "ShipAddress": "Jardim das rosas n. 32", + "ShipCity": "Lisboa", + "ShipRegion": null, + "ShipPostalCode": "1675", + "ShipCountry": "Portugal" + }, { + "OrderID": 10554, + "CustomerID": "OTTIK", + "EmployeeID": 4, + "OrderDate": "1997-05-30T00:00:00", + "RequiredDate": "1997-06-27T00:00:00", + "ShippedDate": "1997-06-05T00:00:00", + "ShipVia": 3, + "Freight": "120.9700", + "ShipName": "Ottilies K\u00e4seladen", + "ShipAddress": "Mehrheimerstr. 369", + "ShipCity": "K\u00f6ln", + "ShipRegion": null, + "ShipPostalCode": "50739", + "ShipCountry": "Germany" + }, { + "OrderID": 10564, + "CustomerID": "RATTC", + "EmployeeID": 4, + "OrderDate": "1997-06-10T00:00:00", + "RequiredDate": "1997-07-08T00:00:00", + "ShippedDate": "1997-06-16T00:00:00", + "ShipVia": 3, + "Freight": "13.7500", + "ShipName": "Rattlesnake Canyon Grocery", + "ShipAddress": "2817 Milton Dr.", + "ShipCity": "Albuquerque", + "ShipRegion": "NM", + "ShipPostalCode": "87110", + "ShipCountry": "USA" + }, { + "OrderID": 10574, + "CustomerID": "TRAIH", + "EmployeeID": 4, + "OrderDate": "1997-06-19T00:00:00", + "RequiredDate": "1997-07-17T00:00:00", + "ShippedDate": "1997-06-30T00:00:00", + "ShipVia": 2, + "Freight": "37.6000", + "ShipName": "Trail's Head Gourmet Provisioners", + "ShipAddress": "722 DaVinci Blvd.", + "ShipCity": "Kirkland", + "ShipRegion": "WA", + "ShipPostalCode": "98034", + "ShipCountry": "USA" + }, { + "OrderID": 10578, + "CustomerID": "BSBEV", + "EmployeeID": 4, + "OrderDate": "1997-06-24T00:00:00", + "RequiredDate": "1997-07-22T00:00:00", + "ShippedDate": "1997-07-25T00:00:00", + "ShipVia": 3, + "Freight": "29.6000", + "ShipName": "B's Beverages", + "ShipAddress": "Fauntleroy Circus", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "EC2 5NT", + "ShipCountry": "UK" + }, { + "OrderID": 10580, + "CustomerID": "OTTIK", + "EmployeeID": 4, + "OrderDate": "1997-06-26T00:00:00", + "RequiredDate": "1997-07-24T00:00:00", + "ShippedDate": "1997-07-01T00:00:00", + "ShipVia": 3, + "Freight": "75.8900", + "ShipName": "Ottilies K\u00e4seladen", + "ShipAddress": "Mehrheimerstr. 369", + "ShipCity": "K\u00f6ln", + "ShipRegion": null, + "ShipPostalCode": "50739", + "ShipCountry": "Germany" + }, { + "OrderID": 10584, + "CustomerID": "BLONP", + "EmployeeID": 4, + "OrderDate": "1997-06-30T00:00:00", + "RequiredDate": "1997-07-28T00:00:00", + "ShippedDate": "1997-07-04T00:00:00", + "ShipVia": 1, + "Freight": "59.1400", + "ShipName": "Blondel p\u00e8re et fils", + "ShipAddress": "24, place Kl\u00e9ber", + "ShipCity": "Strasbourg", + "ShipRegion": null, + "ShipPostalCode": "67000", + "ShipCountry": "France" + }, { + "OrderID": 10590, + "CustomerID": "MEREP", + "EmployeeID": 4, + "OrderDate": "1997-07-07T00:00:00", + "RequiredDate": "1997-08-04T00:00:00", + "ShippedDate": "1997-07-14T00:00:00", + "ShipVia": 3, + "Freight": "44.7700", + "ShipName": "M\u00e8re Paillarde", + "ShipAddress": "43 rue St. Laurent", + "ShipCity": "Montr\u00e9al", + "ShipRegion": "Qu\u00e9bec", + "ShipPostalCode": "H1J 1C3", + "ShipCountry": "Canada" + }, { + "OrderID": 10600, + "CustomerID": "HUNGC", + "EmployeeID": 4, + "OrderDate": "1997-07-16T00:00:00", + "RequiredDate": "1997-08-13T00:00:00", + "ShippedDate": "1997-07-21T00:00:00", + "ShipVia": 1, + "Freight": "45.1300", + "ShipName": "Hungry Coyote Import Store", + "ShipAddress": "City Center Plaza 516 Main St.", + "ShipCity": "Elgin", + "ShipRegion": "OR", + "ShipPostalCode": "97827", + "ShipCountry": "USA" + }, { + "OrderID": 10606, + "CustomerID": "TRADH", + "EmployeeID": 4, + "OrderDate": "1997-07-22T00:00:00", + "RequiredDate": "1997-08-19T00:00:00", + "ShippedDate": "1997-07-31T00:00:00", + "ShipVia": 3, + "Freight": "79.4000", + "ShipName": "Tradi\u00e7ao Hipermercados", + "ShipAddress": "Av. In\u00eas de Castro, 414", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05634-030", + "ShipCountry": "Brazil" + }, { + "OrderID": 10608, + "CustomerID": "TOMSP", + "EmployeeID": 4, + "OrderDate": "1997-07-23T00:00:00", + "RequiredDate": "1997-08-20T00:00:00", + "ShippedDate": "1997-08-01T00:00:00", + "ShipVia": 2, + "Freight": "27.7900", + "ShipName": "Toms Spezialit\u00e4ten", + "ShipAddress": "Luisenstr. 48", + "ShipCity": "M\u00fcnster", + "ShipRegion": null, + "ShipPostalCode": "44087", + "ShipCountry": "Germany" + }, { + "OrderID": 10613, + "CustomerID": "HILAA", + "EmployeeID": 4, + "OrderDate": "1997-07-29T00:00:00", + "RequiredDate": "1997-08-26T00:00:00", + "ShippedDate": "1997-08-01T00:00:00", + "ShipVia": 2, + "Freight": "8.1100", + "ShipName": "HILARION-Abastos", + "ShipAddress": "Carrera 22 con Ave. Carlos Soublette #8-35", + "ShipCity": "San Crist\u00f3bal", + "ShipRegion": "T\u00e1chira", + "ShipPostalCode": "5022", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10617, + "CustomerID": "GREAL", + "EmployeeID": 4, + "OrderDate": "1997-07-31T00:00:00", + "RequiredDate": "1997-08-28T00:00:00", + "ShippedDate": "1997-08-04T00:00:00", + "ShipVia": 2, + "Freight": "18.5300", + "ShipName": "Great Lakes Food Market", + "ShipAddress": "2732 Baker Blvd.", + "ShipCity": "Eugene", + "ShipRegion": "OR", + "ShipPostalCode": "97403", + "ShipCountry": "USA" + }, { + "OrderID": 10621, + "CustomerID": "ISLAT", + "EmployeeID": 4, + "OrderDate": "1997-08-05T00:00:00", + "RequiredDate": "1997-09-02T00:00:00", + "ShippedDate": "1997-08-11T00:00:00", + "ShipVia": 2, + "Freight": "23.7300", + "ShipName": "Island Trading", + "ShipAddress": "Garden House Crowther Way", + "ShipCity": "Cowes", + "ShipRegion": "Isle of Wight", + "ShipPostalCode": "PO31 7PJ", + "ShipCountry": "UK" + }, { + "OrderID": 10622, + "CustomerID": "RICAR", + "EmployeeID": 4, + "OrderDate": "1997-08-06T00:00:00", + "RequiredDate": "1997-09-03T00:00:00", + "ShippedDate": "1997-08-11T00:00:00", + "ShipVia": 3, + "Freight": "50.9700", + "ShipName": "Ricardo Adocicados", + "ShipAddress": "Av. Copacabana, 267", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "02389-890", + "ShipCountry": "Brazil" + }, { + "OrderID": 10624, + "CustomerID": "THECR", + "EmployeeID": 4, + "OrderDate": "1997-08-07T00:00:00", + "RequiredDate": "1997-09-04T00:00:00", + "ShippedDate": "1997-08-19T00:00:00", + "ShipVia": 2, + "Freight": "94.8000", + "ShipName": "The Cracker Box", + "ShipAddress": "55 Grizzly Peak Rd.", + "ShipCity": "Butte", + "ShipRegion": "MT", + "ShipPostalCode": "59801", + "ShipCountry": "USA" + }, { + "OrderID": 10628, + "CustomerID": "BLONP", + "EmployeeID": 4, + "OrderDate": "1997-08-12T00:00:00", + "RequiredDate": "1997-09-09T00:00:00", + "ShippedDate": "1997-08-20T00:00:00", + "ShipVia": 3, + "Freight": "30.3600", + "ShipName": "Blondel p\u00e8re et fils", + "ShipAddress": "24, place Kl\u00e9ber", + "ShipCity": "Strasbourg", + "ShipRegion": null, + "ShipPostalCode": "67000", + "ShipCountry": "France" + }, { + "OrderID": 10629, + "CustomerID": "GODOS", + "EmployeeID": 4, + "OrderDate": "1997-08-12T00:00:00", + "RequiredDate": "1997-09-09T00:00:00", + "ShippedDate": "1997-08-20T00:00:00", + "ShipVia": 3, + "Freight": "85.4600", + "ShipName": "Godos Cocina T\u00edpica", + "ShipAddress": "C/ Romero, 33", + "ShipCity": "Sevilla", + "ShipRegion": null, + "ShipPostalCode": "41101", + "ShipCountry": "Spain" + }, { + "OrderID": 10634, + "CustomerID": "FOLIG", + "EmployeeID": 4, + "OrderDate": "1997-08-15T00:00:00", + "RequiredDate": "1997-09-12T00:00:00", + "ShippedDate": "1997-08-21T00:00:00", + "ShipVia": 3, + "Freight": "487.3800", + "ShipName": "Folies gourmandes", + "ShipAddress": "184, chauss\u00e9e de Tournai", + "ShipCity": "Lille", + "ShipRegion": null, + "ShipPostalCode": "59000", + "ShipCountry": "France" + }, { + "OrderID": 10636, + "CustomerID": "WARTH", + "EmployeeID": 4, + "OrderDate": "1997-08-19T00:00:00", + "RequiredDate": "1997-09-16T00:00:00", + "ShippedDate": "1997-08-26T00:00:00", + "ShipVia": 1, + "Freight": "1.1500", + "ShipName": "Wartian Herkku", + "ShipAddress": "Torikatu 38", + "ShipCity": "Oulu", + "ShipRegion": null, + "ShipPostalCode": "90110", + "ShipCountry": "Finland" + }, { + "OrderID": 10640, + "CustomerID": "WANDK", + "EmployeeID": 4, + "OrderDate": "1997-08-21T00:00:00", + "RequiredDate": "1997-09-18T00:00:00", + "ShippedDate": "1997-08-28T00:00:00", + "ShipVia": 1, + "Freight": "23.5500", + "ShipName": "Die Wandernde Kuh", + "ShipAddress": "Adenauerallee 900", + "ShipCity": "Stuttgart", + "ShipRegion": null, + "ShipPostalCode": "70563", + "ShipCountry": "Germany" + }, { + "OrderID": 10641, + "CustomerID": "HILAA", + "EmployeeID": 4, + "OrderDate": "1997-08-22T00:00:00", + "RequiredDate": "1997-09-19T00:00:00", + "ShippedDate": "1997-08-26T00:00:00", + "ShipVia": 2, + "Freight": "179.6100", + "ShipName": "HILARION-Abastos", + "ShipAddress": "Carrera 22 con Ave. Carlos Soublette #8-35", + "ShipCity": "San Crist\u00f3bal", + "ShipRegion": "T\u00e1chira", + "ShipPostalCode": "5022", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10645, + "CustomerID": "HANAR", + "EmployeeID": 4, + "OrderDate": "1997-08-26T00:00:00", + "RequiredDate": "1997-09-23T00:00:00", + "ShippedDate": "1997-09-02T00:00:00", + "ShipVia": 1, + "Freight": "12.4100", + "ShipName": "Hanari Carnes", + "ShipAddress": "Rua do Pa\u00e7o, 67", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "05454-876", + "ShipCountry": "Brazil" + }, { + "OrderID": 10647, + "CustomerID": "QUEDE", + "EmployeeID": 4, + "OrderDate": "1997-08-27T00:00:00", + "RequiredDate": "1997-09-10T00:00:00", + "ShippedDate": "1997-09-03T00:00:00", + "ShipVia": 2, + "Freight": "45.5400", + "ShipName": "Que Del\u00edcia", + "ShipAddress": "Rua da Panificadora, 12", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "02389-673", + "ShipCountry": "Brazil" + }, { + "OrderID": 10652, + "CustomerID": "GOURL", + "EmployeeID": 4, + "OrderDate": "1997-09-01T00:00:00", + "RequiredDate": "1997-09-29T00:00:00", + "ShippedDate": "1997-09-08T00:00:00", + "ShipVia": 2, + "Freight": "7.1400", + "ShipName": "Gourmet Lanchonetes", + "ShipAddress": "Av. Brasil, 442", + "ShipCity": "Campinas", + "ShipRegion": "SP", + "ShipPostalCode": "04876-786", + "ShipCountry": "Brazil" + }, { + "OrderID": 10658, + "CustomerID": "QUICK", + "EmployeeID": 4, + "OrderDate": "1997-09-05T00:00:00", + "RequiredDate": "1997-10-03T00:00:00", + "ShippedDate": "1997-09-08T00:00:00", + "ShipVia": 1, + "Freight": "364.1500", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10670, + "CustomerID": "FRANK", + "EmployeeID": 4, + "OrderDate": "1997-09-16T00:00:00", + "RequiredDate": "1997-10-14T00:00:00", + "ShippedDate": "1997-09-18T00:00:00", + "ShipVia": 1, + "Freight": "203.4800", + "ShipName": "Frankenversand", + "ShipAddress": "Berliner Platz 43", + "ShipCity": "M\u00fcnchen", + "ShipRegion": null, + "ShipPostalCode": "80805", + "ShipCountry": "Germany" + }, { + "OrderID": 10674, + "CustomerID": "ISLAT", + "EmployeeID": 4, + "OrderDate": "1997-09-18T00:00:00", + "RequiredDate": "1997-10-16T00:00:00", + "ShippedDate": "1997-09-30T00:00:00", + "ShipVia": 2, + "Freight": "0.9000", + "ShipName": "Island Trading", + "ShipAddress": "Garden House Crowther Way", + "ShipCity": "Cowes", + "ShipRegion": "Isle of Wight", + "ShipPostalCode": "PO31 7PJ", + "ShipCountry": "UK" + }, { + "OrderID": 10685, + "CustomerID": "GOURL", + "EmployeeID": 4, + "OrderDate": "1997-09-29T00:00:00", + "RequiredDate": "1997-10-13T00:00:00", + "ShippedDate": "1997-10-03T00:00:00", + "ShipVia": 2, + "Freight": "33.7500", + "ShipName": "Gourmet Lanchonetes", + "ShipAddress": "Av. Brasil, 442", + "ShipCity": "Campinas", + "ShipRegion": "SP", + "ShipPostalCode": "04876-786", + "ShipCountry": "Brazil" + }, { + "OrderID": 10688, + "CustomerID": "VAFFE", + "EmployeeID": 4, + "OrderDate": "1997-10-01T00:00:00", + "RequiredDate": "1997-10-15T00:00:00", + "ShippedDate": "1997-10-07T00:00:00", + "ShipVia": 2, + "Freight": "299.0900", + "ShipName": "Vaffeljernet", + "ShipAddress": "Smagsloget 45", + "ShipCity": "\u00c5rhus", + "ShipRegion": null, + "ShipPostalCode": "8200", + "ShipCountry": "Denmark" + }, { + "OrderID": 10692, + "CustomerID": "ALFKI", + "EmployeeID": 4, + "OrderDate": "1997-10-03T00:00:00", + "RequiredDate": "1997-10-31T00:00:00", + "ShippedDate": "1997-10-13T00:00:00", + "ShipVia": 2, + "Freight": "61.0200", + "ShipName": "Alfred's Futterkiste", + "ShipAddress": "Obere Str. 57", + "ShipCity": "Berlin", + "ShipRegion": null, + "ShipPostalCode": "12209", + "ShipCountry": "Germany" + }, { + "OrderID": 10698, + "CustomerID": "ERNSH", + "EmployeeID": 4, + "OrderDate": "1997-10-09T00:00:00", + "RequiredDate": "1997-11-06T00:00:00", + "ShippedDate": "1997-10-17T00:00:00", + "ShipVia": 1, + "Freight": "272.4700", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10702, + "CustomerID": "ALFKI", + "EmployeeID": 4, + "OrderDate": "1997-10-13T00:00:00", + "RequiredDate": "1997-11-24T00:00:00", + "ShippedDate": "1997-10-21T00:00:00", + "ShipVia": 1, + "Freight": "23.9400", + "ShipName": "Alfred's Futterkiste", + "ShipAddress": "Obere Str. 57", + "ShipCity": "Berlin", + "ShipRegion": null, + "ShipPostalCode": "12209", + "ShipCountry": "Germany" + }, { + "OrderID": 10707, + "CustomerID": "AROUT", + "EmployeeID": 4, + "OrderDate": "1997-10-16T00:00:00", + "RequiredDate": "1997-10-30T00:00:00", + "ShippedDate": "1997-10-23T00:00:00", + "ShipVia": 3, + "Freight": "21.7400", + "ShipName": "Around the Horn", + "ShipAddress": "Brook Farm Stratford St. Mary", + "ShipCity": "Colchester", + "ShipRegion": "Essex", + "ShipPostalCode": "CO7 6JX", + "ShipCountry": "UK" + }, { + "OrderID": 10716, + "CustomerID": "RANCH", + "EmployeeID": 4, + "OrderDate": "1997-10-24T00:00:00", + "RequiredDate": "1997-11-21T00:00:00", + "ShippedDate": "1997-10-27T00:00:00", + "ShipVia": 2, + "Freight": "22.5700", + "ShipName": "Rancho grande", + "ShipAddress": "Av. del Libertador 900", + "ShipCity": "Buenos Aires", + "ShipRegion": null, + "ShipPostalCode": "1010", + "ShipCountry": "Argentina" + }, { + "OrderID": 10725, + "CustomerID": "FAMIA", + "EmployeeID": 4, + "OrderDate": "1997-10-31T00:00:00", + "RequiredDate": "1997-11-28T00:00:00", + "ShippedDate": "1997-11-05T00:00:00", + "ShipVia": 3, + "Freight": "10.8300", + "ShipName": "Familia Arquibaldo", + "ShipAddress": "Rua Or\u00f3s, 92", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05442-030", + "ShipCountry": "Brazil" + }, { + "OrderID": 10726, + "CustomerID": "EASTC", + "EmployeeID": 4, + "OrderDate": "1997-11-03T00:00:00", + "RequiredDate": "1997-11-17T00:00:00", + "ShippedDate": "1997-12-05T00:00:00", + "ShipVia": 1, + "Freight": "16.5600", + "ShipName": "Eastern Connection", + "ShipAddress": "35 King George", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "WX3 6FW", + "ShipCountry": "UK" + }, { + "OrderID": 10728, + "CustomerID": "QUEEN", + "EmployeeID": 4, + "OrderDate": "1997-11-04T00:00:00", + "RequiredDate": "1997-12-02T00:00:00", + "ShippedDate": "1997-11-11T00:00:00", + "ShipVia": 2, + "Freight": "58.3300", + "ShipName": "Queen Cozinha", + "ShipAddress": "Alameda dos Can\u00e0rios, 891", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05487-020", + "ShipCountry": "Brazil" + }, { + "OrderID": 10740, + "CustomerID": "WHITC", + "EmployeeID": 4, + "OrderDate": "1997-11-13T00:00:00", + "RequiredDate": "1997-12-11T00:00:00", + "ShippedDate": "1997-11-25T00:00:00", + "ShipVia": 2, + "Freight": "81.8800", + "ShipName": "White Clover Markets", + "ShipAddress": "1029 - 12th Ave. S.", + "ShipCity": "Seattle", + "ShipRegion": "WA", + "ShipPostalCode": "98124", + "ShipCountry": "USA" + }, { + "OrderID": 10741, + "CustomerID": "AROUT", + "EmployeeID": 4, + "OrderDate": "1997-11-14T00:00:00", + "RequiredDate": "1997-11-28T00:00:00", + "ShippedDate": "1997-11-18T00:00:00", + "ShipVia": 3, + "Freight": "10.9600", + "ShipName": "Around the Horn", + "ShipAddress": "Brook Farm Stratford St. Mary", + "ShipCity": "Colchester", + "ShipRegion": "Essex", + "ShipPostalCode": "CO7 6JX", + "ShipCountry": "UK" + }, { + "OrderID": 10749, + "CustomerID": "ISLAT", + "EmployeeID": 4, + "OrderDate": "1997-11-20T00:00:00", + "RequiredDate": "1997-12-18T00:00:00", + "ShippedDate": "1997-12-19T00:00:00", + "ShipVia": 2, + "Freight": "61.5300", + "ShipName": "Island Trading", + "ShipAddress": "Garden House Crowther Way", + "ShipCity": "Cowes", + "ShipRegion": "Isle of Wight", + "ShipPostalCode": "PO31 7PJ", + "ShipCountry": "UK" + }, { + "OrderID": 10755, + "CustomerID": "BONAP", + "EmployeeID": 4, + "OrderDate": "1997-11-26T00:00:00", + "RequiredDate": "1997-12-24T00:00:00", + "ShippedDate": "1997-11-28T00:00:00", + "ShipVia": 2, + "Freight": "16.7100", + "ShipName": "Bon app'", + "ShipAddress": "12, rue des Bouchers", + "ShipCity": "Marseille", + "ShipRegion": null, + "ShipPostalCode": "13008", + "ShipCountry": "France" + }, { + "OrderID": 10760, + "CustomerID": "MAISD", + "EmployeeID": 4, + "OrderDate": "1997-12-01T00:00:00", + "RequiredDate": "1997-12-29T00:00:00", + "ShippedDate": "1997-12-10T00:00:00", + "ShipVia": 1, + "Freight": "155.6400", + "ShipName": "Maison Dewey", + "ShipAddress": "Rue Joseph-Bens 532", + "ShipCity": "Bruxelles", + "ShipRegion": null, + "ShipPostalCode": "B-1180", + "ShipCountry": "Belgium" + }, { + "OrderID": 10766, + "CustomerID": "OTTIK", + "EmployeeID": 4, + "OrderDate": "1997-12-05T00:00:00", + "RequiredDate": "1998-01-02T00:00:00", + "ShippedDate": "1997-12-09T00:00:00", + "ShipVia": 1, + "Freight": "157.5500", + "ShipName": "Ottilies K\u00e4seladen", + "ShipAddress": "Mehrheimerstr. 369", + "ShipCity": "K\u00f6ln", + "ShipRegion": null, + "ShipPostalCode": "50739", + "ShipCountry": "Germany" + }, { + "OrderID": 10767, + "CustomerID": "SUPRD", + "EmployeeID": 4, + "OrderDate": "1997-12-05T00:00:00", + "RequiredDate": "1998-01-02T00:00:00", + "ShippedDate": "1997-12-15T00:00:00", + "ShipVia": 3, + "Freight": "1.5900", + "ShipName": "Supr\u00eames d\u00e9lices", + "ShipAddress": "Boulevard Tirou, 255", + "ShipCity": "Charleroi", + "ShipRegion": null, + "ShipPostalCode": "B-6000", + "ShipCountry": "Belgium" + }, { + "OrderID": 10774, + "CustomerID": "FOLKO", + "EmployeeID": 4, + "OrderDate": "1997-12-11T00:00:00", + "RequiredDate": "1997-12-25T00:00:00", + "ShippedDate": "1997-12-12T00:00:00", + "ShipVia": 1, + "Freight": "48.2000", + "ShipName": "Folk och f\u00e4 HB", + "ShipAddress": "\u00c5kergatan 24", + "ShipCity": "Br\u00e4cke", + "ShipRegion": null, + "ShipPostalCode": "S-844 67", + "ShipCountry": "Sweden" + }, { + "OrderID": 10783, + "CustomerID": "HANAR", + "EmployeeID": 4, + "OrderDate": "1997-12-18T00:00:00", + "RequiredDate": "1998-01-15T00:00:00", + "ShippedDate": "1997-12-19T00:00:00", + "ShipVia": 2, + "Freight": "124.9800", + "ShipName": "Hanari Carnes", + "ShipAddress": "Rua do Pa\u00e7o, 67", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "05454-876", + "ShipCountry": "Brazil" + }, { + "OrderID": 10784, + "CustomerID": "MAGAA", + "EmployeeID": 4, + "OrderDate": "1997-12-18T00:00:00", + "RequiredDate": "1998-01-15T00:00:00", + "ShippedDate": "1997-12-22T00:00:00", + "ShipVia": 3, + "Freight": "70.0900", + "ShipName": "Magazzini Alimentari Riuniti", + "ShipAddress": "Via Ludovico il Moro 22", + "ShipCity": "Bergamo", + "ShipRegion": null, + "ShipPostalCode": "24100", + "ShipCountry": "Italy" + }, { + "OrderID": 10801, + "CustomerID": "BOLID", + "EmployeeID": 4, + "OrderDate": "1997-12-29T00:00:00", + "RequiredDate": "1998-01-26T00:00:00", + "ShippedDate": "1997-12-31T00:00:00", + "ShipVia": 2, + "Freight": "97.0900", + "ShipName": "B\u00f3lido Comidas preparadas", + "ShipAddress": "C/ Araquil, 67", + "ShipCity": "Madrid", + "ShipRegion": null, + "ShipPostalCode": "28023", + "ShipCountry": "Spain" + }, { + "OrderID": 10802, + "CustomerID": "SIMOB", + "EmployeeID": 4, + "OrderDate": "1997-12-29T00:00:00", + "RequiredDate": "1998-01-26T00:00:00", + "ShippedDate": "1998-01-02T00:00:00", + "ShipVia": 2, + "Freight": "257.2600", + "ShipName": "Simons bistro", + "ShipAddress": "Vinb\u00e6ltet 34", + "ShipCity": "Kobenhavn", + "ShipRegion": null, + "ShipPostalCode": "1734", + "ShipCountry": "Denmark" + }, { + "OrderID": 10803, + "CustomerID": "WELLI", + "EmployeeID": 4, + "OrderDate": "1997-12-30T00:00:00", + "RequiredDate": "1998-01-27T00:00:00", + "ShippedDate": "1998-01-06T00:00:00", + "ShipVia": 1, + "Freight": "55.2300", + "ShipName": "Wellington Importadora", + "ShipAddress": "Rua do Mercado, 12", + "ShipCity": "Resende", + "ShipRegion": "SP", + "ShipPostalCode": "08737-363", + "ShipCountry": "Brazil" + }, { + "OrderID": 10807, + "CustomerID": "FRANS", + "EmployeeID": 4, + "OrderDate": "1997-12-31T00:00:00", + "RequiredDate": "1998-01-28T00:00:00", + "ShippedDate": "1998-01-30T00:00:00", + "ShipVia": 1, + "Freight": "1.3600", + "ShipName": "Franchi S.p.A.", + "ShipAddress": "Via Monte Bianco 34", + "ShipCity": "Torino", + "ShipRegion": null, + "ShipPostalCode": "10100", + "ShipCountry": "Italy" + }, { + "OrderID": 10816, + "CustomerID": "GREAL", + "EmployeeID": 4, + "OrderDate": "1998-01-06T00:00:00", + "RequiredDate": "1998-02-03T00:00:00", + "ShippedDate": "1998-02-04T00:00:00", + "ShipVia": 2, + "Freight": "719.7800", + "ShipName": "Great Lakes Food Market", + "ShipAddress": "2732 Baker Blvd.", + "ShipCity": "Eugene", + "ShipRegion": "OR", + "ShipPostalCode": "97403", + "ShipCountry": "USA" + }, { + "OrderID": 10830, + "CustomerID": "TRADH", + "EmployeeID": 4, + "OrderDate": "1998-01-13T00:00:00", + "RequiredDate": "1998-02-24T00:00:00", + "ShippedDate": "1998-01-21T00:00:00", + "ShipVia": 2, + "Freight": "81.8300", + "ShipName": "Tradi\u00e7ao Hipermercados", + "ShipAddress": "Av. In\u00eas de Castro, 414", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05634-030", + "ShipCountry": "Brazil" + }, { + "OrderID": 10840, + "CustomerID": "LINOD", + "EmployeeID": 4, + "OrderDate": "1998-01-19T00:00:00", + "RequiredDate": "1998-03-02T00:00:00", + "ShippedDate": "1998-02-16T00:00:00", + "ShipVia": 2, + "Freight": "2.7100", + "ShipName": "LINO-Delicateses", + "ShipAddress": "Ave. 5 de Mayo Porlamar", + "ShipCity": "I. de Margarita", + "ShipRegion": "Nueva Esparta", + "ShipPostalCode": "4980", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10843, + "CustomerID": "VICTE", + "EmployeeID": 4, + "OrderDate": "1998-01-21T00:00:00", + "RequiredDate": "1998-02-18T00:00:00", + "ShippedDate": "1998-01-26T00:00:00", + "ShipVia": 2, + "Freight": "9.2600", + "ShipName": "Victuailles en stock", + "ShipAddress": "2, rue du Commerce", + "ShipCity": "Lyon", + "ShipRegion": null, + "ShipPostalCode": "69004", + "ShipCountry": "France" + }, { + "OrderID": 10847, + "CustomerID": "SAVEA", + "EmployeeID": 4, + "OrderDate": "1998-01-22T00:00:00", + "RequiredDate": "1998-02-05T00:00:00", + "ShippedDate": "1998-02-10T00:00:00", + "ShipVia": 3, + "Freight": "487.5700", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10861, + "CustomerID": "WHITC", + "EmployeeID": 4, + "OrderDate": "1998-01-30T00:00:00", + "RequiredDate": "1998-02-27T00:00:00", + "ShippedDate": "1998-02-17T00:00:00", + "ShipVia": 2, + "Freight": "14.9300", + "ShipName": "White Clover Markets", + "ShipAddress": "1029 - 12th Ave. S.", + "ShipCity": "Seattle", + "ShipRegion": "WA", + "ShipPostalCode": "98124", + "ShipCountry": "USA" + }, { + "OrderID": 10863, + "CustomerID": "HILAA", + "EmployeeID": 4, + "OrderDate": "1998-02-02T00:00:00", + "RequiredDate": "1998-03-02T00:00:00", + "ShippedDate": "1998-02-17T00:00:00", + "ShipVia": 2, + "Freight": "30.2600", + "ShipName": "HILARION-Abastos", + "ShipAddress": "Carrera 22 con Ave. Carlos Soublette #8-35", + "ShipCity": "San Crist\u00f3bal", + "ShipRegion": "T\u00e1chira", + "ShipPostalCode": "5022", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10864, + "CustomerID": "AROUT", + "EmployeeID": 4, + "OrderDate": "1998-02-02T00:00:00", + "RequiredDate": "1998-03-02T00:00:00", + "ShippedDate": "1998-02-09T00:00:00", + "ShipVia": 2, + "Freight": "3.0400", + "ShipName": "Around the Horn", + "ShipAddress": "Brook Farm Stratford St. Mary", + "ShipCity": "Colchester", + "ShipRegion": "Essex", + "ShipPostalCode": "CO7 6JX", + "ShipCountry": "UK" + }, { + "OrderID": 10873, + "CustomerID": "WILMK", + "EmployeeID": 4, + "OrderDate": "1998-02-06T00:00:00", + "RequiredDate": "1998-03-06T00:00:00", + "ShippedDate": "1998-02-09T00:00:00", + "ShipVia": 1, + "Freight": "0.8200", + "ShipName": "Wilman Kala", + "ShipAddress": "Keskuskatu 45", + "ShipCity": "Helsinki", + "ShipRegion": null, + "ShipPostalCode": "21240", + "ShipCountry": "Finland" + }, { + "OrderID": 10875, + "CustomerID": "BERGS", + "EmployeeID": 4, + "OrderDate": "1998-02-06T00:00:00", + "RequiredDate": "1998-03-06T00:00:00", + "ShippedDate": "1998-03-03T00:00:00", + "ShipVia": 2, + "Freight": "32.3700", + "ShipName": "Berglunds snabbk\u00f6p", + "ShipAddress": "Berguvsv\u00e4gen 8", + "ShipCity": "Lule\u00e5", + "ShipRegion": null, + "ShipPostalCode": "S-958 22", + "ShipCountry": "Sweden" + }, { + "OrderID": 10878, + "CustomerID": "QUICK", + "EmployeeID": 4, + "OrderDate": "1998-02-10T00:00:00", + "RequiredDate": "1998-03-10T00:00:00", + "ShippedDate": "1998-02-12T00:00:00", + "ShipVia": 1, + "Freight": "46.6900", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10881, + "CustomerID": "CACTU", + "EmployeeID": 4, + "OrderDate": "1998-02-11T00:00:00", + "RequiredDate": "1998-03-11T00:00:00", + "ShippedDate": "1998-02-18T00:00:00", + "ShipVia": 1, + "Freight": "2.8400", + "ShipName": "Cactus Comidas para llevar", + "ShipAddress": "Cerrito 333", + "ShipCity": "Buenos Aires", + "ShipRegion": null, + "ShipPostalCode": "1010", + "ShipCountry": "Argentina" + }, { + "OrderID": 10882, + "CustomerID": "SAVEA", + "EmployeeID": 4, + "OrderDate": "1998-02-11T00:00:00", + "RequiredDate": "1998-03-11T00:00:00", + "ShippedDate": "1998-02-20T00:00:00", + "ShipVia": 3, + "Freight": "23.1000", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10884, + "CustomerID": "LETSS", + "EmployeeID": 4, + "OrderDate": "1998-02-12T00:00:00", + "RequiredDate": "1998-03-12T00:00:00", + "ShippedDate": "1998-02-13T00:00:00", + "ShipVia": 2, + "Freight": "90.9700", + "ShipName": "Let's Stop N Shop", + "ShipAddress": "87 Polk St. Suite 5", + "ShipCity": "San Francisco", + "ShipRegion": "CA", + "ShipPostalCode": "94117", + "ShipCountry": "USA" + }, { + "OrderID": 10892, + "CustomerID": "MAISD", + "EmployeeID": 4, + "OrderDate": "1998-02-17T00:00:00", + "RequiredDate": "1998-03-17T00:00:00", + "ShippedDate": "1998-02-19T00:00:00", + "ShipVia": 2, + "Freight": "120.2700", + "ShipName": "Maison Dewey", + "ShipAddress": "Rue Joseph-Bens 532", + "ShipCity": "Bruxelles", + "ShipRegion": null, + "ShipPostalCode": "B-1180", + "ShipCountry": "Belgium" + }, { + "OrderID": 10898, + "CustomerID": "OCEAN", + "EmployeeID": 4, + "OrderDate": "1998-02-20T00:00:00", + "RequiredDate": "1998-03-20T00:00:00", + "ShippedDate": "1998-03-06T00:00:00", + "ShipVia": 2, + "Freight": "1.2700", + "ShipName": "Oc\u00e9ano Atl\u00e1ntico Ltda.", + "ShipAddress": "Ing. Gustavo Moncada 8585 Piso 20-A", + "ShipCity": "Buenos Aires", + "ShipRegion": null, + "ShipPostalCode": "1010", + "ShipCountry": "Argentina" + }, { + "OrderID": 10901, + "CustomerID": "HILAA", + "EmployeeID": 4, + "OrderDate": "1998-02-23T00:00:00", + "RequiredDate": "1998-03-23T00:00:00", + "ShippedDate": "1998-02-26T00:00:00", + "ShipVia": 1, + "Freight": "62.0900", + "ShipName": "HILARION-Abastos", + "ShipAddress": "Carrera 22 con Ave. Carlos Soublette #8-35", + "ShipCity": "San Crist\u00f3bal", + "ShipRegion": "T\u00e1chira", + "ShipPostalCode": "5022", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10906, + "CustomerID": "WOLZA", + "EmployeeID": 4, + "OrderDate": "1998-02-25T00:00:00", + "RequiredDate": "1998-03-11T00:00:00", + "ShippedDate": "1998-03-03T00:00:00", + "ShipVia": 3, + "Freight": "26.2900", + "ShipName": "Wolski Zajazd", + "ShipAddress": "ul. Filtrowa 68", + "ShipCity": "Warszawa", + "ShipRegion": null, + "ShipPostalCode": "01-012", + "ShipCountry": "Poland" + }, { + "OrderID": 10908, + "CustomerID": "REGGC", + "EmployeeID": 4, + "OrderDate": "1998-02-26T00:00:00", + "RequiredDate": "1998-03-26T00:00:00", + "ShippedDate": "1998-03-06T00:00:00", + "ShipVia": 2, + "Freight": "32.9600", + "ShipName": "Reggiani Caseifici", + "ShipAddress": "Strada Provinciale 124", + "ShipCity": "Reggio Emilia", + "ShipRegion": null, + "ShipPostalCode": "42100", + "ShipCountry": "Italy" + }, { + "OrderID": 10913, + "CustomerID": "QUEEN", + "EmployeeID": 4, + "OrderDate": "1998-02-26T00:00:00", + "RequiredDate": "1998-03-26T00:00:00", + "ShippedDate": "1998-03-04T00:00:00", + "ShipVia": 1, + "Freight": "33.0500", + "ShipName": "Queen Cozinha", + "ShipAddress": "Alameda dos Can\u00e0rios, 891", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05487-020", + "ShipCountry": "Brazil" + }, { + "OrderID": 10917, + "CustomerID": "ROMEY", + "EmployeeID": 4, + "OrderDate": "1998-03-02T00:00:00", + "RequiredDate": "1998-03-30T00:00:00", + "ShippedDate": "1998-03-11T00:00:00", + "ShipVia": 2, + "Freight": "8.2900", + "ShipName": "Romero y tomillo", + "ShipAddress": "Gran V\u00eda, 1", + "ShipCity": "Madrid", + "ShipRegion": null, + "ShipPostalCode": "28001", + "ShipCountry": "Spain" + }, { + "OrderID": 10920, + "CustomerID": "AROUT", + "EmployeeID": 4, + "OrderDate": "1998-03-03T00:00:00", + "RequiredDate": "1998-03-31T00:00:00", + "ShippedDate": "1998-03-09T00:00:00", + "ShipVia": 2, + "Freight": "29.6100", + "ShipName": "Around the Horn", + "ShipAddress": "Brook Farm Stratford St. Mary", + "ShipCity": "Colchester", + "ShipRegion": "Essex", + "ShipPostalCode": "CO7 6JX", + "ShipCountry": "UK" + }, { + "OrderID": 10926, + "CustomerID": "ANATR", + "EmployeeID": 4, + "OrderDate": "1998-03-04T00:00:00", + "RequiredDate": "1998-04-01T00:00:00", + "ShippedDate": "1998-03-11T00:00:00", + "ShipVia": 3, + "Freight": "39.9200", + "ShipName": "Ana Trujillo Emparedados y helados", + "ShipAddress": "Avda. de la Constituci\u00f3n 2222", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05021", + "ShipCountry": "Mexico" + }, { + "OrderID": 10927, + "CustomerID": "LACOR", + "EmployeeID": 4, + "OrderDate": "1998-03-05T00:00:00", + "RequiredDate": "1998-04-02T00:00:00", + "ShippedDate": "1998-04-08T00:00:00", + "ShipVia": 1, + "Freight": "19.7900", + "ShipName": "La corne d'abondance", + "ShipAddress": "67, avenue de l'Europe", + "ShipCity": "Versailles", + "ShipRegion": null, + "ShipPostalCode": "78000", + "ShipCountry": "France" + }, { + "OrderID": 10930, + "CustomerID": "SUPRD", + "EmployeeID": 4, + "OrderDate": "1998-03-06T00:00:00", + "RequiredDate": "1998-04-17T00:00:00", + "ShippedDate": "1998-03-18T00:00:00", + "ShipVia": 3, + "Freight": "15.5500", + "ShipName": "Supr\u00eames d\u00e9lices", + "ShipAddress": "Boulevard Tirou, 255", + "ShipCity": "Charleroi", + "ShipRegion": null, + "ShipPostalCode": "B-6000", + "ShipCountry": "Belgium" + }, { + "OrderID": 10931, + "CustomerID": "RICSU", + "EmployeeID": 4, + "OrderDate": "1998-03-06T00:00:00", + "RequiredDate": "1998-03-20T00:00:00", + "ShippedDate": "1998-03-19T00:00:00", + "ShipVia": 2, + "Freight": "13.6000", + "ShipName": "Richter Supermarkt", + "ShipAddress": "Starenweg 5", + "ShipCity": "Gen\u00e8ve", + "ShipRegion": null, + "ShipPostalCode": "1204", + "ShipCountry": "Switzerland" + }, { + "OrderID": 10935, + "CustomerID": "WELLI", + "EmployeeID": 4, + "OrderDate": "1998-03-09T00:00:00", + "RequiredDate": "1998-04-06T00:00:00", + "ShippedDate": "1998-03-18T00:00:00", + "ShipVia": 3, + "Freight": "47.5900", + "ShipName": "Wellington Importadora", + "ShipAddress": "Rua do Mercado, 12", + "ShipCity": "Resende", + "ShipRegion": "SP", + "ShipPostalCode": "08737-363", + "ShipCountry": "Brazil" + }, { + "OrderID": 10943, + "CustomerID": "BSBEV", + "EmployeeID": 4, + "OrderDate": "1998-03-11T00:00:00", + "RequiredDate": "1998-04-08T00:00:00", + "ShippedDate": "1998-03-19T00:00:00", + "ShipVia": 2, + "Freight": "2.1700", + "ShipName": "B's Beverages", + "ShipAddress": "Fauntleroy Circus", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "EC2 5NT", + "ShipCountry": "UK" + }, { + "OrderID": 10945, + "CustomerID": "MORGK", + "EmployeeID": 4, + "OrderDate": "1998-03-12T00:00:00", + "RequiredDate": "1998-04-09T00:00:00", + "ShippedDate": "1998-03-18T00:00:00", + "ShipVia": 1, + "Freight": "10.2200", + "ShipName": "Morgenstern Gesundkost", + "ShipAddress": "Heerstr. 22", + "ShipCity": "Leipzig", + "ShipRegion": null, + "ShipPostalCode": "04179", + "ShipCountry": "Germany" + }, { + "OrderID": 10966, + "CustomerID": "CHOPS", + "EmployeeID": 4, + "OrderDate": "1998-03-20T00:00:00", + "RequiredDate": "1998-04-17T00:00:00", + "ShippedDate": "1998-04-08T00:00:00", + "ShipVia": 1, + "Freight": "27.1900", + "ShipName": "Chop-suey Chinese", + "ShipAddress": "Hauptstr. 31", + "ShipCity": "Bern", + "ShipRegion": null, + "ShipPostalCode": "3012", + "ShipCountry": "Switzerland" + }, { + "OrderID": 10972, + "CustomerID": "LACOR", + "EmployeeID": 4, + "OrderDate": "1998-03-24T00:00:00", + "RequiredDate": "1998-04-21T00:00:00", + "ShippedDate": "1998-03-26T00:00:00", + "ShipVia": 2, + "Freight": "0.0200", + "ShipName": "La corne d'abondance", + "ShipAddress": "67, avenue de l'Europe", + "ShipCity": "Versailles", + "ShipRegion": null, + "ShipPostalCode": "78000", + "ShipCountry": "France" + }, { + "OrderID": 10980, + "CustomerID": "FOLKO", + "EmployeeID": 4, + "OrderDate": "1998-03-27T00:00:00", + "RequiredDate": "1998-05-08T00:00:00", + "ShippedDate": "1998-04-17T00:00:00", + "ShipVia": 1, + "Freight": "1.2600", + "ShipName": "Folk och f\u00e4 HB", + "ShipAddress": "\u00c5kergatan 24", + "ShipCity": "Br\u00e4cke", + "ShipRegion": null, + "ShipPostalCode": "S-844 67", + "ShipCountry": "Sweden" + }, { + "OrderID": 10996, + "CustomerID": "QUICK", + "EmployeeID": 4, + "OrderDate": "1998-04-02T00:00:00", + "RequiredDate": "1998-04-30T00:00:00", + "ShippedDate": "1998-04-10T00:00:00", + "ShipVia": 2, + "Freight": "1.1200", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 11002, + "CustomerID": "SAVEA", + "EmployeeID": 4, + "OrderDate": "1998-04-06T00:00:00", + "RequiredDate": "1998-05-04T00:00:00", + "ShippedDate": "1998-04-16T00:00:00", + "ShipVia": 1, + "Freight": "141.1600", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 11018, + "CustomerID": "LONEP", + "EmployeeID": 4, + "OrderDate": "1998-04-13T00:00:00", + "RequiredDate": "1998-05-11T00:00:00", + "ShippedDate": "1998-04-16T00:00:00", + "ShipVia": 2, + "Freight": "11.6500", + "ShipName": "Lonesome Pine Restaurant", + "ShipAddress": "89 Chiaroscuro Rd.", + "ShipCity": "Portland", + "ShipRegion": "OR", + "ShipPostalCode": "97219", + "ShipCountry": "USA" + }, { + "OrderID": 11024, + "CustomerID": "EASTC", + "EmployeeID": 4, + "OrderDate": "1998-04-15T00:00:00", + "RequiredDate": "1998-05-13T00:00:00", + "ShippedDate": "1998-04-20T00:00:00", + "ShipVia": 1, + "Freight": "74.3600", + "ShipName": "Eastern Connection", + "ShipAddress": "35 King George", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "WX3 6FW", + "ShipCountry": "UK" + }, { + "OrderID": 11026, + "CustomerID": "FRANS", + "EmployeeID": 4, + "OrderDate": "1998-04-15T00:00:00", + "RequiredDate": "1998-05-13T00:00:00", + "ShippedDate": "1998-04-28T00:00:00", + "ShipVia": 1, + "Freight": "47.0900", + "ShipName": "Franchi S.p.A.", + "ShipAddress": "Via Monte Bianco 34", + "ShipCity": "Torino", + "ShipRegion": null, + "ShipPostalCode": "10100", + "ShipCountry": "Italy" + }, { + "OrderID": 11029, + "CustomerID": "CHOPS", + "EmployeeID": 4, + "OrderDate": "1998-04-16T00:00:00", + "RequiredDate": "1998-05-14T00:00:00", + "ShippedDate": "1998-04-27T00:00:00", + "ShipVia": 1, + "Freight": "47.8400", + "ShipName": "Chop-suey Chinese", + "ShipAddress": "Hauptstr. 31", + "ShipCity": "Bern", + "ShipRegion": null, + "ShipPostalCode": "3012", + "ShipCountry": "Switzerland" + }, { + "OrderID": 11040, + "CustomerID": "GREAL", + "EmployeeID": 4, + "OrderDate": "1998-04-22T00:00:00", + "RequiredDate": "1998-05-20T00:00:00", + "ShippedDate": null, + "ShipVia": 3, + "Freight": "18.8400", + "ShipName": "Great Lakes Food Market", + "ShipAddress": "2732 Baker Blvd.", + "ShipCity": "Eugene", + "ShipRegion": "OR", + "ShipPostalCode": "97403", + "ShipCountry": "USA" + }, { + "OrderID": 11044, + "CustomerID": "WOLZA", + "EmployeeID": 4, + "OrderDate": "1998-04-23T00:00:00", + "RequiredDate": "1998-05-21T00:00:00", + "ShippedDate": "1998-05-01T00:00:00", + "ShipVia": 1, + "Freight": "8.7200", + "ShipName": "Wolski Zajazd", + "ShipAddress": "ul. Filtrowa 68", + "ShipCity": "Warszawa", + "ShipRegion": null, + "ShipPostalCode": "01-012", + "ShipCountry": "Poland" + }, { + "OrderID": 11061, + "CustomerID": "GREAL", + "EmployeeID": 4, + "OrderDate": "1998-04-30T00:00:00", + "RequiredDate": "1998-06-11T00:00:00", + "ShippedDate": null, + "ShipVia": 3, + "Freight": "14.0100", + "ShipName": "Great Lakes Food Market", + "ShipAddress": "2732 Baker Blvd.", + "ShipCity": "Eugene", + "ShipRegion": "OR", + "ShipPostalCode": "97403", + "ShipCountry": "USA" + }, { + "OrderID": 11062, + "CustomerID": "REGGC", + "EmployeeID": 4, + "OrderDate": "1998-04-30T00:00:00", + "RequiredDate": "1998-05-28T00:00:00", + "ShippedDate": null, + "ShipVia": 2, + "Freight": "29.9300", + "ShipName": "Reggiani Caseifici", + "ShipAddress": "Strada Provinciale 124", + "ShipCity": "Reggio Emilia", + "ShipRegion": null, + "ShipPostalCode": "42100", + "ShipCountry": "Italy" + }, { + "OrderID": 11072, + "CustomerID": "ERNSH", + "EmployeeID": 4, + "OrderDate": "1998-05-05T00:00:00", + "RequiredDate": "1998-06-02T00:00:00", + "ShippedDate": null, + "ShipVia": 2, + "Freight": "258.6400", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 11076, + "CustomerID": "BONAP", + "EmployeeID": 4, + "OrderDate": "1998-05-06T00:00:00", + "RequiredDate": "1998-06-03T00:00:00", + "ShippedDate": null, + "ShipVia": 2, + "Freight": "38.2800", + "ShipName": "Bon app'", + "ShipAddress": "12, rue des Bouchers", + "ShipCity": "Marseille", + "ShipRegion": null, + "ShipPostalCode": "13008", + "ShipCountry": "France" + }], + "EmployeeID": 4, + "LastName": "Peacock", + "FirstName": "Margaret", + "Title": "Sales Representative", + "TitleOfCourtesy": "Mrs.", + "BirthDate": "1937-09-19T00:00:00", + "HireDate": "1993-05-03T00:00:00", + "Address": "4110 Old Redmond Rd.", + "City": "Redmond", + "Region": "WA", + "PostalCode": "98052", + "Country": "USA", + "HomePhone": "(206) 555-8122", + "Extension": "5176", + "Notes": "Margaret holds a BA in English literature from Concordia College (1958) and an MA from the American Institute of Culinary Arts (1966). She was assigned to the London office temporarily from July through November 1992.", + "ReportsTo": 2, + "PhotoPath": "http://accweb/emmployees/peacock.bmp" + }, { + "Orders": [{ + "OrderID": 10248, + "CustomerID": "VINET", + "EmployeeID": 5, + "OrderDate": "1996-07-04T00:00:00", + "RequiredDate": "1996-08-01T00:00:00", + "ShippedDate": "1996-07-16T00:00:00", + "ShipVia": 3, + "Freight": "32.3800", + "ShipName": "Vins et alcools Chevalier", + "ShipAddress": "59 rue de l'Abbaye", + "ShipCity": "Reims", + "ShipRegion": null, + "ShipPostalCode": "51100", + "ShipCountry": "France" + }, { + "OrderID": 10254, + "CustomerID": "CHOPS", + "EmployeeID": 5, + "OrderDate": "1996-07-11T00:00:00", + "RequiredDate": "1996-08-08T00:00:00", + "ShippedDate": "1996-07-23T00:00:00", + "ShipVia": 2, + "Freight": "22.9800", + "ShipName": "Chop-suey Chinese", + "ShipAddress": "Hauptstr. 31", + "ShipCity": "Bern", + "ShipRegion": null, + "ShipPostalCode": "3012", + "ShipCountry": "Switzerland" + }, { + "OrderID": 10269, + "CustomerID": "WHITC", + "EmployeeID": 5, + "OrderDate": "1996-07-31T00:00:00", + "RequiredDate": "1996-08-14T00:00:00", + "ShippedDate": "1996-08-09T00:00:00", + "ShipVia": 1, + "Freight": "4.5600", + "ShipName": "White Clover Markets", + "ShipAddress": "1029 - 12th Ave. S.", + "ShipCity": "Seattle", + "ShipRegion": "WA", + "ShipPostalCode": "98124", + "ShipCountry": "USA" + }, { + "OrderID": 10297, + "CustomerID": "BLONP", + "EmployeeID": 5, + "OrderDate": "1996-09-04T00:00:00", + "RequiredDate": "1996-10-16T00:00:00", + "ShippedDate": "1996-09-10T00:00:00", + "ShipVia": 2, + "Freight": "5.7400", + "ShipName": "Blondel p\u00e8re et fils", + "ShipAddress": "24, place Kl\u00e9ber", + "ShipCity": "Strasbourg", + "ShipRegion": null, + "ShipPostalCode": "67000", + "ShipCountry": "France" + }, { + "OrderID": 10320, + "CustomerID": "WARTH", + "EmployeeID": 5, + "OrderDate": "1996-10-03T00:00:00", + "RequiredDate": "1996-10-17T00:00:00", + "ShippedDate": "1996-10-18T00:00:00", + "ShipVia": 3, + "Freight": "34.5700", + "ShipName": "Wartian Herkku", + "ShipAddress": "Torikatu 38", + "ShipCity": "Oulu", + "ShipRegion": null, + "ShipPostalCode": "90110", + "ShipCountry": "Finland" + }, { + "OrderID": 10333, + "CustomerID": "WARTH", + "EmployeeID": 5, + "OrderDate": "1996-10-18T00:00:00", + "RequiredDate": "1996-11-15T00:00:00", + "ShippedDate": "1996-10-25T00:00:00", + "ShipVia": 3, + "Freight": "0.5900", + "ShipName": "Wartian Herkku", + "ShipAddress": "Torikatu 38", + "ShipCity": "Oulu", + "ShipRegion": null, + "ShipPostalCode": "90110", + "ShipCountry": "Finland" + }, { + "OrderID": 10358, + "CustomerID": "LAMAI", + "EmployeeID": 5, + "OrderDate": "1996-11-20T00:00:00", + "RequiredDate": "1996-12-18T00:00:00", + "ShippedDate": "1996-11-27T00:00:00", + "ShipVia": 1, + "Freight": "19.6400", + "ShipName": "La maison d'Asie", + "ShipAddress": "1 rue Alsace-Lorraine", + "ShipCity": "Toulouse", + "ShipRegion": null, + "ShipPostalCode": "31000", + "ShipCountry": "France" + }, { + "OrderID": 10359, + "CustomerID": "SEVES", + "EmployeeID": 5, + "OrderDate": "1996-11-21T00:00:00", + "RequiredDate": "1996-12-19T00:00:00", + "ShippedDate": "1996-11-26T00:00:00", + "ShipVia": 3, + "Freight": "288.4300", + "ShipName": "Seven Seas Imports", + "ShipAddress": "90 Wadhurst Rd.", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "OX15 4NB", + "ShipCountry": "UK" + }, { + "OrderID": 10372, + "CustomerID": "QUEEN", + "EmployeeID": 5, + "OrderDate": "1996-12-04T00:00:00", + "RequiredDate": "1997-01-01T00:00:00", + "ShippedDate": "1996-12-09T00:00:00", + "ShipVia": 2, + "Freight": "890.7800", + "ShipName": "Queen Cozinha", + "ShipAddress": "Alameda dos Can\u00e0rios, 891", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05487-020", + "ShipCountry": "Brazil" + }, { + "OrderID": 10378, + "CustomerID": "FOLKO", + "EmployeeID": 5, + "OrderDate": "1996-12-10T00:00:00", + "RequiredDate": "1997-01-07T00:00:00", + "ShippedDate": "1996-12-19T00:00:00", + "ShipVia": 3, + "Freight": "5.4400", + "ShipName": "Folk och f\u00e4 HB", + "ShipAddress": "\u00c5kergatan 24", + "ShipCity": "Br\u00e4cke", + "ShipRegion": null, + "ShipPostalCode": "S-844 67", + "ShipCountry": "Sweden" + }, { + "OrderID": 10397, + "CustomerID": "PRINI", + "EmployeeID": 5, + "OrderDate": "1996-12-27T00:00:00", + "RequiredDate": "1997-01-24T00:00:00", + "ShippedDate": "1997-01-02T00:00:00", + "ShipVia": 1, + "Freight": "60.2600", + "ShipName": "Princesa Isabel Vinhos", + "ShipAddress": "Estrada da sa\u00fade n. 58", + "ShipCity": "Lisboa", + "ShipRegion": null, + "ShipPostalCode": "1756", + "ShipCountry": "Portugal" + }, { + "OrderID": 10463, + "CustomerID": "SUPRD", + "EmployeeID": 5, + "OrderDate": "1997-03-04T00:00:00", + "RequiredDate": "1997-04-01T00:00:00", + "ShippedDate": "1997-03-06T00:00:00", + "ShipVia": 3, + "Freight": "14.7800", + "ShipName": "Supr\u00eames d\u00e9lices", + "ShipAddress": "Boulevard Tirou, 255", + "ShipCity": "Charleroi", + "ShipRegion": null, + "ShipPostalCode": "B-6000", + "ShipCountry": "Belgium" + }, { + "OrderID": 10474, + "CustomerID": "PERIC", + "EmployeeID": 5, + "OrderDate": "1997-03-13T00:00:00", + "RequiredDate": "1997-04-10T00:00:00", + "ShippedDate": "1997-03-21T00:00:00", + "ShipVia": 2, + "Freight": "83.4900", + "ShipName": "Pericles Comidas cl\u00e1sicas", + "ShipAddress": "Calle Dr. Jorge Cash 321", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05033", + "ShipCountry": "Mexico" + }, { + "OrderID": 10477, + "CustomerID": "PRINI", + "EmployeeID": 5, + "OrderDate": "1997-03-17T00:00:00", + "RequiredDate": "1997-04-14T00:00:00", + "ShippedDate": "1997-03-25T00:00:00", + "ShipVia": 2, + "Freight": "13.0200", + "ShipName": "Princesa Isabel Vinhos", + "ShipAddress": "Estrada da sa\u00fade n. 58", + "ShipCity": "Lisboa", + "ShipRegion": null, + "ShipPostalCode": "1756", + "ShipCountry": "Portugal" + }, { + "OrderID": 10529, + "CustomerID": "MAISD", + "EmployeeID": 5, + "OrderDate": "1997-05-07T00:00:00", + "RequiredDate": "1997-06-04T00:00:00", + "ShippedDate": "1997-05-09T00:00:00", + "ShipVia": 2, + "Freight": "66.6900", + "ShipName": "Maison Dewey", + "ShipAddress": "Rue Joseph-Bens 532", + "ShipCity": "Bruxelles", + "ShipRegion": null, + "ShipPostalCode": "B-1180", + "ShipCountry": "Belgium" + }, { + "OrderID": 10549, + "CustomerID": "QUICK", + "EmployeeID": 5, + "OrderDate": "1997-05-27T00:00:00", + "RequiredDate": "1997-06-10T00:00:00", + "ShippedDate": "1997-05-30T00:00:00", + "ShipVia": 1, + "Freight": "171.2400", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10569, + "CustomerID": "RATTC", + "EmployeeID": 5, + "OrderDate": "1997-06-16T00:00:00", + "RequiredDate": "1997-07-14T00:00:00", + "ShippedDate": "1997-07-11T00:00:00", + "ShipVia": 1, + "Freight": "58.9800", + "ShipName": "Rattlesnake Canyon Grocery", + "ShipAddress": "2817 Milton Dr.", + "ShipCity": "Albuquerque", + "ShipRegion": "NM", + "ShipPostalCode": "87110", + "ShipCountry": "USA" + }, { + "OrderID": 10575, + "CustomerID": "MORGK", + "EmployeeID": 5, + "OrderDate": "1997-06-20T00:00:00", + "RequiredDate": "1997-07-04T00:00:00", + "ShippedDate": "1997-06-30T00:00:00", + "ShipVia": 1, + "Freight": "127.3400", + "ShipName": "Morgenstern Gesundkost", + "ShipAddress": "Heerstr. 22", + "ShipCity": "Leipzig", + "ShipRegion": null, + "ShipPostalCode": "04179", + "ShipCountry": "Germany" + }, { + "OrderID": 10607, + "CustomerID": "SAVEA", + "EmployeeID": 5, + "OrderDate": "1997-07-22T00:00:00", + "RequiredDate": "1997-08-19T00:00:00", + "ShippedDate": "1997-07-25T00:00:00", + "ShipVia": 1, + "Freight": "200.2400", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10648, + "CustomerID": "RICAR", + "EmployeeID": 5, + "OrderDate": "1997-08-28T00:00:00", + "RequiredDate": "1997-10-09T00:00:00", + "ShippedDate": "1997-09-09T00:00:00", + "ShipVia": 2, + "Freight": "14.2500", + "ShipName": "Ricardo Adocicados", + "ShipAddress": "Av. Copacabana, 267", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "02389-890", + "ShipCountry": "Brazil" + }, { + "OrderID": 10649, + "CustomerID": "MAISD", + "EmployeeID": 5, + "OrderDate": "1997-08-28T00:00:00", + "RequiredDate": "1997-09-25T00:00:00", + "ShippedDate": "1997-08-29T00:00:00", + "ShipVia": 3, + "Freight": "6.2000", + "ShipName": "Maison Dewey", + "ShipAddress": "Rue Joseph-Bens 532", + "ShipCity": "Bruxelles", + "ShipRegion": null, + "ShipPostalCode": "B-1180", + "ShipCountry": "Belgium" + }, { + "OrderID": 10650, + "CustomerID": "FAMIA", + "EmployeeID": 5, + "OrderDate": "1997-08-29T00:00:00", + "RequiredDate": "1997-09-26T00:00:00", + "ShippedDate": "1997-09-03T00:00:00", + "ShipVia": 3, + "Freight": "176.8100", + "ShipName": "Familia Arquibaldo", + "ShipAddress": "Rua Or\u00f3s, 92", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05442-030", + "ShipCountry": "Brazil" + }, { + "OrderID": 10654, + "CustomerID": "BERGS", + "EmployeeID": 5, + "OrderDate": "1997-09-02T00:00:00", + "RequiredDate": "1997-09-30T00:00:00", + "ShippedDate": "1997-09-11T00:00:00", + "ShipVia": 1, + "Freight": "55.2600", + "ShipName": "Berglunds snabbk\u00f6p", + "ShipAddress": "Berguvsv\u00e4gen 8", + "ShipCity": "Lule\u00e5", + "ShipRegion": null, + "ShipPostalCode": "S-958 22", + "ShipCountry": "Sweden" + }, { + "OrderID": 10675, + "CustomerID": "FRANK", + "EmployeeID": 5, + "OrderDate": "1997-09-19T00:00:00", + "RequiredDate": "1997-10-17T00:00:00", + "ShippedDate": "1997-09-23T00:00:00", + "ShipVia": 2, + "Freight": "31.8500", + "ShipName": "Frankenversand", + "ShipAddress": "Berliner Platz 43", + "ShipCity": "M\u00fcnchen", + "ShipRegion": null, + "ShipPostalCode": "80805", + "ShipCountry": "Germany" + }, { + "OrderID": 10711, + "CustomerID": "SAVEA", + "EmployeeID": 5, + "OrderDate": "1997-10-21T00:00:00", + "RequiredDate": "1997-12-02T00:00:00", + "ShippedDate": "1997-10-29T00:00:00", + "ShipVia": 2, + "Freight": "52.4100", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10714, + "CustomerID": "SAVEA", + "EmployeeID": 5, + "OrderDate": "1997-10-22T00:00:00", + "RequiredDate": "1997-11-19T00:00:00", + "ShippedDate": "1997-10-27T00:00:00", + "ShipVia": 3, + "Freight": "24.4900", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10721, + "CustomerID": "QUICK", + "EmployeeID": 5, + "OrderDate": "1997-10-29T00:00:00", + "RequiredDate": "1997-11-26T00:00:00", + "ShippedDate": "1997-10-31T00:00:00", + "ShipVia": 3, + "Freight": "48.9200", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10730, + "CustomerID": "BONAP", + "EmployeeID": 5, + "OrderDate": "1997-11-05T00:00:00", + "RequiredDate": "1997-12-03T00:00:00", + "ShippedDate": "1997-11-14T00:00:00", + "ShipVia": 1, + "Freight": "20.1200", + "ShipName": "Bon app'", + "ShipAddress": "12, rue des Bouchers", + "ShipCity": "Marseille", + "ShipRegion": null, + "ShipPostalCode": "13008", + "ShipCountry": "France" + }, { + "OrderID": 10761, + "CustomerID": "RATTC", + "EmployeeID": 5, + "OrderDate": "1997-12-02T00:00:00", + "RequiredDate": "1997-12-30T00:00:00", + "ShippedDate": "1997-12-08T00:00:00", + "ShipVia": 2, + "Freight": "18.6600", + "ShipName": "Rattlesnake Canyon Grocery", + "ShipAddress": "2817 Milton Dr.", + "ShipCity": "Albuquerque", + "ShipRegion": "NM", + "ShipPostalCode": "87110", + "ShipCountry": "USA" + }, { + "OrderID": 10812, + "CustomerID": "REGGC", + "EmployeeID": 5, + "OrderDate": "1998-01-02T00:00:00", + "RequiredDate": "1998-01-30T00:00:00", + "ShippedDate": "1998-01-12T00:00:00", + "ShipVia": 1, + "Freight": "59.7800", + "ShipName": "Reggiani Caseifici", + "ShipAddress": "Strada Provinciale 124", + "ShipCity": "Reggio Emilia", + "ShipRegion": null, + "ShipPostalCode": "42100", + "ShipCountry": "Italy" + }, { + "OrderID": 10823, + "CustomerID": "LILAS", + "EmployeeID": 5, + "OrderDate": "1998-01-09T00:00:00", + "RequiredDate": "1998-02-06T00:00:00", + "ShippedDate": "1998-01-13T00:00:00", + "ShipVia": 2, + "Freight": "163.9700", + "ShipName": "LILA-Supermercado", + "ShipAddress": "Carrera 52 con Ave. Bol\u00edvar #65-98 Llano Largo", + "ShipCity": "Barquisimeto", + "ShipRegion": "Lara", + "ShipPostalCode": "3508", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10841, + "CustomerID": "SUPRD", + "EmployeeID": 5, + "OrderDate": "1998-01-20T00:00:00", + "RequiredDate": "1998-02-17T00:00:00", + "ShippedDate": "1998-01-29T00:00:00", + "ShipVia": 2, + "Freight": "424.3000", + "ShipName": "Supr\u00eames d\u00e9lices", + "ShipAddress": "Boulevard Tirou, 255", + "ShipCity": "Charleroi", + "ShipRegion": null, + "ShipPostalCode": "B-6000", + "ShipCountry": "Belgium" + }, { + "OrderID": 10851, + "CustomerID": "RICAR", + "EmployeeID": 5, + "OrderDate": "1998-01-26T00:00:00", + "RequiredDate": "1998-02-23T00:00:00", + "ShippedDate": "1998-02-02T00:00:00", + "ShipVia": 1, + "Freight": "160.5500", + "ShipName": "Ricardo Adocicados", + "ShipAddress": "Av. Copacabana, 267", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "02389-890", + "ShipCountry": "Brazil" + }, { + "OrderID": 10866, + "CustomerID": "BERGS", + "EmployeeID": 5, + "OrderDate": "1998-02-03T00:00:00", + "RequiredDate": "1998-03-03T00:00:00", + "ShippedDate": "1998-02-12T00:00:00", + "ShipVia": 1, + "Freight": "109.1100", + "ShipName": "Berglunds snabbk\u00f6p", + "ShipAddress": "Berguvsv\u00e4gen 8", + "ShipCity": "Lule\u00e5", + "ShipRegion": null, + "ShipPostalCode": "S-958 22", + "ShipCountry": "Sweden" + }, { + "OrderID": 10869, + "CustomerID": "SEVES", + "EmployeeID": 5, + "OrderDate": "1998-02-04T00:00:00", + "RequiredDate": "1998-03-04T00:00:00", + "ShippedDate": "1998-02-09T00:00:00", + "ShipVia": 1, + "Freight": "143.2800", + "ShipName": "Seven Seas Imports", + "ShipAddress": "90 Wadhurst Rd.", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "OX15 4NB", + "ShipCountry": "UK" + }, { + "OrderID": 10870, + "CustomerID": "WOLZA", + "EmployeeID": 5, + "OrderDate": "1998-02-04T00:00:00", + "RequiredDate": "1998-03-04T00:00:00", + "ShippedDate": "1998-02-13T00:00:00", + "ShipVia": 3, + "Freight": "12.0400", + "ShipName": "Wolski Zajazd", + "ShipAddress": "ul. Filtrowa 68", + "ShipCity": "Warszawa", + "ShipRegion": null, + "ShipPostalCode": "01-012", + "ShipCountry": "Poland" + }, { + "OrderID": 10872, + "CustomerID": "GODOS", + "EmployeeID": 5, + "OrderDate": "1998-02-05T00:00:00", + "RequiredDate": "1998-03-05T00:00:00", + "ShippedDate": "1998-02-09T00:00:00", + "ShipVia": 2, + "Freight": "175.3200", + "ShipName": "Godos Cocina T\u00edpica", + "ShipAddress": "C/ Romero, 33", + "ShipCity": "Sevilla", + "ShipRegion": null, + "ShipPostalCode": "41101", + "ShipCountry": "Spain" + }, { + "OrderID": 10874, + "CustomerID": "GODOS", + "EmployeeID": 5, + "OrderDate": "1998-02-06T00:00:00", + "RequiredDate": "1998-03-06T00:00:00", + "ShippedDate": "1998-02-11T00:00:00", + "ShipVia": 2, + "Freight": "19.5800", + "ShipName": "Godos Cocina T\u00edpica", + "ShipAddress": "C/ Romero, 33", + "ShipCity": "Sevilla", + "ShipRegion": null, + "ShipPostalCode": "41101", + "ShipCountry": "Spain" + }, { + "OrderID": 10899, + "CustomerID": "LILAS", + "EmployeeID": 5, + "OrderDate": "1998-02-20T00:00:00", + "RequiredDate": "1998-03-20T00:00:00", + "ShippedDate": "1998-02-26T00:00:00", + "ShipVia": 3, + "Freight": "1.2100", + "ShipName": "LILA-Supermercado", + "ShipAddress": "Carrera 52 con Ave. Bol\u00edvar #65-98 Llano Largo", + "ShipCity": "Barquisimeto", + "ShipRegion": "Lara", + "ShipPostalCode": "3508", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10922, + "CustomerID": "HANAR", + "EmployeeID": 5, + "OrderDate": "1998-03-03T00:00:00", + "RequiredDate": "1998-03-31T00:00:00", + "ShippedDate": "1998-03-05T00:00:00", + "ShipVia": 3, + "Freight": "62.7400", + "ShipName": "Hanari Carnes", + "ShipAddress": "Rua do Pa\u00e7o, 67", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "05454-876", + "ShipCountry": "Brazil" + }, { + "OrderID": 10954, + "CustomerID": "LINOD", + "EmployeeID": 5, + "OrderDate": "1998-03-17T00:00:00", + "RequiredDate": "1998-04-28T00:00:00", + "ShippedDate": "1998-03-20T00:00:00", + "ShipVia": 1, + "Freight": "27.9100", + "ShipName": "LINO-Delicateses", + "ShipAddress": "Ave. 5 de Mayo Porlamar", + "ShipCity": "I. de Margarita", + "ShipRegion": "Nueva Esparta", + "ShipPostalCode": "4980", + "ShipCountry": "Venezuela" + }, { + "OrderID": 11043, + "CustomerID": "SPECD", + "EmployeeID": 5, + "OrderDate": "1998-04-22T00:00:00", + "RequiredDate": "1998-05-20T00:00:00", + "ShippedDate": "1998-04-29T00:00:00", + "ShipVia": 2, + "Freight": "8.8000", + "ShipName": "Sp\u00e9cialit\u00e9s du monde", + "ShipAddress": "25, rue Lauriston", + "ShipCity": "Paris", + "ShipRegion": null, + "ShipPostalCode": "75016", + "ShipCountry": "France" + }], + "EmployeeID": 5, + "LastName": "Buchanan", + "FirstName": "Steven", + "Title": "Sales Manager", + "TitleOfCourtesy": "Mr.", + "BirthDate": "1955-03-04T00:00:00", + "HireDate": "1993-10-17T00:00:00", + "Address": "14 Garrett Hill", + "City": "London", + "Region": null, + "PostalCode": "SW1 8JR", + "Country": "UK", + "HomePhone": "(71) 555-4848", + "Extension": "3453", + "Notes": "Steven Buchanan graduated from St. Andrews University, Scotland, with a BSC degree in 1976. Upon joining the company as a sales representative in 1992, he spent 6 months in an orientation program at the Seattle office and then returned to his permanent post in London. He was promoted to sales manager in March 1993. Mr. Buchanan has completed the courses \"Successful Telemarketing\" and \"International Sales Management.\" He is fluent in French.", + "ReportsTo": 2, + "PhotoPath": "http://accweb/emmployees/buchanan.bmp" + }, { + "Orders": [{ + "OrderID": 10249, + "CustomerID": "TOMSP", + "EmployeeID": 6, + "OrderDate": "1996-07-05T00:00:00", + "RequiredDate": "1996-08-16T00:00:00", + "ShippedDate": "1996-07-10T00:00:00", + "ShipVia": 1, + "Freight": "11.6100", + "ShipName": "Toms Spezialit\u00e4ten", + "ShipAddress": "Luisenstr. 48", + "ShipCity": "M\u00fcnster", + "ShipRegion": null, + "ShipPostalCode": "44087", + "ShipCountry": "Germany" + }, { + "OrderID": 10264, + "CustomerID": "FOLKO", + "EmployeeID": 6, + "OrderDate": "1996-07-24T00:00:00", + "RequiredDate": "1996-08-21T00:00:00", + "ShippedDate": "1996-08-23T00:00:00", + "ShipVia": 3, + "Freight": "3.6700", + "ShipName": "Folk och f\u00e4 HB", + "ShipAddress": "\u00c5kergatan 24", + "ShipCity": "Br\u00e4cke", + "ShipRegion": null, + "ShipPostalCode": "S-844 67", + "ShipCountry": "Sweden" + }, { + "OrderID": 10271, + "CustomerID": "SPLIR", + "EmployeeID": 6, + "OrderDate": "1996-08-01T00:00:00", + "RequiredDate": "1996-08-29T00:00:00", + "ShippedDate": "1996-08-30T00:00:00", + "ShipVia": 2, + "Freight": "4.5400", + "ShipName": "Split Rail Beer & Ale", + "ShipAddress": "P.O. Box 555", + "ShipCity": "Lander", + "ShipRegion": "WY", + "ShipPostalCode": "82520", + "ShipCountry": "USA" + }, { + "OrderID": 10272, + "CustomerID": "RATTC", + "EmployeeID": 6, + "OrderDate": "1996-08-02T00:00:00", + "RequiredDate": "1996-08-30T00:00:00", + "ShippedDate": "1996-08-06T00:00:00", + "ShipVia": 2, + "Freight": "98.0300", + "ShipName": "Rattlesnake Canyon Grocery", + "ShipAddress": "2817 Milton Dr.", + "ShipCity": "Albuquerque", + "ShipRegion": "NM", + "ShipPostalCode": "87110", + "ShipCountry": "USA" + }, { + "OrderID": 10274, + "CustomerID": "VINET", + "EmployeeID": 6, + "OrderDate": "1996-08-06T00:00:00", + "RequiredDate": "1996-09-03T00:00:00", + "ShippedDate": "1996-08-16T00:00:00", + "ShipVia": 1, + "Freight": "6.0100", + "ShipName": "Vins et alcools Chevalier", + "ShipAddress": "59 rue de l'Abbaye", + "ShipCity": "Reims", + "ShipRegion": null, + "ShipPostalCode": "51100", + "ShipCountry": "France" + }, { + "OrderID": 10291, + "CustomerID": "QUEDE", + "EmployeeID": 6, + "OrderDate": "1996-08-27T00:00:00", + "RequiredDate": "1996-09-24T00:00:00", + "ShippedDate": "1996-09-04T00:00:00", + "ShipVia": 2, + "Freight": "6.4000", + "ShipName": "Que Del\u00edcia", + "ShipAddress": "Rua da Panificadora, 12", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "02389-673", + "ShipCountry": "Brazil" + }, { + "OrderID": 10296, + "CustomerID": "LILAS", + "EmployeeID": 6, + "OrderDate": "1996-09-03T00:00:00", + "RequiredDate": "1996-10-01T00:00:00", + "ShippedDate": "1996-09-11T00:00:00", + "ShipVia": 1, + "Freight": "0.1200", + "ShipName": "LILA-Supermercado", + "ShipAddress": "Carrera 52 con Ave. Bol\u00edvar #65-98 Llano Largo", + "ShipCity": "Barquisimeto", + "ShipRegion": "Lara", + "ShipPostalCode": "3508", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10298, + "CustomerID": "HUNGO", + "EmployeeID": 6, + "OrderDate": "1996-09-05T00:00:00", + "RequiredDate": "1996-10-03T00:00:00", + "ShippedDate": "1996-09-11T00:00:00", + "ShipVia": 2, + "Freight": "168.2200", + "ShipName": "Hungry Owl All-Night Grocers", + "ShipAddress": "8 Johnstown Road", + "ShipCity": "Cork", + "ShipRegion": "Co. Cork", + "ShipPostalCode": null, + "ShipCountry": "Ireland" + }, { + "OrderID": 10317, + "CustomerID": "LONEP", + "EmployeeID": 6, + "OrderDate": "1996-09-30T00:00:00", + "RequiredDate": "1996-10-28T00:00:00", + "ShippedDate": "1996-10-10T00:00:00", + "ShipVia": 1, + "Freight": "12.6900", + "ShipName": "Lonesome Pine Restaurant", + "ShipAddress": "89 Chiaroscuro Rd.", + "ShipCity": "Portland", + "ShipRegion": "OR", + "ShipPostalCode": "97219", + "ShipCountry": "USA" + }, { + "OrderID": 10350, + "CustomerID": "LAMAI", + "EmployeeID": 6, + "OrderDate": "1996-11-11T00:00:00", + "RequiredDate": "1996-12-09T00:00:00", + "ShippedDate": "1996-12-03T00:00:00", + "ShipVia": 2, + "Freight": "64.1900", + "ShipName": "La maison d'Asie", + "ShipAddress": "1 rue Alsace-Lorraine", + "ShipCity": "Toulouse", + "ShipRegion": null, + "ShipPostalCode": "31000", + "ShipCountry": "France" + }, { + "OrderID": 10355, + "CustomerID": "AROUT", + "EmployeeID": 6, + "OrderDate": "1996-11-15T00:00:00", + "RequiredDate": "1996-12-13T00:00:00", + "ShippedDate": "1996-11-20T00:00:00", + "ShipVia": 1, + "Freight": "41.9500", + "ShipName": "Around the Horn", + "ShipAddress": "Brook Farm Stratford St. Mary", + "ShipCity": "Colchester", + "ShipRegion": "Essex", + "ShipPostalCode": "CO7 6JX", + "ShipCountry": "UK" + }, { + "OrderID": 10356, + "CustomerID": "WANDK", + "EmployeeID": 6, + "OrderDate": "1996-11-18T00:00:00", + "RequiredDate": "1996-12-16T00:00:00", + "ShippedDate": "1996-11-27T00:00:00", + "ShipVia": 2, + "Freight": "36.7100", + "ShipName": "Die Wandernde Kuh", + "ShipAddress": "Adenauerallee 900", + "ShipCity": "Stuttgart", + "ShipRegion": null, + "ShipPostalCode": "70563", + "ShipCountry": "Germany" + }, { + "OrderID": 10370, + "CustomerID": "CHOPS", + "EmployeeID": 6, + "OrderDate": "1996-12-03T00:00:00", + "RequiredDate": "1996-12-31T00:00:00", + "ShippedDate": "1996-12-27T00:00:00", + "ShipVia": 2, + "Freight": "1.1700", + "ShipName": "Chop-suey Chinese", + "ShipAddress": "Hauptstr. 31", + "ShipCity": "Bern", + "ShipRegion": null, + "ShipPostalCode": "3012", + "ShipCountry": "Switzerland" + }, { + "OrderID": 10390, + "CustomerID": "ERNSH", + "EmployeeID": 6, + "OrderDate": "1996-12-23T00:00:00", + "RequiredDate": "1997-01-20T00:00:00", + "ShippedDate": "1996-12-26T00:00:00", + "ShipVia": 1, + "Freight": "126.3800", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10395, + "CustomerID": "HILAA", + "EmployeeID": 6, + "OrderDate": "1996-12-26T00:00:00", + "RequiredDate": "1997-01-23T00:00:00", + "ShippedDate": "1997-01-03T00:00:00", + "ShipVia": 1, + "Freight": "184.4100", + "ShipName": "HILARION-Abastos", + "ShipAddress": "Carrera 22 con Ave. Carlos Soublette #8-35", + "ShipCity": "San Crist\u00f3bal", + "ShipRegion": "T\u00e1chira", + "ShipPostalCode": "5022", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10423, + "CustomerID": "GOURL", + "EmployeeID": 6, + "OrderDate": "1997-01-23T00:00:00", + "RequiredDate": "1997-02-06T00:00:00", + "ShippedDate": "1997-02-24T00:00:00", + "ShipVia": 3, + "Freight": "24.5000", + "ShipName": "Gourmet Lanchonetes", + "ShipAddress": "Av. Brasil, 442", + "ShipCity": "Campinas", + "ShipRegion": "SP", + "ShipPostalCode": "04876-786", + "ShipCountry": "Brazil" + }, { + "OrderID": 10425, + "CustomerID": "LAMAI", + "EmployeeID": 6, + "OrderDate": "1997-01-24T00:00:00", + "RequiredDate": "1997-02-21T00:00:00", + "ShippedDate": "1997-02-14T00:00:00", + "ShipVia": 2, + "Freight": "7.9300", + "ShipName": "La maison d'Asie", + "ShipAddress": "1 rue Alsace-Lorraine", + "ShipCity": "Toulouse", + "ShipRegion": null, + "ShipPostalCode": "31000", + "ShipCountry": "France" + }, { + "OrderID": 10439, + "CustomerID": "MEREP", + "EmployeeID": 6, + "OrderDate": "1997-02-07T00:00:00", + "RequiredDate": "1997-03-07T00:00:00", + "ShippedDate": "1997-02-10T00:00:00", + "ShipVia": 3, + "Freight": "4.0700", + "ShipName": "M\u00e8re Paillarde", + "ShipAddress": "43 rue St. Laurent", + "ShipCity": "Montr\u00e9al", + "ShipRegion": "Qu\u00e9bec", + "ShipPostalCode": "H1J 1C3", + "ShipCountry": "Canada" + }, { + "OrderID": 10446, + "CustomerID": "TOMSP", + "EmployeeID": 6, + "OrderDate": "1997-02-14T00:00:00", + "RequiredDate": "1997-03-14T00:00:00", + "ShippedDate": "1997-02-19T00:00:00", + "ShipVia": 1, + "Freight": "14.6800", + "ShipName": "Toms Spezialit\u00e4ten", + "ShipAddress": "Luisenstr. 48", + "ShipCity": "M\u00fcnster", + "ShipRegion": null, + "ShipPostalCode": "44087", + "ShipCountry": "Germany" + }, { + "OrderID": 10480, + "CustomerID": "FOLIG", + "EmployeeID": 6, + "OrderDate": "1997-03-20T00:00:00", + "RequiredDate": "1997-04-17T00:00:00", + "ShippedDate": "1997-03-24T00:00:00", + "ShipVia": 2, + "Freight": "1.3500", + "ShipName": "Folies gourmandes", + "ShipAddress": "184, chauss\u00e9e de Tournai", + "ShipCity": "Lille", + "ShipRegion": null, + "ShipPostalCode": "59000", + "ShipCountry": "France" + }, { + "OrderID": 10489, + "CustomerID": "PICCO", + "EmployeeID": 6, + "OrderDate": "1997-03-28T00:00:00", + "RequiredDate": "1997-04-25T00:00:00", + "ShippedDate": "1997-04-09T00:00:00", + "ShipVia": 2, + "Freight": "5.2900", + "ShipName": "Piccolo und mehr", + "ShipAddress": "Geislweg 14", + "ShipCity": "Salzburg", + "ShipRegion": null, + "ShipPostalCode": "5020", + "ShipCountry": "Austria" + }, { + "OrderID": 10500, + "CustomerID": "LAMAI", + "EmployeeID": 6, + "OrderDate": "1997-04-09T00:00:00", + "RequiredDate": "1997-05-07T00:00:00", + "ShippedDate": "1997-04-17T00:00:00", + "ShipVia": 1, + "Freight": "42.6800", + "ShipName": "La maison d'Asie", + "ShipAddress": "1 rue Alsace-Lorraine", + "ShipCity": "Toulouse", + "ShipRegion": null, + "ShipPostalCode": "31000", + "ShipCountry": "France" + }, { + "OrderID": 10503, + "CustomerID": "HUNGO", + "EmployeeID": 6, + "OrderDate": "1997-04-11T00:00:00", + "RequiredDate": "1997-05-09T00:00:00", + "ShippedDate": "1997-04-16T00:00:00", + "ShipVia": 2, + "Freight": "16.7400", + "ShipName": "Hungry Owl All-Night Grocers", + "ShipAddress": "8 Johnstown Road", + "ShipCity": "Cork", + "ShipRegion": "Co. Cork", + "ShipPostalCode": null, + "ShipCountry": "Ireland" + }, { + "OrderID": 10510, + "CustomerID": "SAVEA", + "EmployeeID": 6, + "OrderDate": "1997-04-18T00:00:00", + "RequiredDate": "1997-05-16T00:00:00", + "ShippedDate": "1997-04-28T00:00:00", + "ShipVia": 3, + "Freight": "367.6300", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10519, + "CustomerID": "CHOPS", + "EmployeeID": 6, + "OrderDate": "1997-04-28T00:00:00", + "RequiredDate": "1997-05-26T00:00:00", + "ShippedDate": "1997-05-01T00:00:00", + "ShipVia": 3, + "Freight": "91.7600", + "ShipName": "Chop-suey Chinese", + "ShipAddress": "Hauptstr. 31", + "ShipCity": "Bern", + "ShipRegion": null, + "ShipPostalCode": "3012", + "ShipCountry": "Switzerland" + }, { + "OrderID": 10528, + "CustomerID": "GREAL", + "EmployeeID": 6, + "OrderDate": "1997-05-06T00:00:00", + "RequiredDate": "1997-05-20T00:00:00", + "ShippedDate": "1997-05-09T00:00:00", + "ShipVia": 2, + "Freight": "3.3500", + "ShipName": "Great Lakes Food Market", + "ShipAddress": "2732 Baker Blvd.", + "ShipCity": "Eugene", + "ShipRegion": "OR", + "ShipPostalCode": "97403", + "ShipCountry": "USA" + }, { + "OrderID": 10539, + "CustomerID": "BSBEV", + "EmployeeID": 6, + "OrderDate": "1997-05-16T00:00:00", + "RequiredDate": "1997-06-13T00:00:00", + "ShippedDate": "1997-05-23T00:00:00", + "ShipVia": 3, + "Freight": "12.3600", + "ShipName": "B's Beverages", + "ShipAddress": "Fauntleroy Circus", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "EC2 5NT", + "ShipCountry": "UK" + }, { + "OrderID": 10555, + "CustomerID": "SAVEA", + "EmployeeID": 6, + "OrderDate": "1997-06-02T00:00:00", + "RequiredDate": "1997-06-30T00:00:00", + "ShippedDate": "1997-06-04T00:00:00", + "ShipVia": 3, + "Freight": "252.4900", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10559, + "CustomerID": "BLONP", + "EmployeeID": 6, + "OrderDate": "1997-06-05T00:00:00", + "RequiredDate": "1997-07-03T00:00:00", + "ShippedDate": "1997-06-13T00:00:00", + "ShipVia": 1, + "Freight": "8.0500", + "ShipName": "Blondel p\u00e8re et fils", + "ShipAddress": "24, place Kl\u00e9ber", + "ShipCity": "Strasbourg", + "ShipRegion": null, + "ShipPostalCode": "67000", + "ShipCountry": "France" + }, { + "OrderID": 10599, + "CustomerID": "BSBEV", + "EmployeeID": 6, + "OrderDate": "1997-07-15T00:00:00", + "RequiredDate": "1997-08-26T00:00:00", + "ShippedDate": "1997-07-21T00:00:00", + "ShipVia": 3, + "Freight": "29.9800", + "ShipName": "B's Beverages", + "ShipAddress": "Fauntleroy Circus", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "EC2 5NT", + "ShipCountry": "UK" + }, { + "OrderID": 10611, + "CustomerID": "WOLZA", + "EmployeeID": 6, + "OrderDate": "1997-07-25T00:00:00", + "RequiredDate": "1997-08-22T00:00:00", + "ShippedDate": "1997-08-01T00:00:00", + "ShipVia": 2, + "Freight": "80.6500", + "ShipName": "Wolski Zajazd", + "ShipAddress": "ul. Filtrowa 68", + "ShipCity": "Warszawa", + "ShipRegion": null, + "ShipPostalCode": "01-012", + "ShipCountry": "Poland" + }, { + "OrderID": 10637, + "CustomerID": "QUEEN", + "EmployeeID": 6, + "OrderDate": "1997-08-19T00:00:00", + "RequiredDate": "1997-09-16T00:00:00", + "ShippedDate": "1997-08-26T00:00:00", + "ShipVia": 1, + "Freight": "201.2900", + "ShipName": "Queen Cozinha", + "ShipAddress": "Alameda dos Can\u00e0rios, 891", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05487-020", + "ShipCountry": "Brazil" + }, { + "OrderID": 10643, + "CustomerID": "ALFKI", + "EmployeeID": 6, + "OrderDate": "1997-08-25T00:00:00", + "RequiredDate": "1997-09-22T00:00:00", + "ShippedDate": "1997-09-02T00:00:00", + "ShipVia": 1, + "Freight": "29.4600", + "ShipName": "Alfreds Futterkiste", + "ShipAddress": "Obere Str. 57", + "ShipCity": "Berlin", + "ShipRegion": null, + "ShipPostalCode": "12209", + "ShipCountry": "Germany" + }, { + "OrderID": 10656, + "CustomerID": "GREAL", + "EmployeeID": 6, + "OrderDate": "1997-09-04T00:00:00", + "RequiredDate": "1997-10-02T00:00:00", + "ShippedDate": "1997-09-10T00:00:00", + "ShipVia": 1, + "Freight": "57.1500", + "ShipName": "Great Lakes Food Market", + "ShipAddress": "2732 Baker Blvd.", + "ShipCity": "Eugene", + "ShipRegion": "OR", + "ShipPostalCode": "97403", + "ShipCountry": "USA" + }, { + "OrderID": 10701, + "CustomerID": "HUNGO", + "EmployeeID": 6, + "OrderDate": "1997-10-13T00:00:00", + "RequiredDate": "1997-10-27T00:00:00", + "ShippedDate": "1997-10-15T00:00:00", + "ShipVia": 3, + "Freight": "220.3100", + "ShipName": "Hungry Owl All-Night Grocers", + "ShipAddress": "8 Johnstown Road", + "ShipCity": "Cork", + "ShipRegion": "Co. Cork", + "ShipPostalCode": null, + "ShipCountry": "Ireland" + }, { + "OrderID": 10703, + "CustomerID": "FOLKO", + "EmployeeID": 6, + "OrderDate": "1997-10-14T00:00:00", + "RequiredDate": "1997-11-11T00:00:00", + "ShippedDate": "1997-10-20T00:00:00", + "ShipVia": 2, + "Freight": "152.3000", + "ShipName": "Folk och f\u00e4 HB", + "ShipAddress": "\u00c5kergatan 24", + "ShipCity": "Br\u00e4cke", + "ShipRegion": null, + "ShipPostalCode": "S-844 67", + "ShipCountry": "Sweden" + }, { + "OrderID": 10704, + "CustomerID": "QUEEN", + "EmployeeID": 6, + "OrderDate": "1997-10-14T00:00:00", + "RequiredDate": "1997-11-11T00:00:00", + "ShippedDate": "1997-11-07T00:00:00", + "ShipVia": 1, + "Freight": "4.7800", + "ShipName": "Queen Cozinha", + "ShipAddress": "Alameda dos Can\u00e0rios, 891", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05487-020", + "ShipCountry": "Brazil" + }, { + "OrderID": 10708, + "CustomerID": "THEBI", + "EmployeeID": 6, + "OrderDate": "1997-10-17T00:00:00", + "RequiredDate": "1997-11-28T00:00:00", + "ShippedDate": "1997-11-05T00:00:00", + "ShipVia": 2, + "Freight": "2.9600", + "ShipName": "The Big Cheese", + "ShipAddress": "89 Jefferson Way Suite 2", + "ShipCity": "Portland", + "ShipRegion": "OR", + "ShipPostalCode": "97201", + "ShipCountry": "USA" + }, { + "OrderID": 10735, + "CustomerID": "LETSS", + "EmployeeID": 6, + "OrderDate": "1997-11-10T00:00:00", + "RequiredDate": "1997-12-08T00:00:00", + "ShippedDate": "1997-11-21T00:00:00", + "ShipVia": 2, + "Freight": "45.9700", + "ShipName": "Let's Stop N Shop", + "ShipAddress": "87 Polk St. Suite 5", + "ShipCity": "San Francisco", + "ShipRegion": "CA", + "ShipPostalCode": "94117", + "ShipCountry": "USA" + }, { + "OrderID": 10744, + "CustomerID": "VAFFE", + "EmployeeID": 6, + "OrderDate": "1997-11-17T00:00:00", + "RequiredDate": "1997-12-15T00:00:00", + "ShippedDate": "1997-11-24T00:00:00", + "ShipVia": 1, + "Freight": "69.1900", + "ShipName": "Vaffeljernet", + "ShipAddress": "Smagsloget 45", + "ShipCity": "\u00c5rhus", + "ShipRegion": null, + "ShipPostalCode": "8200", + "ShipCountry": "Denmark" + }, { + "OrderID": 10747, + "CustomerID": "PICCO", + "EmployeeID": 6, + "OrderDate": "1997-11-19T00:00:00", + "RequiredDate": "1997-12-17T00:00:00", + "ShippedDate": "1997-11-26T00:00:00", + "ShipVia": 1, + "Freight": "117.3300", + "ShipName": "Piccolo und mehr", + "ShipAddress": "Geislweg 14", + "ShipCity": "Salzburg", + "ShipRegion": null, + "ShipPostalCode": "5020", + "ShipCountry": "Austria" + }, { + "OrderID": 10754, + "CustomerID": "MAGAA", + "EmployeeID": 6, + "OrderDate": "1997-11-25T00:00:00", + "RequiredDate": "1997-12-23T00:00:00", + "ShippedDate": "1997-11-27T00:00:00", + "ShipVia": 3, + "Freight": "2.3800", + "ShipName": "Magazzini Alimentari Riuniti", + "ShipAddress": "Via Ludovico il Moro 22", + "ShipCity": "Bergamo", + "ShipRegion": null, + "ShipPostalCode": "24100", + "ShipCountry": "Italy" + }, { + "OrderID": 10757, + "CustomerID": "SAVEA", + "EmployeeID": 6, + "OrderDate": "1997-11-27T00:00:00", + "RequiredDate": "1997-12-25T00:00:00", + "ShippedDate": "1997-12-15T00:00:00", + "ShipVia": 1, + "Freight": "8.1900", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10764, + "CustomerID": "ERNSH", + "EmployeeID": 6, + "OrderDate": "1997-12-03T00:00:00", + "RequiredDate": "1997-12-31T00:00:00", + "ShippedDate": "1997-12-08T00:00:00", + "ShipVia": 3, + "Freight": "145.4500", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10790, + "CustomerID": "GOURL", + "EmployeeID": 6, + "OrderDate": "1997-12-22T00:00:00", + "RequiredDate": "1998-01-19T00:00:00", + "ShippedDate": "1997-12-26T00:00:00", + "ShipVia": 1, + "Freight": "28.2300", + "ShipName": "Gourmet Lanchonetes", + "ShipAddress": "Av. Brasil, 442", + "ShipCity": "Campinas", + "ShipRegion": "SP", + "ShipPostalCode": "04876-786", + "ShipCountry": "Brazil" + }, { + "OrderID": 10791, + "CustomerID": "FRANK", + "EmployeeID": 6, + "OrderDate": "1997-12-23T00:00:00", + "RequiredDate": "1998-01-20T00:00:00", + "ShippedDate": "1998-01-01T00:00:00", + "ShipVia": 2, + "Freight": "16.8500", + "ShipName": "Frankenversand", + "ShipAddress": "Berliner Platz 43", + "ShipCity": "M\u00fcnchen", + "ShipRegion": null, + "ShipPostalCode": "80805", + "ShipCountry": "Germany" + }, { + "OrderID": 10794, + "CustomerID": "QUEDE", + "EmployeeID": 6, + "OrderDate": "1997-12-24T00:00:00", + "RequiredDate": "1998-01-21T00:00:00", + "ShippedDate": "1998-01-02T00:00:00", + "ShipVia": 1, + "Freight": "21.4900", + "ShipName": "Que Del\u00edcia", + "ShipAddress": "Rua da Panificadora, 12", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "02389-673", + "ShipCountry": "Brazil" + }, { + "OrderID": 10804, + "CustomerID": "SEVES", + "EmployeeID": 6, + "OrderDate": "1997-12-30T00:00:00", + "RequiredDate": "1998-01-27T00:00:00", + "ShippedDate": "1998-01-07T00:00:00", + "ShipVia": 2, + "Freight": "27.3300", + "ShipName": "Seven Seas Imports", + "ShipAddress": "90 Wadhurst Rd.", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "OX15 4NB", + "ShipCountry": "UK" + }, { + "OrderID": 10822, + "CustomerID": "TRAIH", + "EmployeeID": 6, + "OrderDate": "1998-01-08T00:00:00", + "RequiredDate": "1998-02-05T00:00:00", + "ShippedDate": "1998-01-16T00:00:00", + "ShipVia": 3, + "Freight": "7.0000", + "ShipName": "Trail's Head Gourmet Provisioners", + "ShipAddress": "722 DaVinci Blvd.", + "ShipCity": "Kirkland", + "ShipRegion": "WA", + "ShipPostalCode": "98034", + "ShipCountry": "USA" + }, { + "OrderID": 10826, + "CustomerID": "BLONP", + "EmployeeID": 6, + "OrderDate": "1998-01-12T00:00:00", + "RequiredDate": "1998-02-09T00:00:00", + "ShippedDate": "1998-02-06T00:00:00", + "ShipVia": 1, + "Freight": "7.0900", + "ShipName": "Blondel p\u00e8re et fils", + "ShipAddress": "24, place Kl\u00e9ber", + "ShipCity": "Strasbourg", + "ShipRegion": null, + "ShipPostalCode": "67000", + "ShipCountry": "France" + }, { + "OrderID": 10833, + "CustomerID": "OTTIK", + "EmployeeID": 6, + "OrderDate": "1998-01-15T00:00:00", + "RequiredDate": "1998-02-12T00:00:00", + "ShippedDate": "1998-01-23T00:00:00", + "ShipVia": 2, + "Freight": "71.4900", + "ShipName": "Ottilies K\u00e4seladen", + "ShipAddress": "Mehrheimerstr. 369", + "ShipCity": "K\u00f6ln", + "ShipRegion": null, + "ShipPostalCode": "50739", + "ShipCountry": "Germany" + }, { + "OrderID": 10867, + "CustomerID": "LONEP", + "EmployeeID": 6, + "OrderDate": "1998-02-03T00:00:00", + "RequiredDate": "1998-03-17T00:00:00", + "ShippedDate": "1998-02-11T00:00:00", + "ShipVia": 1, + "Freight": "1.9300", + "ShipName": "Lonesome Pine Restaurant", + "ShipAddress": "89 Chiaroscuro Rd.", + "ShipCity": "Portland", + "ShipRegion": "OR", + "ShipPostalCode": "97219", + "ShipCountry": "USA" + }, { + "OrderID": 10885, + "CustomerID": "SUPRD", + "EmployeeID": 6, + "OrderDate": "1998-02-12T00:00:00", + "RequiredDate": "1998-03-12T00:00:00", + "ShippedDate": "1998-02-18T00:00:00", + "ShipVia": 3, + "Freight": "5.6400", + "ShipName": "Supr\u00eames d\u00e9lices", + "ShipAddress": "Boulevard Tirou, 255", + "ShipCity": "Charleroi", + "ShipRegion": null, + "ShipPostalCode": "B-6000", + "ShipCountry": "Belgium" + }, { + "OrderID": 10907, + "CustomerID": "SPECD", + "EmployeeID": 6, + "OrderDate": "1998-02-25T00:00:00", + "RequiredDate": "1998-03-25T00:00:00", + "ShippedDate": "1998-02-27T00:00:00", + "ShipVia": 3, + "Freight": "9.1900", + "ShipName": "Sp\u00e9cialit\u00e9s du monde", + "ShipAddress": "25, rue Lauriston", + "ShipCity": "Paris", + "ShipRegion": null, + "ShipPostalCode": "75016", + "ShipCountry": "France" + }, { + "OrderID": 10914, + "CustomerID": "QUEEN", + "EmployeeID": 6, + "OrderDate": "1998-02-27T00:00:00", + "RequiredDate": "1998-03-27T00:00:00", + "ShippedDate": "1998-03-02T00:00:00", + "ShipVia": 1, + "Freight": "21.1900", + "ShipName": "Queen Cozinha", + "ShipAddress": "Alameda dos Can\u00e0rios, 891", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05487-020", + "ShipCountry": "Brazil" + }, { + "OrderID": 10929, + "CustomerID": "FRANK", + "EmployeeID": 6, + "OrderDate": "1998-03-05T00:00:00", + "RequiredDate": "1998-04-02T00:00:00", + "ShippedDate": "1998-03-12T00:00:00", + "ShipVia": 1, + "Freight": "33.9300", + "ShipName": "Frankenversand", + "ShipAddress": "Berliner Platz 43", + "ShipCity": "M\u00fcnchen", + "ShipRegion": null, + "ShipPostalCode": "80805", + "ShipCountry": "Germany" + }, { + "OrderID": 10933, + "CustomerID": "ISLAT", + "EmployeeID": 6, + "OrderDate": "1998-03-06T00:00:00", + "RequiredDate": "1998-04-03T00:00:00", + "ShippedDate": "1998-03-16T00:00:00", + "ShipVia": 3, + "Freight": "54.1500", + "ShipName": "Island Trading", + "ShipAddress": "Garden House Crowther Way", + "ShipCity": "Cowes", + "ShipRegion": "Isle of Wight", + "ShipPostalCode": "PO31 7PJ", + "ShipCountry": "UK" + }, { + "OrderID": 10944, + "CustomerID": "BOTTM", + "EmployeeID": 6, + "OrderDate": "1998-03-12T00:00:00", + "RequiredDate": "1998-03-26T00:00:00", + "ShippedDate": "1998-03-13T00:00:00", + "ShipVia": 3, + "Freight": "52.9200", + "ShipName": "Bottom-Dollar Markets", + "ShipAddress": "23 Tsawassen Blvd.", + "ShipCity": "Tsawassen", + "ShipRegion": "BC", + "ShipPostalCode": "T2F 8M4", + "ShipCountry": "Canada" + }, { + "OrderID": 10956, + "CustomerID": "BLAUS", + "EmployeeID": 6, + "OrderDate": "1998-03-17T00:00:00", + "RequiredDate": "1998-04-28T00:00:00", + "ShippedDate": "1998-03-20T00:00:00", + "ShipVia": 2, + "Freight": "44.6500", + "ShipName": "Blauer See Delikatessen", + "ShipAddress": "Forsterstr. 57", + "ShipCity": "Mannheim", + "ShipRegion": null, + "ShipPostalCode": "68306", + "ShipCountry": "Germany" + }, { + "OrderID": 10959, + "CustomerID": "GOURL", + "EmployeeID": 6, + "OrderDate": "1998-03-18T00:00:00", + "RequiredDate": "1998-04-29T00:00:00", + "ShippedDate": "1998-03-23T00:00:00", + "ShipVia": 2, + "Freight": "4.9800", + "ShipName": "Gourmet Lanchonetes", + "ShipAddress": "Av. Brasil, 442", + "ShipCity": "Campinas", + "ShipRegion": "SP", + "ShipPostalCode": "04876-786", + "ShipCountry": "Brazil" + }, { + "OrderID": 10965, + "CustomerID": "OLDWO", + "EmployeeID": 6, + "OrderDate": "1998-03-20T00:00:00", + "RequiredDate": "1998-04-17T00:00:00", + "ShippedDate": "1998-03-30T00:00:00", + "ShipVia": 3, + "Freight": "144.3800", + "ShipName": "Old World Delicatessen", + "ShipAddress": "2743 Bering St.", + "ShipCity": "Anchorage", + "ShipRegion": "AK", + "ShipPostalCode": "99508", + "ShipCountry": "USA" + }, { + "OrderID": 10973, + "CustomerID": "LACOR", + "EmployeeID": 6, + "OrderDate": "1998-03-24T00:00:00", + "RequiredDate": "1998-04-21T00:00:00", + "ShippedDate": "1998-03-27T00:00:00", + "ShipVia": 2, + "Freight": "15.1700", + "ShipName": "La corne d'abondance", + "ShipAddress": "67, avenue de l'Europe", + "ShipCity": "Versailles", + "ShipRegion": null, + "ShipPostalCode": "78000", + "ShipCountry": "France" + }, { + "OrderID": 10999, + "CustomerID": "OTTIK", + "EmployeeID": 6, + "OrderDate": "1998-04-03T00:00:00", + "RequiredDate": "1998-05-01T00:00:00", + "ShippedDate": "1998-04-10T00:00:00", + "ShipVia": 2, + "Freight": "96.3500", + "ShipName": "Ottilies K\u00e4seladen", + "ShipAddress": "Mehrheimerstr. 369", + "ShipCity": "K\u00f6ln", + "ShipRegion": null, + "ShipPostalCode": "50739", + "ShipCountry": "Germany" + }, { + "OrderID": 11019, + "CustomerID": "RANCH", + "EmployeeID": 6, + "OrderDate": "1998-04-13T00:00:00", + "RequiredDate": "1998-05-11T00:00:00", + "ShippedDate": null, + "ShipVia": 3, + "Freight": "3.1700", + "ShipName": "Rancho grande", + "ShipAddress": "Av. del Libertador 900", + "ShipCity": "Buenos Aires", + "ShipRegion": null, + "ShipPostalCode": "1010", + "ShipCountry": "Argentina" + }, { + "OrderID": 11025, + "CustomerID": "WARTH", + "EmployeeID": 6, + "OrderDate": "1998-04-15T00:00:00", + "RequiredDate": "1998-05-13T00:00:00", + "ShippedDate": "1998-04-24T00:00:00", + "ShipVia": 3, + "Freight": "29.1700", + "ShipName": "Wartian Herkku", + "ShipAddress": "Torikatu 38", + "ShipCity": "Oulu", + "ShipRegion": null, + "ShipPostalCode": "90110", + "ShipCountry": "Finland" + }, { + "OrderID": 11031, + "CustomerID": "SAVEA", + "EmployeeID": 6, + "OrderDate": "1998-04-17T00:00:00", + "RequiredDate": "1998-05-15T00:00:00", + "ShippedDate": "1998-04-24T00:00:00", + "ShipVia": 2, + "Freight": "227.2200", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 11045, + "CustomerID": "BOTTM", + "EmployeeID": 6, + "OrderDate": "1998-04-23T00:00:00", + "RequiredDate": "1998-05-21T00:00:00", + "ShippedDate": null, + "ShipVia": 2, + "Freight": "70.5800", + "ShipName": "Bottom-Dollar Markets", + "ShipAddress": "23 Tsawassen Blvd.", + "ShipCity": "Tsawassen", + "ShipRegion": "BC", + "ShipPostalCode": "T2F 8M4", + "ShipCountry": "Canada" + }], + "EmployeeID": 6, + "LastName": "Suyama", + "FirstName": "Michael", + "Title": "Sales Representative", + "TitleOfCourtesy": "Mr.", + "BirthDate": "1963-07-02T00:00:00", + "HireDate": "1993-10-17T00:00:00", + "Address": "Coventry House\r\nMiner Rd.", + "City": "London", + "Region": null, + "PostalCode": "EC2 7JR", + "Country": "UK", + "HomePhone": "(71) 555-7773", + "Extension": "428", + "Notes": "Michael is a graduate of Sussex University (MA, economics, 1983) and the University of California at Los Angeles (MBA, marketing, 1986). He has also taken the courses \"Multi-Cultural Selling\" and \"Time Management for the Sales Professional.\" He is fluent in Japanese and can read and write French, Portuguese, and Spanish.", + "ReportsTo": 5, + "PhotoPath": "http://accweb/emmployees/davolio.bmp" + }, { + "Orders": [{ + "OrderID": 10289, + "CustomerID": "BSBEV", + "EmployeeID": 7, + "OrderDate": "1996-08-26T00:00:00", + "RequiredDate": "1996-09-23T00:00:00", + "ShippedDate": "1996-08-28T00:00:00", + "ShipVia": 3, + "Freight": "22.7700", + "ShipName": "B's Beverages", + "ShipAddress": "Fauntleroy Circus", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "EC2 5NT", + "ShipCountry": "UK" + }, { + "OrderID": 10303, + "CustomerID": "GODOS", + "EmployeeID": 7, + "OrderDate": "1996-09-11T00:00:00", + "RequiredDate": "1996-10-09T00:00:00", + "ShippedDate": "1996-09-18T00:00:00", + "ShipVia": 2, + "Freight": "107.8300", + "ShipName": "Godos Cocina T\u00edpica", + "ShipAddress": "C/ Romero, 33", + "ShipCity": "Sevilla", + "ShipRegion": null, + "ShipPostalCode": "41101", + "ShipCountry": "Spain" + }, { + "OrderID": 10308, + "CustomerID": "ANATR", + "EmployeeID": 7, + "OrderDate": "1996-09-18T00:00:00", + "RequiredDate": "1996-10-16T00:00:00", + "ShippedDate": "1996-09-24T00:00:00", + "ShipVia": 3, + "Freight": "1.6100", + "ShipName": "Ana Trujillo Emparedados y helados", + "ShipAddress": "Avda. de la Constituci\u00f3n 2222", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05021", + "ShipCountry": "Mexico" + }, { + "OrderID": 10319, + "CustomerID": "TORTU", + "EmployeeID": 7, + "OrderDate": "1996-10-02T00:00:00", + "RequiredDate": "1996-10-30T00:00:00", + "ShippedDate": "1996-10-11T00:00:00", + "ShipVia": 3, + "Freight": "64.5000", + "ShipName": "Tortuga Restaurante", + "ShipAddress": "Avda. Azteca 123", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05033", + "ShipCountry": "Mexico" + }, { + "OrderID": 10322, + "CustomerID": "PERIC", + "EmployeeID": 7, + "OrderDate": "1996-10-04T00:00:00", + "RequiredDate": "1996-11-01T00:00:00", + "ShippedDate": "1996-10-23T00:00:00", + "ShipVia": 3, + "Freight": "0.4000", + "ShipName": "Pericles Comidas cl\u00e1sicas", + "ShipAddress": "Calle Dr. Jorge Cash 321", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05033", + "ShipCountry": "Mexico" + }, { + "OrderID": 10335, + "CustomerID": "HUNGO", + "EmployeeID": 7, + "OrderDate": "1996-10-22T00:00:00", + "RequiredDate": "1996-11-19T00:00:00", + "ShippedDate": "1996-10-24T00:00:00", + "ShipVia": 2, + "Freight": "42.1100", + "ShipName": "Hungry Owl All-Night Grocers", + "ShipAddress": "8 Johnstown Road", + "ShipCity": "Cork", + "ShipRegion": "Co. Cork", + "ShipPostalCode": null, + "ShipCountry": "Ireland" + }, { + "OrderID": 10336, + "CustomerID": "PRINI", + "EmployeeID": 7, + "OrderDate": "1996-10-23T00:00:00", + "RequiredDate": "1996-11-20T00:00:00", + "ShippedDate": "1996-10-25T00:00:00", + "ShipVia": 2, + "Freight": "15.5100", + "ShipName": "Princesa Isabel Vinhos", + "ShipAddress": "Estrada da sa\u00fade n. 58", + "ShipCity": "Lisboa", + "ShipRegion": null, + "ShipPostalCode": "1756", + "ShipCountry": "Portugal" + }, { + "OrderID": 10341, + "CustomerID": "SIMOB", + "EmployeeID": 7, + "OrderDate": "1996-10-29T00:00:00", + "RequiredDate": "1996-11-26T00:00:00", + "ShippedDate": "1996-11-05T00:00:00", + "ShipVia": 3, + "Freight": "26.7800", + "ShipName": "Simons bistro", + "ShipAddress": "Vinb\u00e6ltet 34", + "ShipCity": "Kobenhavn", + "ShipRegion": null, + "ShipPostalCode": "1734", + "ShipCountry": "Denmark" + }, { + "OrderID": 10349, + "CustomerID": "SPLIR", + "EmployeeID": 7, + "OrderDate": "1996-11-08T00:00:00", + "RequiredDate": "1996-12-06T00:00:00", + "ShippedDate": "1996-11-15T00:00:00", + "ShipVia": 1, + "Freight": "8.6300", + "ShipName": "Split Rail Beer & Ale", + "ShipAddress": "P.O. Box 555", + "ShipCity": "Lander", + "ShipRegion": "WY", + "ShipPostalCode": "82520", + "ShipCountry": "USA" + }, { + "OrderID": 10353, + "CustomerID": "PICCO", + "EmployeeID": 7, + "OrderDate": "1996-11-13T00:00:00", + "RequiredDate": "1996-12-11T00:00:00", + "ShippedDate": "1996-11-25T00:00:00", + "ShipVia": 3, + "Freight": "360.6300", + "ShipName": "Piccolo und mehr", + "ShipAddress": "Geislweg 14", + "ShipCity": "Salzburg", + "ShipRegion": null, + "ShipPostalCode": "5020", + "ShipCountry": "Austria" + }, { + "OrderID": 10367, + "CustomerID": "VAFFE", + "EmployeeID": 7, + "OrderDate": "1996-11-28T00:00:00", + "RequiredDate": "1996-12-26T00:00:00", + "ShippedDate": "1996-12-02T00:00:00", + "ShipVia": 3, + "Freight": "13.5500", + "ShipName": "Vaffeljernet", + "ShipAddress": "Smagsloget 45", + "ShipCity": "\u00c5rhus", + "ShipRegion": null, + "ShipPostalCode": "8200", + "ShipCountry": "Denmark" + }, { + "OrderID": 10406, + "CustomerID": "QUEEN", + "EmployeeID": 7, + "OrderDate": "1997-01-07T00:00:00", + "RequiredDate": "1997-02-18T00:00:00", + "ShippedDate": "1997-01-13T00:00:00", + "ShipVia": 1, + "Freight": "108.0400", + "ShipName": "Queen Cozinha", + "ShipAddress": "Alameda dos Can\u00e0rios, 891", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05487-020", + "ShipCountry": "Brazil" + }, { + "OrderID": 10424, + "CustomerID": "MEREP", + "EmployeeID": 7, + "OrderDate": "1997-01-23T00:00:00", + "RequiredDate": "1997-02-20T00:00:00", + "ShippedDate": "1997-01-27T00:00:00", + "ShipVia": 2, + "Freight": "370.6100", + "ShipName": "M\u00e8re Paillarde", + "ShipAddress": "43 rue St. Laurent", + "ShipCity": "Montr\u00e9al", + "ShipRegion": "Qu\u00e9bec", + "ShipPostalCode": "H1J 1C3", + "ShipCountry": "Canada" + }, { + "OrderID": 10428, + "CustomerID": "REGGC", + "EmployeeID": 7, + "OrderDate": "1997-01-28T00:00:00", + "RequiredDate": "1997-02-25T00:00:00", + "ShippedDate": "1997-02-04T00:00:00", + "ShipVia": 1, + "Freight": "11.0900", + "ShipName": "Reggiani Caseifici", + "ShipAddress": "Strada Provinciale 124", + "ShipCity": "Reggio Emilia", + "ShipRegion": null, + "ShipPostalCode": "42100", + "ShipCountry": "Italy" + }, { + "OrderID": 10458, + "CustomerID": "SUPRD", + "EmployeeID": 7, + "OrderDate": "1997-02-26T00:00:00", + "RequiredDate": "1997-03-26T00:00:00", + "ShippedDate": "1997-03-04T00:00:00", + "ShipVia": 3, + "Freight": "147.0600", + "ShipName": "Supr\u00eames d\u00e9lices", + "ShipAddress": "Boulevard Tirou, 255", + "ShipCity": "Charleroi", + "ShipRegion": null, + "ShipPostalCode": "B-6000", + "ShipCountry": "Belgium" + }, { + "OrderID": 10483, + "CustomerID": "WHITC", + "EmployeeID": 7, + "OrderDate": "1997-03-24T00:00:00", + "RequiredDate": "1997-04-21T00:00:00", + "ShippedDate": "1997-04-25T00:00:00", + "ShipVia": 2, + "Freight": "15.2800", + "ShipName": "White Clover Markets", + "ShipAddress": "1029 - 12th Ave. S.", + "ShipCity": "Seattle", + "ShipRegion": "WA", + "ShipPostalCode": "98124", + "ShipCountry": "USA" + }, { + "OrderID": 10490, + "CustomerID": "HILAA", + "EmployeeID": 7, + "OrderDate": "1997-03-31T00:00:00", + "RequiredDate": "1997-04-28T00:00:00", + "ShippedDate": "1997-04-03T00:00:00", + "ShipVia": 2, + "Freight": "210.1900", + "ShipName": "HILARION-Abastos", + "ShipAddress": "Carrera 22 con Ave. Carlos Soublette #8-35", + "ShipCity": "San Crist\u00f3bal", + "ShipRegion": "T\u00e1chira", + "ShipPostalCode": "5022", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10496, + "CustomerID": "TRADH", + "EmployeeID": 7, + "OrderDate": "1997-04-04T00:00:00", + "RequiredDate": "1997-05-02T00:00:00", + "ShippedDate": "1997-04-07T00:00:00", + "ShipVia": 2, + "Freight": "46.7700", + "ShipName": "Tradi\u00e7ao Hipermercados", + "ShipAddress": "Av. In\u00eas de Castro, 414", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05634-030", + "ShipCountry": "Brazil" + }, { + "OrderID": 10497, + "CustomerID": "LEHMS", + "EmployeeID": 7, + "OrderDate": "1997-04-04T00:00:00", + "RequiredDate": "1997-05-02T00:00:00", + "ShippedDate": "1997-04-07T00:00:00", + "ShipVia": 1, + "Freight": "36.2100", + "ShipName": "Lehmanns Marktstand", + "ShipAddress": "Magazinweg 7", + "ShipCity": "Frankfurt a.M.", + "ShipRegion": null, + "ShipPostalCode": "60528", + "ShipCountry": "Germany" + }, { + "OrderID": 10507, + "CustomerID": "ANTON", + "EmployeeID": 7, + "OrderDate": "1997-04-15T00:00:00", + "RequiredDate": "1997-05-13T00:00:00", + "ShippedDate": "1997-04-22T00:00:00", + "ShipVia": 1, + "Freight": "47.4500", + "ShipName": "Antonio Moreno Taquer\u00eda", + "ShipAddress": "Mataderos 2312", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05023", + "ShipCountry": "Mexico" + }, { + "OrderID": 10512, + "CustomerID": "FAMIA", + "EmployeeID": 7, + "OrderDate": "1997-04-21T00:00:00", + "RequiredDate": "1997-05-19T00:00:00", + "ShippedDate": "1997-04-24T00:00:00", + "ShipVia": 2, + "Freight": "3.5300", + "ShipName": "Familia Arquibaldo", + "ShipAddress": "Rua Or\u00f3s, 92", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05442-030", + "ShipCountry": "Brazil" + }, { + "OrderID": 10513, + "CustomerID": "WANDK", + "EmployeeID": 7, + "OrderDate": "1997-04-22T00:00:00", + "RequiredDate": "1997-06-03T00:00:00", + "ShippedDate": "1997-04-28T00:00:00", + "ShipVia": 1, + "Freight": "105.6500", + "ShipName": "Die Wandernde Kuh", + "ShipAddress": "Adenauerallee 900", + "ShipCity": "Stuttgart", + "ShipRegion": null, + "ShipPostalCode": "70563", + "ShipCountry": "Germany" + }, { + "OrderID": 10520, + "CustomerID": "SANTG", + "EmployeeID": 7, + "OrderDate": "1997-04-29T00:00:00", + "RequiredDate": "1997-05-27T00:00:00", + "ShippedDate": "1997-05-01T00:00:00", + "ShipVia": 1, + "Freight": "13.3700", + "ShipName": "Sant\u00e9 Gourmet", + "ShipAddress": "Erling Skakkes gate 78", + "ShipCity": "Stavern", + "ShipRegion": null, + "ShipPostalCode": "4110", + "ShipCountry": "Norway" + }, { + "OrderID": 10523, + "CustomerID": "SEVES", + "EmployeeID": 7, + "OrderDate": "1997-05-01T00:00:00", + "RequiredDate": "1997-05-29T00:00:00", + "ShippedDate": "1997-05-30T00:00:00", + "ShipVia": 2, + "Freight": "77.6300", + "ShipName": "Seven Seas Imports", + "ShipAddress": "90 Wadhurst Rd.", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "OX15 4NB", + "ShipCountry": "UK" + }, { + "OrderID": 10527, + "CustomerID": "QUICK", + "EmployeeID": 7, + "OrderDate": "1997-05-05T00:00:00", + "RequiredDate": "1997-06-02T00:00:00", + "ShippedDate": "1997-05-07T00:00:00", + "ShipVia": 1, + "Freight": "41.9000", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10531, + "CustomerID": "OCEAN", + "EmployeeID": 7, + "OrderDate": "1997-05-08T00:00:00", + "RequiredDate": "1997-06-05T00:00:00", + "ShippedDate": "1997-05-19T00:00:00", + "ShipVia": 1, + "Freight": "8.1200", + "ShipName": "Oc\u00e9ano Atl\u00e1ntico Ltda.", + "ShipAddress": "Ing. Gustavo Moncada 8585 Piso 20-A", + "ShipCity": "Buenos Aires", + "ShipRegion": null, + "ShipPostalCode": "1010", + "ShipCountry": "Argentina" + }, { + "OrderID": 10532, + "CustomerID": "EASTC", + "EmployeeID": 7, + "OrderDate": "1997-05-09T00:00:00", + "RequiredDate": "1997-06-06T00:00:00", + "ShippedDate": "1997-05-12T00:00:00", + "ShipVia": 3, + "Freight": "74.4600", + "ShipName": "Eastern Connection", + "ShipAddress": "35 King George", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "WX3 6FW", + "ShipCountry": "UK" + }, { + "OrderID": 10550, + "CustomerID": "GODOS", + "EmployeeID": 7, + "OrderDate": "1997-05-28T00:00:00", + "RequiredDate": "1997-06-25T00:00:00", + "ShippedDate": "1997-06-06T00:00:00", + "ShipVia": 3, + "Freight": "4.3200", + "ShipName": "Godos Cocina T\u00edpica", + "ShipAddress": "C/ Romero, 33", + "ShipCity": "Sevilla", + "ShipRegion": null, + "ShipPostalCode": "41101", + "ShipCountry": "Spain" + }, { + "OrderID": 10573, + "CustomerID": "ANTON", + "EmployeeID": 7, + "OrderDate": "1997-06-19T00:00:00", + "RequiredDate": "1997-07-17T00:00:00", + "ShippedDate": "1997-06-20T00:00:00", + "ShipVia": 3, + "Freight": "84.8400", + "ShipName": "Antonio Moreno Taquer\u00eda", + "ShipAddress": "Mataderos 2312", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05023", + "ShipCountry": "Mexico" + }, { + "OrderID": 10585, + "CustomerID": "WELLI", + "EmployeeID": 7, + "OrderDate": "1997-07-01T00:00:00", + "RequiredDate": "1997-07-29T00:00:00", + "ShippedDate": "1997-07-10T00:00:00", + "ShipVia": 1, + "Freight": "13.4100", + "ShipName": "Wellington Importadora", + "ShipAddress": "Rua do Mercado, 12", + "ShipCity": "Resende", + "ShipRegion": "SP", + "ShipPostalCode": "08737-363", + "ShipCountry": "Brazil" + }, { + "OrderID": 10593, + "CustomerID": "LEHMS", + "EmployeeID": 7, + "OrderDate": "1997-07-09T00:00:00", + "RequiredDate": "1997-08-06T00:00:00", + "ShippedDate": "1997-08-13T00:00:00", + "ShipVia": 2, + "Freight": "174.2000", + "ShipName": "Lehmanns Marktstand", + "ShipAddress": "Magazinweg 7", + "ShipCity": "Frankfurt a.M.", + "ShipRegion": null, + "ShipPostalCode": "60528", + "ShipCountry": "Germany" + }, { + "OrderID": 10597, + "CustomerID": "PICCO", + "EmployeeID": 7, + "OrderDate": "1997-07-11T00:00:00", + "RequiredDate": "1997-08-08T00:00:00", + "ShippedDate": "1997-07-18T00:00:00", + "ShipVia": 3, + "Freight": "35.1200", + "ShipName": "Piccolo und mehr", + "ShipAddress": "Geislweg 14", + "ShipCity": "Salzburg", + "ShipRegion": null, + "ShipPostalCode": "5020", + "ShipCountry": "Austria" + }, { + "OrderID": 10601, + "CustomerID": "HILAA", + "EmployeeID": 7, + "OrderDate": "1997-07-16T00:00:00", + "RequiredDate": "1997-08-27T00:00:00", + "ShippedDate": "1997-07-22T00:00:00", + "ShipVia": 1, + "Freight": "58.3000", + "ShipName": "HILARION-Abastos", + "ShipAddress": "Carrera 22 con Ave. Carlos Soublette #8-35", + "ShipCity": "San Crist\u00f3bal", + "ShipRegion": "T\u00e1chira", + "ShipPostalCode": "5022", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10609, + "CustomerID": "DUMON", + "EmployeeID": 7, + "OrderDate": "1997-07-24T00:00:00", + "RequiredDate": "1997-08-21T00:00:00", + "ShippedDate": "1997-07-30T00:00:00", + "ShipVia": 2, + "Freight": "1.8500", + "ShipName": "Du monde entier", + "ShipAddress": "67, rue des Cinquante Otages", + "ShipCity": "Nantes", + "ShipRegion": null, + "ShipPostalCode": "44000", + "ShipCountry": "France" + }, { + "OrderID": 10633, + "CustomerID": "ERNSH", + "EmployeeID": 7, + "OrderDate": "1997-08-15T00:00:00", + "RequiredDate": "1997-09-12T00:00:00", + "ShippedDate": "1997-08-18T00:00:00", + "ShipVia": 3, + "Freight": "477.9000", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10639, + "CustomerID": "SANTG", + "EmployeeID": 7, + "OrderDate": "1997-08-20T00:00:00", + "RequiredDate": "1997-09-17T00:00:00", + "ShippedDate": "1997-08-27T00:00:00", + "ShipVia": 3, + "Freight": "38.6400", + "ShipName": "Sant\u00e9 Gourmet", + "ShipAddress": "Erling Skakkes gate 78", + "ShipCity": "Stavern", + "ShipRegion": null, + "ShipPostalCode": "4110", + "ShipCountry": "Norway" + }, { + "OrderID": 10642, + "CustomerID": "SIMOB", + "EmployeeID": 7, + "OrderDate": "1997-08-22T00:00:00", + "RequiredDate": "1997-09-19T00:00:00", + "ShippedDate": "1997-09-05T00:00:00", + "ShipVia": 3, + "Freight": "41.8900", + "ShipName": "Simons bistro", + "ShipAddress": "Vinb\u00e6ltet 34", + "ShipCity": "Kobenhavn", + "ShipRegion": null, + "ShipPostalCode": "1734", + "ShipCountry": "Denmark" + }, { + "OrderID": 10659, + "CustomerID": "QUEEN", + "EmployeeID": 7, + "OrderDate": "1997-09-05T00:00:00", + "RequiredDate": "1997-10-03T00:00:00", + "ShippedDate": "1997-09-10T00:00:00", + "ShipVia": 2, + "Freight": "105.8100", + "ShipName": "Queen Cozinha", + "ShipAddress": "Alameda dos Can\u00e0rios, 891", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05487-020", + "ShipCountry": "Brazil" + }, { + "OrderID": 10661, + "CustomerID": "HUNGO", + "EmployeeID": 7, + "OrderDate": "1997-09-09T00:00:00", + "RequiredDate": "1997-10-07T00:00:00", + "ShippedDate": "1997-09-15T00:00:00", + "ShipVia": 3, + "Freight": "17.5500", + "ShipName": "Hungry Owl All-Night Grocers", + "ShipAddress": "8 Johnstown Road", + "ShipCity": "Cork", + "ShipRegion": "Co. Cork", + "ShipPostalCode": null, + "ShipCountry": "Ireland" + }, { + "OrderID": 10666, + "CustomerID": "RICSU", + "EmployeeID": 7, + "OrderDate": "1997-09-12T00:00:00", + "RequiredDate": "1997-10-10T00:00:00", + "ShippedDate": "1997-09-22T00:00:00", + "ShipVia": 2, + "Freight": "232.4200", + "ShipName": "Richter Supermarkt", + "ShipAddress": "Starenweg 5", + "ShipCity": "Gen\u00e8ve", + "ShipRegion": null, + "ShipPostalCode": "1204", + "ShipCountry": "Switzerland" + }, { + "OrderID": 10667, + "CustomerID": "ERNSH", + "EmployeeID": 7, + "OrderDate": "1997-09-12T00:00:00", + "RequiredDate": "1997-10-10T00:00:00", + "ShippedDate": "1997-09-19T00:00:00", + "ShipVia": 1, + "Freight": "78.0900", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10678, + "CustomerID": "SAVEA", + "EmployeeID": 7, + "OrderDate": "1997-09-23T00:00:00", + "RequiredDate": "1997-10-21T00:00:00", + "ShippedDate": "1997-10-16T00:00:00", + "ShipVia": 3, + "Freight": "388.9800", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10695, + "CustomerID": "WILMK", + "EmployeeID": 7, + "OrderDate": "1997-10-07T00:00:00", + "RequiredDate": "1997-11-18T00:00:00", + "ShippedDate": "1997-10-14T00:00:00", + "ShipVia": 1, + "Freight": "16.7200", + "ShipName": "Wilman Kala", + "ShipAddress": "Keskuskatu 45", + "ShipCity": "Helsinki", + "ShipRegion": null, + "ShipPostalCode": "21240", + "ShipCountry": "Finland" + }, { + "OrderID": 10731, + "CustomerID": "CHOPS", + "EmployeeID": 7, + "OrderDate": "1997-11-06T00:00:00", + "RequiredDate": "1997-12-04T00:00:00", + "ShippedDate": "1997-11-14T00:00:00", + "ShipVia": 1, + "Freight": "96.6500", + "ShipName": "Chop-suey Chinese", + "ShipAddress": "Hauptstr. 31", + "ShipCity": "Bern", + "ShipRegion": null, + "ShipPostalCode": "3012", + "ShipCountry": "Switzerland" + }, { + "OrderID": 10775, + "CustomerID": "THECR", + "EmployeeID": 7, + "OrderDate": "1997-12-12T00:00:00", + "RequiredDate": "1998-01-09T00:00:00", + "ShippedDate": "1997-12-26T00:00:00", + "ShipVia": 1, + "Freight": "20.2500", + "ShipName": "The Cracker Box", + "ShipAddress": "55 Grizzly Peak Rd.", + "ShipCity": "Butte", + "ShipRegion": "MT", + "ShipPostalCode": "59801", + "ShipCountry": "USA" + }, { + "OrderID": 10777, + "CustomerID": "GOURL", + "EmployeeID": 7, + "OrderDate": "1997-12-15T00:00:00", + "RequiredDate": "1997-12-29T00:00:00", + "ShippedDate": "1998-01-21T00:00:00", + "ShipVia": 2, + "Freight": "3.0100", + "ShipName": "Gourmet Lanchonetes", + "ShipAddress": "Av. Brasil, 442", + "ShipCity": "Campinas", + "ShipRegion": "SP", + "ShipPostalCode": "04876-786", + "ShipCountry": "Brazil" + }, { + "OrderID": 10797, + "CustomerID": "DRACD", + "EmployeeID": 7, + "OrderDate": "1997-12-25T00:00:00", + "RequiredDate": "1998-01-22T00:00:00", + "ShippedDate": "1998-01-05T00:00:00", + "ShipVia": 2, + "Freight": "33.3500", + "ShipName": "Drachenblut Delikatessen", + "ShipAddress": "Walserweg 21", + "ShipCity": "Aachen", + "ShipRegion": null, + "ShipPostalCode": "52066", + "ShipCountry": "Germany" + }, { + "OrderID": 10809, + "CustomerID": "WELLI", + "EmployeeID": 7, + "OrderDate": "1998-01-01T00:00:00", + "RequiredDate": "1998-01-29T00:00:00", + "ShippedDate": "1998-01-07T00:00:00", + "ShipVia": 1, + "Freight": "4.8700", + "ShipName": "Wellington Importadora", + "ShipAddress": "Rua do Mercado, 12", + "ShipCity": "Resende", + "ShipRegion": "SP", + "ShipPostalCode": "08737-363", + "ShipCountry": "Brazil" + }, { + "OrderID": 10818, + "CustomerID": "MAGAA", + "EmployeeID": 7, + "OrderDate": "1998-01-07T00:00:00", + "RequiredDate": "1998-02-04T00:00:00", + "ShippedDate": "1998-01-12T00:00:00", + "ShipVia": 3, + "Freight": "65.4800", + "ShipName": "Magazzini Alimentari Riuniti", + "ShipAddress": "Via Ludovico il Moro 22", + "ShipCity": "Bergamo", + "ShipRegion": null, + "ShipPostalCode": "24100", + "ShipCountry": "Italy" + }, { + "OrderID": 10836, + "CustomerID": "ERNSH", + "EmployeeID": 7, + "OrderDate": "1998-01-16T00:00:00", + "RequiredDate": "1998-02-13T00:00:00", + "ShippedDate": "1998-01-21T00:00:00", + "ShipVia": 1, + "Freight": "411.8800", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10848, + "CustomerID": "CONSH", + "EmployeeID": 7, + "OrderDate": "1998-01-23T00:00:00", + "RequiredDate": "1998-02-20T00:00:00", + "ShippedDate": "1998-01-29T00:00:00", + "ShipVia": 2, + "Freight": "38.2400", + "ShipName": "Consolidated Holdings", + "ShipAddress": "Berkeley Gardens 12 Brewery", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "WX1 6LT", + "ShipCountry": "UK" + }, { + "OrderID": 10868, + "CustomerID": "QUEEN", + "EmployeeID": 7, + "OrderDate": "1998-02-04T00:00:00", + "RequiredDate": "1998-03-04T00:00:00", + "ShippedDate": "1998-02-23T00:00:00", + "ShipVia": 2, + "Freight": "191.2700", + "ShipName": "Queen Cozinha", + "ShipAddress": "Alameda dos Can\u00e0rios, 891", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05487-020", + "ShipCountry": "Brazil" + }, { + "OrderID": 10876, + "CustomerID": "BONAP", + "EmployeeID": 7, + "OrderDate": "1998-02-09T00:00:00", + "RequiredDate": "1998-03-09T00:00:00", + "ShippedDate": "1998-02-12T00:00:00", + "ShipVia": 3, + "Freight": "60.4200", + "ShipName": "Bon app'", + "ShipAddress": "12, rue des Bouchers", + "ShipCity": "Marseille", + "ShipRegion": null, + "ShipPostalCode": "13008", + "ShipCountry": "France" + }, { + "OrderID": 10880, + "CustomerID": "FOLKO", + "EmployeeID": 7, + "OrderDate": "1998-02-10T00:00:00", + "RequiredDate": "1998-03-24T00:00:00", + "ShippedDate": "1998-02-18T00:00:00", + "ShipVia": 1, + "Freight": "88.0100", + "ShipName": "Folk och f\u00e4 HB", + "ShipAddress": "\u00c5kergatan 24", + "ShipCity": "Br\u00e4cke", + "ShipRegion": null, + "ShipPostalCode": "S-844 67", + "ShipCountry": "Sweden" + }, { + "OrderID": 10890, + "CustomerID": "DUMON", + "EmployeeID": 7, + "OrderDate": "1998-02-16T00:00:00", + "RequiredDate": "1998-03-16T00:00:00", + "ShippedDate": "1998-02-18T00:00:00", + "ShipVia": 1, + "Freight": "32.7600", + "ShipName": "Du monde entier", + "ShipAddress": "67, rue des Cinquante Otages", + "ShipCity": "Nantes", + "ShipRegion": null, + "ShipPostalCode": "44000", + "ShipCountry": "France" + }, { + "OrderID": 10891, + "CustomerID": "LEHMS", + "EmployeeID": 7, + "OrderDate": "1998-02-17T00:00:00", + "RequiredDate": "1998-03-17T00:00:00", + "ShippedDate": "1998-02-19T00:00:00", + "ShipVia": 2, + "Freight": "20.3700", + "ShipName": "Lehmanns Marktstand", + "ShipAddress": "Magazinweg 7", + "ShipCity": "Frankfurt a.M.", + "ShipRegion": null, + "ShipPostalCode": "60528", + "ShipCountry": "Germany" + }, { + "OrderID": 10896, + "CustomerID": "MAISD", + "EmployeeID": 7, + "OrderDate": "1998-02-19T00:00:00", + "RequiredDate": "1998-03-19T00:00:00", + "ShippedDate": "1998-02-27T00:00:00", + "ShipVia": 3, + "Freight": "32.4500", + "ShipName": "Maison Dewey", + "ShipAddress": "Rue Joseph-Bens 532", + "ShipCity": "Bruxelles", + "ShipRegion": null, + "ShipPostalCode": "B-1180", + "ShipCountry": "Belgium" + }, { + "OrderID": 10923, + "CustomerID": "LAMAI", + "EmployeeID": 7, + "OrderDate": "1998-03-03T00:00:00", + "RequiredDate": "1998-04-14T00:00:00", + "ShippedDate": "1998-03-13T00:00:00", + "ShipVia": 3, + "Freight": "68.2600", + "ShipName": "La maison d'Asie", + "ShipAddress": "1 rue Alsace-Lorraine", + "ShipCity": "Toulouse", + "ShipRegion": null, + "ShipPostalCode": "31000", + "ShipCountry": "France" + }, { + "OrderID": 10937, + "CustomerID": "CACTU", + "EmployeeID": 7, + "OrderDate": "1998-03-10T00:00:00", + "RequiredDate": "1998-03-24T00:00:00", + "ShippedDate": "1998-03-13T00:00:00", + "ShipVia": 3, + "Freight": "31.5100", + "ShipName": "Cactus Comidas para llevar", + "ShipAddress": "Cerrito 333", + "ShipCity": "Buenos Aires", + "ShipRegion": null, + "ShipPostalCode": "1010", + "ShipCountry": "Argentina" + }, { + "OrderID": 10941, + "CustomerID": "SAVEA", + "EmployeeID": 7, + "OrderDate": "1998-03-11T00:00:00", + "RequiredDate": "1998-04-08T00:00:00", + "ShippedDate": "1998-03-20T00:00:00", + "ShipVia": 2, + "Freight": "400.8100", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10958, + "CustomerID": "OCEAN", + "EmployeeID": 7, + "OrderDate": "1998-03-18T00:00:00", + "RequiredDate": "1998-04-15T00:00:00", + "ShippedDate": "1998-03-27T00:00:00", + "ShipVia": 2, + "Freight": "49.5600", + "ShipName": "Oc\u00e9ano Atl\u00e1ntico Ltda.", + "ShipAddress": "Ing. Gustavo Moncada 8585 Piso 20-A", + "ShipCity": "Buenos Aires", + "ShipRegion": null, + "ShipPostalCode": "1010", + "ShipCountry": "Argentina" + }, { + "OrderID": 10993, + "CustomerID": "FOLKO", + "EmployeeID": 7, + "OrderDate": "1998-04-01T00:00:00", + "RequiredDate": "1998-04-29T00:00:00", + "ShippedDate": "1998-04-10T00:00:00", + "ShipVia": 3, + "Freight": "8.8100", + "ShipName": "Folk och f\u00e4 HB", + "ShipAddress": "\u00c5kergatan 24", + "ShipCity": "Br\u00e4cke", + "ShipRegion": null, + "ShipPostalCode": "S-844 67", + "ShipCountry": "Sweden" + }, { + "OrderID": 11008, + "CustomerID": "ERNSH", + "EmployeeID": 7, + "OrderDate": "1998-04-08T00:00:00", + "RequiredDate": "1998-05-06T00:00:00", + "ShippedDate": null, + "ShipVia": 3, + "Freight": "79.4600", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 11030, + "CustomerID": "SAVEA", + "EmployeeID": 7, + "OrderDate": "1998-04-17T00:00:00", + "RequiredDate": "1998-05-15T00:00:00", + "ShippedDate": "1998-04-27T00:00:00", + "ShipVia": 2, + "Freight": "830.7500", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 11033, + "CustomerID": "RICSU", + "EmployeeID": 7, + "OrderDate": "1998-04-17T00:00:00", + "RequiredDate": "1998-05-15T00:00:00", + "ShippedDate": "1998-04-23T00:00:00", + "ShipVia": 3, + "Freight": "84.7400", + "ShipName": "Richter Supermarkt", + "ShipAddress": "Starenweg 5", + "ShipCity": "Gen\u00e8ve", + "ShipRegion": null, + "ShipPostalCode": "1204", + "ShipCountry": "Switzerland" + }, { + "OrderID": 11037, + "CustomerID": "GODOS", + "EmployeeID": 7, + "OrderDate": "1998-04-21T00:00:00", + "RequiredDate": "1998-05-19T00:00:00", + "ShippedDate": "1998-04-27T00:00:00", + "ShipVia": 1, + "Freight": "3.2000", + "ShipName": "Godos Cocina T\u00edpica", + "ShipAddress": "C/ Romero, 33", + "ShipCity": "Sevilla", + "ShipRegion": null, + "ShipPostalCode": "41101", + "ShipCountry": "Spain" + }, { + "OrderID": 11047, + "CustomerID": "EASTC", + "EmployeeID": 7, + "OrderDate": "1998-04-24T00:00:00", + "RequiredDate": "1998-05-22T00:00:00", + "ShippedDate": "1998-05-01T00:00:00", + "ShipVia": 3, + "Freight": "46.6200", + "ShipName": "Eastern Connection", + "ShipAddress": "35 King George", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "WX3 6FW", + "ShipCountry": "UK" + }, { + "OrderID": 11048, + "CustomerID": "BOTTM", + "EmployeeID": 7, + "OrderDate": "1998-04-24T00:00:00", + "RequiredDate": "1998-05-22T00:00:00", + "ShippedDate": "1998-04-30T00:00:00", + "ShipVia": 3, + "Freight": "24.1200", + "ShipName": "Bottom-Dollar Markets", + "ShipAddress": "23 Tsawassen Blvd.", + "ShipCity": "Tsawassen", + "ShipRegion": "BC", + "ShipPostalCode": "T2F 8M4", + "ShipCountry": "Canada" + }, { + "OrderID": 11051, + "CustomerID": "LAMAI", + "EmployeeID": 7, + "OrderDate": "1998-04-27T00:00:00", + "RequiredDate": "1998-05-25T00:00:00", + "ShippedDate": null, + "ShipVia": 3, + "Freight": "2.7900", + "ShipName": "La maison d'Asie", + "ShipAddress": "1 rue Alsace-Lorraine", + "ShipCity": "Toulouse", + "ShipRegion": null, + "ShipPostalCode": "31000", + "ShipCountry": "France" + }, { + "OrderID": 11055, + "CustomerID": "HILAA", + "EmployeeID": 7, + "OrderDate": "1998-04-28T00:00:00", + "RequiredDate": "1998-05-26T00:00:00", + "ShippedDate": "1998-05-05T00:00:00", + "ShipVia": 2, + "Freight": "120.9200", + "ShipName": "HILARION-Abastos", + "ShipAddress": "Carrera 22 con Ave. Carlos Soublette #8-35", + "ShipCity": "San Crist\u00f3bal", + "ShipRegion": "T\u00e1chira", + "ShipPostalCode": "5022", + "ShipCountry": "Venezuela" + }, { + "OrderID": 11066, + "CustomerID": "WHITC", + "EmployeeID": 7, + "OrderDate": "1998-05-01T00:00:00", + "RequiredDate": "1998-05-29T00:00:00", + "ShippedDate": "1998-05-04T00:00:00", + "ShipVia": 2, + "Freight": "44.7200", + "ShipName": "White Clover Markets", + "ShipAddress": "1029 - 12th Ave. S.", + "ShipCity": "Seattle", + "ShipRegion": "WA", + "ShipPostalCode": "98124", + "ShipCountry": "USA" + }, { + "OrderID": 11074, + "CustomerID": "SIMOB", + "EmployeeID": 7, + "OrderDate": "1998-05-06T00:00:00", + "RequiredDate": "1998-06-03T00:00:00", + "ShippedDate": null, + "ShipVia": 2, + "Freight": "18.4400", + "ShipName": "Simons bistro", + "ShipAddress": "Vinb\u00e6ltet 34", + "ShipCity": "Kobenhavn", + "ShipRegion": null, + "ShipPostalCode": "1734", + "ShipCountry": "Denmark" + }], + "EmployeeID": 7, + "LastName": "King", + "FirstName": "Robert", + "Title": "Sales Representative", + "TitleOfCourtesy": "Mr.", + "BirthDate": "1960-05-29T00:00:00", + "HireDate": "1994-01-02T00:00:00", + "Address": "Edgeham Hollow\r\nWinchester Way", + "City": "London", + "Region": null, + "PostalCode": "RG1 9SP", + "Country": "UK", + "HomePhone": "(71) 555-5598", + "Extension": "465", + "Notes": "Robert King served in the Peace Corps and traveled extensively before completing his degree in English at the University of Michigan in 1992, the year he joined the company. After completing a course entitled \"Selling in Europe,\" he was transferred to the London office in March 1993.", + "ReportsTo": 5, + "PhotoPath": "http://accweb/emmployees/davolio.bmp" + }, { + "Orders": [{ + "OrderID": 10262, + "CustomerID": "RATTC", + "EmployeeID": 8, + "OrderDate": "1996-07-22T00:00:00", + "RequiredDate": "1996-08-19T00:00:00", + "ShippedDate": "1996-07-25T00:00:00", + "ShipVia": 3, + "Freight": "48.2900", + "ShipName": "Rattlesnake Canyon Grocery", + "ShipAddress": "2817 Milton Dr.", + "ShipCity": "Albuquerque", + "ShipRegion": "NM", + "ShipPostalCode": "87110", + "ShipCountry": "USA" + }, { + "OrderID": 10268, + "CustomerID": "GROSR", + "EmployeeID": 8, + "OrderDate": "1996-07-30T00:00:00", + "RequiredDate": "1996-08-27T00:00:00", + "ShippedDate": "1996-08-02T00:00:00", + "ShipVia": 3, + "Freight": "66.2900", + "ShipName": "GROSELLA-Restaurante", + "ShipAddress": "5\u00aa Ave. Los Palos Grandes", + "ShipCity": "Caracas", + "ShipRegion": "DF", + "ShipPostalCode": "1081", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10276, + "CustomerID": "TORTU", + "EmployeeID": 8, + "OrderDate": "1996-08-08T00:00:00", + "RequiredDate": "1996-08-22T00:00:00", + "ShippedDate": "1996-08-14T00:00:00", + "ShipVia": 3, + "Freight": "13.8400", + "ShipName": "Tortuga Restaurante", + "ShipAddress": "Avda. Azteca 123", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05033", + "ShipCountry": "Mexico" + }, { + "OrderID": 10278, + "CustomerID": "BERGS", + "EmployeeID": 8, + "OrderDate": "1996-08-12T00:00:00", + "RequiredDate": "1996-09-09T00:00:00", + "ShippedDate": "1996-08-16T00:00:00", + "ShipVia": 2, + "Freight": "92.6900", + "ShipName": "Berglunds snabbk\u00f6p", + "ShipAddress": "Berguvsv\u00e4gen 8", + "ShipCity": "Lule\u00e5", + "ShipRegion": null, + "ShipPostalCode": "S-958 22", + "ShipCountry": "Sweden" + }, { + "OrderID": 10279, + "CustomerID": "LEHMS", + "EmployeeID": 8, + "OrderDate": "1996-08-13T00:00:00", + "RequiredDate": "1996-09-10T00:00:00", + "ShippedDate": "1996-08-16T00:00:00", + "ShipVia": 2, + "Freight": "25.8300", + "ShipName": "Lehmanns Marktstand", + "ShipAddress": "Magazinweg 7", + "ShipCity": "Frankfurt a.M.", + "ShipRegion": null, + "ShipPostalCode": "60528", + "ShipCountry": "Germany" + }, { + "OrderID": 10286, + "CustomerID": "QUICK", + "EmployeeID": 8, + "OrderDate": "1996-08-21T00:00:00", + "RequiredDate": "1996-09-18T00:00:00", + "ShippedDate": "1996-08-30T00:00:00", + "ShipVia": 3, + "Freight": "229.2400", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10287, + "CustomerID": "RICAR", + "EmployeeID": 8, + "OrderDate": "1996-08-22T00:00:00", + "RequiredDate": "1996-09-19T00:00:00", + "ShippedDate": "1996-08-28T00:00:00", + "ShipVia": 3, + "Freight": "12.7600", + "ShipName": "Ricardo Adocicados", + "ShipAddress": "Av. Copacabana, 267", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "02389-890", + "ShipCountry": "Brazil" + }, { + "OrderID": 10290, + "CustomerID": "COMMI", + "EmployeeID": 8, + "OrderDate": "1996-08-27T00:00:00", + "RequiredDate": "1996-09-24T00:00:00", + "ShippedDate": "1996-09-03T00:00:00", + "ShipVia": 1, + "Freight": "79.7000", + "ShipName": "Com\u00e9rcio Mineiro", + "ShipAddress": "Av. dos Lus\u00edadas, 23", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05432-043", + "ShipCountry": "Brazil" + }, { + "OrderID": 10301, + "CustomerID": "WANDK", + "EmployeeID": 8, + "OrderDate": "1996-09-09T00:00:00", + "RequiredDate": "1996-10-07T00:00:00", + "ShippedDate": "1996-09-17T00:00:00", + "ShipVia": 2, + "Freight": "45.0800", + "ShipName": "Die Wandernde Kuh", + "ShipAddress": "Adenauerallee 900", + "ShipCity": "Stuttgart", + "ShipRegion": null, + "ShipPostalCode": "70563", + "ShipCountry": "Germany" + }, { + "OrderID": 10305, + "CustomerID": "OLDWO", + "EmployeeID": 8, + "OrderDate": "1996-09-13T00:00:00", + "RequiredDate": "1996-10-11T00:00:00", + "ShippedDate": "1996-10-09T00:00:00", + "ShipVia": 3, + "Freight": "257.6200", + "ShipName": "Old World Delicatessen", + "ShipAddress": "2743 Bering St.", + "ShipCity": "Anchorage", + "ShipRegion": "AK", + "ShipPostalCode": "99508", + "ShipCountry": "USA" + }, { + "OrderID": 10310, + "CustomerID": "THEBI", + "EmployeeID": 8, + "OrderDate": "1996-09-20T00:00:00", + "RequiredDate": "1996-10-18T00:00:00", + "ShippedDate": "1996-09-27T00:00:00", + "ShipVia": 2, + "Freight": "17.5200", + "ShipName": "The Big Cheese", + "ShipAddress": "89 Jefferson Way Suite 2", + "ShipCity": "Portland", + "ShipRegion": "OR", + "ShipPostalCode": "97201", + "ShipCountry": "USA" + }, { + "OrderID": 10318, + "CustomerID": "ISLAT", + "EmployeeID": 8, + "OrderDate": "1996-10-01T00:00:00", + "RequiredDate": "1996-10-29T00:00:00", + "ShippedDate": "1996-10-04T00:00:00", + "ShipVia": 2, + "Freight": "4.7300", + "ShipName": "Island Trading", + "ShipAddress": "Garden House Crowther Way", + "ShipCity": "Cowes", + "ShipRegion": "Isle of Wight", + "ShipPostalCode": "PO31 7PJ", + "ShipCountry": "UK" + }, { + "OrderID": 10334, + "CustomerID": "VICTE", + "EmployeeID": 8, + "OrderDate": "1996-10-21T00:00:00", + "RequiredDate": "1996-11-18T00:00:00", + "ShippedDate": "1996-10-28T00:00:00", + "ShipVia": 2, + "Freight": "8.5600", + "ShipName": "Victuailles en stock", + "ShipAddress": "2, rue du Commerce", + "ShipCity": "Lyon", + "ShipRegion": null, + "ShipPostalCode": "69004", + "ShipCountry": "France" + }, { + "OrderID": 10354, + "CustomerID": "PERIC", + "EmployeeID": 8, + "OrderDate": "1996-11-14T00:00:00", + "RequiredDate": "1996-12-12T00:00:00", + "ShippedDate": "1996-11-20T00:00:00", + "ShipVia": 3, + "Freight": "53.8000", + "ShipName": "Pericles Comidas cl\u00e1sicas", + "ShipAddress": "Calle Dr. Jorge Cash 321", + "ShipCity": "M\u00e9xico D.F.", + "ShipRegion": null, + "ShipPostalCode": "05033", + "ShipCountry": "Mexico" + }, { + "OrderID": 10366, + "CustomerID": "GALED", + "EmployeeID": 8, + "OrderDate": "1996-11-28T00:00:00", + "RequiredDate": "1997-01-09T00:00:00", + "ShippedDate": "1996-12-30T00:00:00", + "ShipVia": 2, + "Freight": "10.1400", + "ShipName": "Galer\u00eda del gastron\u00f3mo", + "ShipAddress": "Rambla de Catalu\u00f1a, 23", + "ShipCity": "Barcelona", + "ShipRegion": null, + "ShipPostalCode": "8022", + "ShipCountry": "Spain" + }, { + "OrderID": 10369, + "CustomerID": "SPLIR", + "EmployeeID": 8, + "OrderDate": "1996-12-02T00:00:00", + "RequiredDate": "1996-12-30T00:00:00", + "ShippedDate": "1996-12-09T00:00:00", + "ShipVia": 2, + "Freight": "195.6800", + "ShipName": "Split Rail Beer & Ale", + "ShipAddress": "P.O. Box 555", + "ShipCity": "Lander", + "ShipRegion": "WY", + "ShipPostalCode": "82520", + "ShipCountry": "USA" + }, { + "OrderID": 10380, + "CustomerID": "HUNGO", + "EmployeeID": 8, + "OrderDate": "1996-12-12T00:00:00", + "RequiredDate": "1997-01-09T00:00:00", + "ShippedDate": "1997-01-16T00:00:00", + "ShipVia": 3, + "Freight": "35.0300", + "ShipName": "Hungry Owl All-Night Grocers", + "ShipAddress": "8 Johnstown Road", + "ShipCity": "Cork", + "ShipRegion": "Co. Cork", + "ShipPostalCode": null, + "ShipCountry": "Ireland" + }, { + "OrderID": 10383, + "CustomerID": "AROUT", + "EmployeeID": 8, + "OrderDate": "1996-12-16T00:00:00", + "RequiredDate": "1997-01-13T00:00:00", + "ShippedDate": "1996-12-18T00:00:00", + "ShipVia": 3, + "Freight": "34.2400", + "ShipName": "Around the Horn", + "ShipAddress": "Brook Farm Stratford St. Mary", + "ShipCity": "Colchester", + "ShipRegion": "Essex", + "ShipPostalCode": "CO7 6JX", + "ShipCountry": "UK" + }, { + "OrderID": 10399, + "CustomerID": "VAFFE", + "EmployeeID": 8, + "OrderDate": "1996-12-31T00:00:00", + "RequiredDate": "1997-01-14T00:00:00", + "ShippedDate": "1997-01-08T00:00:00", + "ShipVia": 3, + "Freight": "27.3600", + "ShipName": "Vaffeljernet", + "ShipAddress": "Smagsloget 45", + "ShipCity": "\u00c5rhus", + "ShipRegion": null, + "ShipPostalCode": "8200", + "ShipCountry": "Denmark" + }, { + "OrderID": 10402, + "CustomerID": "ERNSH", + "EmployeeID": 8, + "OrderDate": "1997-01-02T00:00:00", + "RequiredDate": "1997-02-13T00:00:00", + "ShippedDate": "1997-01-10T00:00:00", + "ShipVia": 2, + "Freight": "67.8800", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10408, + "CustomerID": "FOLIG", + "EmployeeID": 8, + "OrderDate": "1997-01-08T00:00:00", + "RequiredDate": "1997-02-05T00:00:00", + "ShippedDate": "1997-01-14T00:00:00", + "ShipVia": 1, + "Freight": "11.2600", + "ShipName": "Folies gourmandes", + "ShipAddress": "184, chauss\u00e9e de Tournai", + "ShipCity": "Lille", + "ShipRegion": null, + "ShipPostalCode": "59000", + "ShipCountry": "France" + }, { + "OrderID": 10412, + "CustomerID": "WARTH", + "EmployeeID": 8, + "OrderDate": "1997-01-13T00:00:00", + "RequiredDate": "1997-02-10T00:00:00", + "ShippedDate": "1997-01-15T00:00:00", + "ShipVia": 2, + "Freight": "3.7700", + "ShipName": "Wartian Herkku", + "ShipAddress": "Torikatu 38", + "ShipCity": "Oulu", + "ShipRegion": null, + "ShipPostalCode": "90110", + "ShipCountry": "Finland" + }, { + "OrderID": 10416, + "CustomerID": "WARTH", + "EmployeeID": 8, + "OrderDate": "1997-01-16T00:00:00", + "RequiredDate": "1997-02-13T00:00:00", + "ShippedDate": "1997-01-27T00:00:00", + "ShipVia": 3, + "Freight": "22.7200", + "ShipName": "Wartian Herkku", + "ShipAddress": "Torikatu 38", + "ShipCity": "Oulu", + "ShipRegion": null, + "ShipPostalCode": "90110", + "ShipCountry": "Finland" + }, { + "OrderID": 10421, + "CustomerID": "QUEDE", + "EmployeeID": 8, + "OrderDate": "1997-01-21T00:00:00", + "RequiredDate": "1997-03-04T00:00:00", + "ShippedDate": "1997-01-27T00:00:00", + "ShipVia": 1, + "Freight": "99.2300", + "ShipName": "Que Del\u00edcia", + "ShipAddress": "Rua da Panificadora, 12", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "02389-673", + "ShipCountry": "Brazil" + }, { + "OrderID": 10435, + "CustomerID": "CONSH", + "EmployeeID": 8, + "OrderDate": "1997-02-04T00:00:00", + "RequiredDate": "1997-03-18T00:00:00", + "ShippedDate": "1997-02-07T00:00:00", + "ShipVia": 2, + "Freight": "9.2100", + "ShipName": "Consolidated Holdings", + "ShipAddress": "Berkeley Gardens 12 Brewery", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "WX1 6LT", + "ShipCountry": "UK" + }, { + "OrderID": 10437, + "CustomerID": "WARTH", + "EmployeeID": 8, + "OrderDate": "1997-02-05T00:00:00", + "RequiredDate": "1997-03-05T00:00:00", + "ShippedDate": "1997-02-12T00:00:00", + "ShipVia": 1, + "Freight": "19.9700", + "ShipName": "Wartian Herkku", + "ShipAddress": "Torikatu 38", + "ShipCity": "Oulu", + "ShipRegion": null, + "ShipPostalCode": "90110", + "ShipCountry": "Finland" + }, { + "OrderID": 10443, + "CustomerID": "REGGC", + "EmployeeID": 8, + "OrderDate": "1997-02-12T00:00:00", + "RequiredDate": "1997-03-12T00:00:00", + "ShippedDate": "1997-02-14T00:00:00", + "ShipVia": 1, + "Freight": "13.9500", + "ShipName": "Reggiani Caseifici", + "ShipAddress": "Strada Provinciale 124", + "ShipCity": "Reggio Emilia", + "ShipRegion": null, + "ShipPostalCode": "42100", + "ShipCountry": "Italy" + }, { + "OrderID": 10450, + "CustomerID": "VICTE", + "EmployeeID": 8, + "OrderDate": "1997-02-19T00:00:00", + "RequiredDate": "1997-03-19T00:00:00", + "ShippedDate": "1997-03-11T00:00:00", + "ShipVia": 2, + "Freight": "7.2300", + "ShipName": "Victuailles en stock", + "ShipAddress": "2, rue du Commerce", + "ShipCity": "Lyon", + "ShipRegion": null, + "ShipPostalCode": "69004", + "ShipCountry": "France" + }, { + "OrderID": 10452, + "CustomerID": "SAVEA", + "EmployeeID": 8, + "OrderDate": "1997-02-20T00:00:00", + "RequiredDate": "1997-03-20T00:00:00", + "ShippedDate": "1997-02-26T00:00:00", + "ShipVia": 1, + "Freight": "140.2600", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10455, + "CustomerID": "WARTH", + "EmployeeID": 8, + "OrderDate": "1997-02-24T00:00:00", + "RequiredDate": "1997-04-07T00:00:00", + "ShippedDate": "1997-03-03T00:00:00", + "ShipVia": 2, + "Freight": "180.4500", + "ShipName": "Wartian Herkku", + "ShipAddress": "Torikatu 38", + "ShipCity": "Oulu", + "ShipRegion": null, + "ShipPostalCode": "90110", + "ShipCountry": "Finland" + }, { + "OrderID": 10456, + "CustomerID": "KOENE", + "EmployeeID": 8, + "OrderDate": "1997-02-25T00:00:00", + "RequiredDate": "1997-04-08T00:00:00", + "ShippedDate": "1997-02-28T00:00:00", + "ShipVia": 2, + "Freight": "8.1200", + "ShipName": "K\u00f6niglich Essen", + "ShipAddress": "Maubelstr. 90", + "ShipCity": "Brandenburg", + "ShipRegion": null, + "ShipPostalCode": "14776", + "ShipCountry": "Germany" + }, { + "OrderID": 10460, + "CustomerID": "FOLKO", + "EmployeeID": 8, + "OrderDate": "1997-02-28T00:00:00", + "RequiredDate": "1997-03-28T00:00:00", + "ShippedDate": "1997-03-03T00:00:00", + "ShipVia": 1, + "Freight": "16.2700", + "ShipName": "Folk och f\u00e4 HB", + "ShipAddress": "\u00c5kergatan 24", + "ShipCity": "Br\u00e4cke", + "ShipRegion": null, + "ShipPostalCode": "S-844 67", + "ShipCountry": "Sweden" + }, { + "OrderID": 10467, + "CustomerID": "MAGAA", + "EmployeeID": 8, + "OrderDate": "1997-03-06T00:00:00", + "RequiredDate": "1997-04-03T00:00:00", + "ShippedDate": "1997-03-11T00:00:00", + "ShipVia": 2, + "Freight": "4.9300", + "ShipName": "Magazzini Alimentari Riuniti", + "ShipAddress": "Via Ludovico il Moro 22", + "ShipCity": "Bergamo", + "ShipRegion": null, + "ShipPostalCode": "24100", + "ShipCountry": "Italy" + }, { + "OrderID": 10472, + "CustomerID": "SEVES", + "EmployeeID": 8, + "OrderDate": "1997-03-12T00:00:00", + "RequiredDate": "1997-04-09T00:00:00", + "ShippedDate": "1997-03-19T00:00:00", + "ShipVia": 1, + "Freight": "4.2000", + "ShipName": "Seven Seas Imports", + "ShipAddress": "90 Wadhurst Rd.", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "OX15 4NB", + "ShipCountry": "UK" + }, { + "OrderID": 10476, + "CustomerID": "HILAA", + "EmployeeID": 8, + "OrderDate": "1997-03-17T00:00:00", + "RequiredDate": "1997-04-14T00:00:00", + "ShippedDate": "1997-03-24T00:00:00", + "ShipVia": 3, + "Freight": "4.4100", + "ShipName": "HILARION-Abastos", + "ShipAddress": "Carrera 22 con Ave. Carlos Soublette #8-35", + "ShipCity": "San Crist\u00f3bal", + "ShipRegion": "T\u00e1chira", + "ShipPostalCode": "5022", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10481, + "CustomerID": "RICAR", + "EmployeeID": 8, + "OrderDate": "1997-03-20T00:00:00", + "RequiredDate": "1997-04-17T00:00:00", + "ShippedDate": "1997-03-25T00:00:00", + "ShipVia": 2, + "Freight": "64.3300", + "ShipName": "Ricardo Adocicados", + "ShipAddress": "Av. Copacabana, 267", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "02389-890", + "ShipCountry": "Brazil" + }, { + "OrderID": 10488, + "CustomerID": "FRANK", + "EmployeeID": 8, + "OrderDate": "1997-03-27T00:00:00", + "RequiredDate": "1997-04-24T00:00:00", + "ShippedDate": "1997-04-02T00:00:00", + "ShipVia": 2, + "Freight": "4.9300", + "ShipName": "Frankenversand", + "ShipAddress": "Berliner Platz 43", + "ShipCity": "M\u00fcnchen", + "ShipRegion": null, + "ShipPostalCode": "80805", + "ShipCountry": "Germany" + }, { + "OrderID": 10491, + "CustomerID": "FURIB", + "EmployeeID": 8, + "OrderDate": "1997-03-31T00:00:00", + "RequiredDate": "1997-04-28T00:00:00", + "ShippedDate": "1997-04-08T00:00:00", + "ShipVia": 3, + "Freight": "16.9600", + "ShipName": "Furia Bacalhau e Frutos do Mar", + "ShipAddress": "Jardim das rosas n. 32", + "ShipCity": "Lisboa", + "ShipRegion": null, + "ShipPostalCode": "1675", + "ShipCountry": "Portugal" + }, { + "OrderID": 10498, + "CustomerID": "HILAA", + "EmployeeID": 8, + "OrderDate": "1997-04-07T00:00:00", + "RequiredDate": "1997-05-05T00:00:00", + "ShippedDate": "1997-04-11T00:00:00", + "ShipVia": 2, + "Freight": "29.7500", + "ShipName": "HILARION-Abastos", + "ShipAddress": "Carrera 22 con Ave. Carlos Soublette #8-35", + "ShipCity": "San Crist\u00f3bal", + "ShipRegion": "T\u00e1chira", + "ShipPostalCode": "5022", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10521, + "CustomerID": "CACTU", + "EmployeeID": 8, + "OrderDate": "1997-04-29T00:00:00", + "RequiredDate": "1997-05-27T00:00:00", + "ShippedDate": "1997-05-02T00:00:00", + "ShipVia": 2, + "Freight": "17.2200", + "ShipName": "Cactus Comidas para llevar", + "ShipAddress": "Cerrito 333", + "ShipCity": "Buenos Aires", + "ShipRegion": null, + "ShipPostalCode": "1010", + "ShipCountry": "Argentina" + }, { + "OrderID": 10533, + "CustomerID": "FOLKO", + "EmployeeID": 8, + "OrderDate": "1997-05-12T00:00:00", + "RequiredDate": "1997-06-09T00:00:00", + "ShippedDate": "1997-05-22T00:00:00", + "ShipVia": 1, + "Freight": "188.0400", + "ShipName": "Folk och f\u00e4 HB", + "ShipAddress": "\u00c5kergatan 24", + "ShipCity": "Br\u00e4cke", + "ShipRegion": null, + "ShipPostalCode": "S-844 67", + "ShipCountry": "Sweden" + }, { + "OrderID": 10534, + "CustomerID": "LEHMS", + "EmployeeID": 8, + "OrderDate": "1997-05-12T00:00:00", + "RequiredDate": "1997-06-09T00:00:00", + "ShippedDate": "1997-05-14T00:00:00", + "ShipVia": 2, + "Freight": "27.9400", + "ShipName": "Lehmanns Marktstand", + "ShipAddress": "Magazinweg 7", + "ShipCity": "Frankfurt a.M.", + "ShipRegion": null, + "ShipPostalCode": "60528", + "ShipCountry": "Germany" + }, { + "OrderID": 10543, + "CustomerID": "LILAS", + "EmployeeID": 8, + "OrderDate": "1997-05-21T00:00:00", + "RequiredDate": "1997-06-18T00:00:00", + "ShippedDate": "1997-05-23T00:00:00", + "ShipVia": 2, + "Freight": "48.1700", + "ShipName": "LILA-Supermercado", + "ShipAddress": "Carrera 52 con Ave. Bol\u00edvar #65-98 Llano Largo", + "ShipCity": "Barquisimeto", + "ShipRegion": "Lara", + "ShipPostalCode": "3508", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10545, + "CustomerID": "LAZYK", + "EmployeeID": 8, + "OrderDate": "1997-05-22T00:00:00", + "RequiredDate": "1997-06-19T00:00:00", + "ShippedDate": "1997-06-26T00:00:00", + "ShipVia": 2, + "Freight": "11.9200", + "ShipName": "Lazy K Kountry Store", + "ShipAddress": "12 Orchestra Terrace", + "ShipCity": "Walla Walla", + "ShipRegion": "WA", + "ShipPostalCode": "99362", + "ShipCountry": "USA" + }, { + "OrderID": 10560, + "CustomerID": "FRANK", + "EmployeeID": 8, + "OrderDate": "1997-06-06T00:00:00", + "RequiredDate": "1997-07-04T00:00:00", + "ShippedDate": "1997-06-09T00:00:00", + "ShipVia": 1, + "Freight": "36.6500", + "ShipName": "Frankenversand", + "ShipAddress": "Berliner Platz 43", + "ShipCity": "M\u00fcnchen", + "ShipRegion": null, + "ShipPostalCode": "80805", + "ShipCountry": "Germany" + }, { + "OrderID": 10565, + "CustomerID": "MEREP", + "EmployeeID": 8, + "OrderDate": "1997-06-11T00:00:00", + "RequiredDate": "1997-07-09T00:00:00", + "ShippedDate": "1997-06-18T00:00:00", + "ShipVia": 2, + "Freight": "7.1500", + "ShipName": "M\u00e8re Paillarde", + "ShipAddress": "43 rue St. Laurent", + "ShipCity": "Montr\u00e9al", + "ShipRegion": "Qu\u00e9bec", + "ShipPostalCode": "H1J 1C3", + "ShipCountry": "Canada" + }, { + "OrderID": 10571, + "CustomerID": "ERNSH", + "EmployeeID": 8, + "OrderDate": "1997-06-17T00:00:00", + "RequiredDate": "1997-07-29T00:00:00", + "ShippedDate": "1997-07-04T00:00:00", + "ShipVia": 3, + "Freight": "26.0600", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10589, + "CustomerID": "GREAL", + "EmployeeID": 8, + "OrderDate": "1997-07-04T00:00:00", + "RequiredDate": "1997-08-01T00:00:00", + "ShippedDate": "1997-07-14T00:00:00", + "ShipVia": 2, + "Freight": "4.4200", + "ShipName": "Great Lakes Food Market", + "ShipAddress": "2732 Baker Blvd.", + "ShipCity": "Eugene", + "ShipRegion": "OR", + "ShipPostalCode": "97403", + "ShipCountry": "USA" + }, { + "OrderID": 10596, + "CustomerID": "WHITC", + "EmployeeID": 8, + "OrderDate": "1997-07-11T00:00:00", + "RequiredDate": "1997-08-08T00:00:00", + "ShippedDate": "1997-08-12T00:00:00", + "ShipVia": 1, + "Freight": "16.3400", + "ShipName": "White Clover Markets", + "ShipAddress": "1029 - 12th Ave. S.", + "ShipCity": "Seattle", + "ShipRegion": "WA", + "ShipPostalCode": "98124", + "ShipCountry": "USA" + }, { + "OrderID": 10602, + "CustomerID": "VAFFE", + "EmployeeID": 8, + "OrderDate": "1997-07-17T00:00:00", + "RequiredDate": "1997-08-14T00:00:00", + "ShippedDate": "1997-07-22T00:00:00", + "ShipVia": 2, + "Freight": "2.9200", + "ShipName": "Vaffeljernet", + "ShipAddress": "Smagsloget 45", + "ShipCity": "\u00c5rhus", + "ShipRegion": null, + "ShipPostalCode": "8200", + "ShipCountry": "Denmark" + }, { + "OrderID": 10603, + "CustomerID": "SAVEA", + "EmployeeID": 8, + "OrderDate": "1997-07-18T00:00:00", + "RequiredDate": "1997-08-15T00:00:00", + "ShippedDate": "1997-08-08T00:00:00", + "ShipVia": 2, + "Freight": "48.7700", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10610, + "CustomerID": "LAMAI", + "EmployeeID": 8, + "OrderDate": "1997-07-25T00:00:00", + "RequiredDate": "1997-08-22T00:00:00", + "ShippedDate": "1997-08-06T00:00:00", + "ShipVia": 1, + "Freight": "26.7800", + "ShipName": "La maison d'Asie", + "ShipAddress": "1 rue Alsace-Lorraine", + "ShipCity": "Toulouse", + "ShipRegion": null, + "ShipPostalCode": "31000", + "ShipCountry": "France" + }, { + "OrderID": 10614, + "CustomerID": "BLAUS", + "EmployeeID": 8, + "OrderDate": "1997-07-29T00:00:00", + "RequiredDate": "1997-08-26T00:00:00", + "ShippedDate": "1997-08-01T00:00:00", + "ShipVia": 3, + "Freight": "1.9300", + "ShipName": "Blauer See Delikatessen", + "ShipAddress": "Forsterstr. 57", + "ShipCity": "Mannheim", + "ShipRegion": null, + "ShipPostalCode": "68306", + "ShipCountry": "Germany" + }, { + "OrderID": 10623, + "CustomerID": "FRANK", + "EmployeeID": 8, + "OrderDate": "1997-08-07T00:00:00", + "RequiredDate": "1997-09-04T00:00:00", + "ShippedDate": "1997-08-12T00:00:00", + "ShipVia": 2, + "Freight": "97.1800", + "ShipName": "Frankenversand", + "ShipAddress": "Berliner Platz 43", + "ShipCity": "M\u00fcnchen", + "ShipRegion": null, + "ShipPostalCode": "80805", + "ShipCountry": "Germany" + }, { + "OrderID": 10627, + "CustomerID": "SAVEA", + "EmployeeID": 8, + "OrderDate": "1997-08-11T00:00:00", + "RequiredDate": "1997-09-22T00:00:00", + "ShippedDate": "1997-08-21T00:00:00", + "ShipVia": 3, + "Freight": "107.4600", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10631, + "CustomerID": "LAMAI", + "EmployeeID": 8, + "OrderDate": "1997-08-14T00:00:00", + "RequiredDate": "1997-09-11T00:00:00", + "ShippedDate": "1997-08-15T00:00:00", + "ShipVia": 1, + "Freight": "0.8700", + "ShipName": "La maison d'Asie", + "ShipAddress": "1 rue Alsace-Lorraine", + "ShipCity": "Toulouse", + "ShipRegion": null, + "ShipPostalCode": "31000", + "ShipCountry": "France" + }, { + "OrderID": 10632, + "CustomerID": "WANDK", + "EmployeeID": 8, + "OrderDate": "1997-08-14T00:00:00", + "RequiredDate": "1997-09-11T00:00:00", + "ShippedDate": "1997-08-19T00:00:00", + "ShipVia": 1, + "Freight": "41.3800", + "ShipName": "Die Wandernde Kuh", + "ShipAddress": "Adenauerallee 900", + "ShipCity": "Stuttgart", + "ShipRegion": null, + "ShipPostalCode": "70563", + "ShipCountry": "Germany" + }, { + "OrderID": 10635, + "CustomerID": "MAGAA", + "EmployeeID": 8, + "OrderDate": "1997-08-18T00:00:00", + "RequiredDate": "1997-09-15T00:00:00", + "ShippedDate": "1997-08-21T00:00:00", + "ShipVia": 3, + "Freight": "47.4600", + "ShipName": "Magazzini Alimentari Riuniti", + "ShipAddress": "Via Ludovico il Moro 22", + "ShipCity": "Bergamo", + "ShipRegion": null, + "ShipPostalCode": "24100", + "ShipCountry": "Italy" + }, { + "OrderID": 10651, + "CustomerID": "WANDK", + "EmployeeID": 8, + "OrderDate": "1997-09-01T00:00:00", + "RequiredDate": "1997-09-29T00:00:00", + "ShippedDate": "1997-09-11T00:00:00", + "ShipVia": 2, + "Freight": "20.6000", + "ShipName": "Die Wandernde Kuh", + "ShipAddress": "Adenauerallee 900", + "ShipCity": "Stuttgart", + "ShipRegion": null, + "ShipPostalCode": "70563", + "ShipCountry": "Germany" + }, { + "OrderID": 10660, + "CustomerID": "HUNGC", + "EmployeeID": 8, + "OrderDate": "1997-09-08T00:00:00", + "RequiredDate": "1997-10-06T00:00:00", + "ShippedDate": "1997-10-15T00:00:00", + "ShipVia": 1, + "Freight": "111.2900", + "ShipName": "Hungry Coyote Import Store", + "ShipAddress": "City Center Plaza 516 Main St.", + "ShipCity": "Elgin", + "ShipRegion": "OR", + "ShipPostalCode": "97827", + "ShipCountry": "USA" + }, { + "OrderID": 10679, + "CustomerID": "BLONP", + "EmployeeID": 8, + "OrderDate": "1997-09-23T00:00:00", + "RequiredDate": "1997-10-21T00:00:00", + "ShippedDate": "1997-09-30T00:00:00", + "ShipVia": 3, + "Freight": "27.9400", + "ShipName": "Blondel p\u00e8re et fils", + "ShipAddress": "24, place Kl\u00e9ber", + "ShipCity": "Strasbourg", + "ShipRegion": null, + "ShipPostalCode": "67000", + "ShipCountry": "France" + }, { + "OrderID": 10694, + "CustomerID": "QUICK", + "EmployeeID": 8, + "OrderDate": "1997-10-06T00:00:00", + "RequiredDate": "1997-11-03T00:00:00", + "ShippedDate": "1997-10-09T00:00:00", + "ShipVia": 3, + "Freight": "398.3600", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10696, + "CustomerID": "WHITC", + "EmployeeID": 8, + "OrderDate": "1997-10-08T00:00:00", + "RequiredDate": "1997-11-19T00:00:00", + "ShippedDate": "1997-10-14T00:00:00", + "ShipVia": 3, + "Freight": "102.5500", + "ShipName": "White Clover Markets", + "ShipAddress": "1029 - 12th Ave. S.", + "ShipCity": "Seattle", + "ShipRegion": "WA", + "ShipPostalCode": "98124", + "ShipCountry": "USA" + }, { + "OrderID": 10706, + "CustomerID": "OLDWO", + "EmployeeID": 8, + "OrderDate": "1997-10-16T00:00:00", + "RequiredDate": "1997-11-13T00:00:00", + "ShippedDate": "1997-10-21T00:00:00", + "ShipVia": 3, + "Freight": "135.6300", + "ShipName": "Old World Delicatessen", + "ShipAddress": "2743 Bering St.", + "ShipCity": "Anchorage", + "ShipRegion": "AK", + "ShipPostalCode": "99508", + "ShipCountry": "USA" + }, { + "OrderID": 10719, + "CustomerID": "LETSS", + "EmployeeID": 8, + "OrderDate": "1997-10-27T00:00:00", + "RequiredDate": "1997-11-24T00:00:00", + "ShippedDate": "1997-11-05T00:00:00", + "ShipVia": 2, + "Freight": "51.4400", + "ShipName": "Let's Stop N Shop", + "ShipAddress": "87 Polk St. Suite 5", + "ShipCity": "San Francisco", + "ShipRegion": "CA", + "ShipPostalCode": "94117", + "ShipCountry": "USA" + }, { + "OrderID": 10720, + "CustomerID": "QUEDE", + "EmployeeID": 8, + "OrderDate": "1997-10-28T00:00:00", + "RequiredDate": "1997-11-11T00:00:00", + "ShippedDate": "1997-11-05T00:00:00", + "ShipVia": 2, + "Freight": "9.5300", + "ShipName": "Que Del\u00edcia", + "ShipAddress": "Rua da Panificadora, 12", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "02389-673", + "ShipCountry": "Brazil" + }, { + "OrderID": 10722, + "CustomerID": "SAVEA", + "EmployeeID": 8, + "OrderDate": "1997-10-29T00:00:00", + "RequiredDate": "1997-12-10T00:00:00", + "ShippedDate": "1997-11-04T00:00:00", + "ShipVia": 1, + "Freight": "74.5800", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10724, + "CustomerID": "MEREP", + "EmployeeID": 8, + "OrderDate": "1997-10-30T00:00:00", + "RequiredDate": "1997-12-11T00:00:00", + "ShippedDate": "1997-11-05T00:00:00", + "ShipVia": 2, + "Freight": "57.7500", + "ShipName": "M\u00e8re Paillarde", + "ShipAddress": "43 rue St. Laurent", + "ShipCity": "Montr\u00e9al", + "ShipRegion": "Qu\u00e9bec", + "ShipPostalCode": "H1J 1C3", + "ShipCountry": "Canada" + }, { + "OrderID": 10729, + "CustomerID": "LINOD", + "EmployeeID": 8, + "OrderDate": "1997-11-04T00:00:00", + "RequiredDate": "1997-12-16T00:00:00", + "ShippedDate": "1997-11-14T00:00:00", + "ShipVia": 3, + "Freight": "141.0600", + "ShipName": "LINO-Delicateses", + "ShipAddress": "Ave. 5 de Mayo Porlamar", + "ShipCity": "I. de Margarita", + "ShipRegion": "Nueva Esparta", + "ShipPostalCode": "4980", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10756, + "CustomerID": "SPLIR", + "EmployeeID": 8, + "OrderDate": "1997-11-27T00:00:00", + "RequiredDate": "1997-12-25T00:00:00", + "ShippedDate": "1997-12-02T00:00:00", + "ShipVia": 2, + "Freight": "73.2100", + "ShipName": "Split Rail Beer & Ale", + "ShipAddress": "P.O. Box 555", + "ShipCity": "Lander", + "ShipRegion": "WY", + "ShipPostalCode": "82520", + "ShipCountry": "USA" + }, { + "OrderID": 10770, + "CustomerID": "HANAR", + "EmployeeID": 8, + "OrderDate": "1997-12-09T00:00:00", + "RequiredDate": "1998-01-06T00:00:00", + "ShippedDate": "1997-12-17T00:00:00", + "ShipVia": 3, + "Freight": "5.3200", + "ShipName": "Hanari Carnes", + "ShipAddress": "Rua do Pa\u00e7o, 67", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "05454-876", + "ShipCountry": "Brazil" + }, { + "OrderID": 10786, + "CustomerID": "QUEEN", + "EmployeeID": 8, + "OrderDate": "1997-12-19T00:00:00", + "RequiredDate": "1998-01-16T00:00:00", + "ShippedDate": "1997-12-23T00:00:00", + "ShipVia": 1, + "Freight": "110.8700", + "ShipName": "Queen Cozinha", + "ShipAddress": "Alameda dos Can\u00e0rios, 891", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05487-020", + "ShipCountry": "Brazil" + }, { + "OrderID": 10795, + "CustomerID": "ERNSH", + "EmployeeID": 8, + "OrderDate": "1997-12-24T00:00:00", + "RequiredDate": "1998-01-21T00:00:00", + "ShippedDate": "1998-01-20T00:00:00", + "ShipVia": 2, + "Freight": "126.6600", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10811, + "CustomerID": "LINOD", + "EmployeeID": 8, + "OrderDate": "1998-01-02T00:00:00", + "RequiredDate": "1998-01-30T00:00:00", + "ShippedDate": "1998-01-08T00:00:00", + "ShipVia": 1, + "Freight": "31.2200", + "ShipName": "LINO-Delicateses", + "ShipAddress": "Ave. 5 de Mayo Porlamar", + "ShipCity": "I. de Margarita", + "ShipRegion": "Nueva Esparta", + "ShipPostalCode": "4980", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10824, + "CustomerID": "FOLKO", + "EmployeeID": 8, + "OrderDate": "1998-01-09T00:00:00", + "RequiredDate": "1998-02-06T00:00:00", + "ShippedDate": "1998-01-30T00:00:00", + "ShipVia": 1, + "Freight": "1.2300", + "ShipName": "Folk och f\u00e4 HB", + "ShipAddress": "\u00c5kergatan 24", + "ShipCity": "Br\u00e4cke", + "ShipRegion": null, + "ShipPostalCode": "S-844 67", + "ShipCountry": "Sweden" + }, { + "OrderID": 10844, + "CustomerID": "PICCO", + "EmployeeID": 8, + "OrderDate": "1998-01-21T00:00:00", + "RequiredDate": "1998-02-18T00:00:00", + "ShippedDate": "1998-01-26T00:00:00", + "ShipVia": 2, + "Freight": "25.2200", + "ShipName": "Piccolo und mehr", + "ShipAddress": "Geislweg 14", + "ShipCity": "Salzburg", + "ShipRegion": null, + "ShipPostalCode": "5020", + "ShipCountry": "Austria" + }, { + "OrderID": 10845, + "CustomerID": "QUICK", + "EmployeeID": 8, + "OrderDate": "1998-01-21T00:00:00", + "RequiredDate": "1998-02-04T00:00:00", + "ShippedDate": "1998-01-30T00:00:00", + "ShipVia": 1, + "Freight": "212.9800", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10852, + "CustomerID": "RATTC", + "EmployeeID": 8, + "OrderDate": "1998-01-26T00:00:00", + "RequiredDate": "1998-02-09T00:00:00", + "ShippedDate": "1998-01-30T00:00:00", + "ShipVia": 1, + "Freight": "174.0500", + "ShipName": "Rattlesnake Canyon Grocery", + "ShipAddress": "2817 Milton Dr.", + "ShipCity": "Albuquerque", + "ShipRegion": "NM", + "ShipPostalCode": "87110", + "ShipCountry": "USA" + }, { + "OrderID": 10857, + "CustomerID": "BERGS", + "EmployeeID": 8, + "OrderDate": "1998-01-28T00:00:00", + "RequiredDate": "1998-02-25T00:00:00", + "ShippedDate": "1998-02-06T00:00:00", + "ShipVia": 2, + "Freight": "188.8500", + "ShipName": "Berglunds snabbk\u00f6p", + "ShipAddress": "Berguvsv\u00e4gen 8", + "ShipCity": "Lule\u00e5", + "ShipRegion": null, + "ShipPostalCode": "S-958 22", + "ShipCountry": "Sweden" + }, { + "OrderID": 10862, + "CustomerID": "LEHMS", + "EmployeeID": 8, + "OrderDate": "1998-01-30T00:00:00", + "RequiredDate": "1998-03-13T00:00:00", + "ShippedDate": "1998-02-02T00:00:00", + "ShipVia": 2, + "Freight": "53.2300", + "ShipName": "Lehmanns Marktstand", + "ShipAddress": "Magazinweg 7", + "ShipCity": "Frankfurt a.M.", + "ShipRegion": null, + "ShipPostalCode": "60528", + "ShipCountry": "Germany" + }, { + "OrderID": 10883, + "CustomerID": "LONEP", + "EmployeeID": 8, + "OrderDate": "1998-02-12T00:00:00", + "RequiredDate": "1998-03-12T00:00:00", + "ShippedDate": "1998-02-20T00:00:00", + "ShipVia": 3, + "Freight": "0.5300", + "ShipName": "Lonesome Pine Restaurant", + "ShipAddress": "89 Chiaroscuro Rd.", + "ShipCity": "Portland", + "ShipRegion": "OR", + "ShipPostalCode": "97219", + "ShipCountry": "USA" + }, { + "OrderID": 10887, + "CustomerID": "GALED", + "EmployeeID": 8, + "OrderDate": "1998-02-13T00:00:00", + "RequiredDate": "1998-03-13T00:00:00", + "ShippedDate": "1998-02-16T00:00:00", + "ShipVia": 3, + "Freight": "1.2500", + "ShipName": "Galer\u00eda del gastron\u00f3mo", + "ShipAddress": "Rambla de Catalu\u00f1a, 23", + "ShipCity": "Barcelona", + "ShipRegion": null, + "ShipPostalCode": "8022", + "ShipCountry": "Spain" + }, { + "OrderID": 10932, + "CustomerID": "BONAP", + "EmployeeID": 8, + "OrderDate": "1998-03-06T00:00:00", + "RequiredDate": "1998-04-03T00:00:00", + "ShippedDate": "1998-03-24T00:00:00", + "ShipVia": 1, + "Freight": "134.6400", + "ShipName": "Bon app'", + "ShipAddress": "12, rue des Bouchers", + "ShipCity": "Marseille", + "ShipRegion": null, + "ShipPostalCode": "13008", + "ShipCountry": "France" + }, { + "OrderID": 10940, + "CustomerID": "BONAP", + "EmployeeID": 8, + "OrderDate": "1998-03-11T00:00:00", + "RequiredDate": "1998-04-08T00:00:00", + "ShippedDate": "1998-03-23T00:00:00", + "ShipVia": 3, + "Freight": "19.7700", + "ShipName": "Bon app'", + "ShipAddress": "12, rue des Bouchers", + "ShipCity": "Marseille", + "ShipRegion": null, + "ShipPostalCode": "13008", + "ShipCountry": "France" + }, { + "OrderID": 10955, + "CustomerID": "FOLKO", + "EmployeeID": 8, + "OrderDate": "1998-03-17T00:00:00", + "RequiredDate": "1998-04-14T00:00:00", + "ShippedDate": "1998-03-20T00:00:00", + "ShipVia": 2, + "Freight": "3.2600", + "ShipName": "Folk och f\u00e4 HB", + "ShipAddress": "\u00c5kergatan 24", + "ShipCity": "Br\u00e4cke", + "ShipRegion": null, + "ShipPostalCode": "S-844 67", + "ShipCountry": "Sweden" + }, { + "OrderID": 10957, + "CustomerID": "HILAA", + "EmployeeID": 8, + "OrderDate": "1998-03-18T00:00:00", + "RequiredDate": "1998-04-15T00:00:00", + "ShippedDate": "1998-03-27T00:00:00", + "ShipVia": 3, + "Freight": "105.3600", + "ShipName": "HILARION-Abastos", + "ShipAddress": "Carrera 22 con Ave. Carlos Soublette #8-35", + "ShipCity": "San Crist\u00f3bal", + "ShipRegion": "T\u00e1chira", + "ShipPostalCode": "5022", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10961, + "CustomerID": "QUEEN", + "EmployeeID": 8, + "OrderDate": "1998-03-19T00:00:00", + "RequiredDate": "1998-04-16T00:00:00", + "ShippedDate": "1998-03-30T00:00:00", + "ShipVia": 1, + "Freight": "104.4700", + "ShipName": "Queen Cozinha", + "ShipAddress": "Alameda dos Can\u00e0rios, 891", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05487-020", + "ShipCountry": "Brazil" + }, { + "OrderID": 10962, + "CustomerID": "QUICK", + "EmployeeID": 8, + "OrderDate": "1998-03-19T00:00:00", + "RequiredDate": "1998-04-16T00:00:00", + "ShippedDate": "1998-03-23T00:00:00", + "ShipVia": 2, + "Freight": "275.7900", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10977, + "CustomerID": "FOLKO", + "EmployeeID": 8, + "OrderDate": "1998-03-26T00:00:00", + "RequiredDate": "1998-04-23T00:00:00", + "ShippedDate": "1998-04-10T00:00:00", + "ShipVia": 3, + "Freight": "208.5000", + "ShipName": "Folk och f\u00e4 HB", + "ShipAddress": "\u00c5kergatan 24", + "ShipCity": "Br\u00e4cke", + "ShipRegion": null, + "ShipPostalCode": "S-844 67", + "ShipCountry": "Sweden" + }, { + "OrderID": 10979, + "CustomerID": "ERNSH", + "EmployeeID": 8, + "OrderDate": "1998-03-26T00:00:00", + "RequiredDate": "1998-04-23T00:00:00", + "ShippedDate": "1998-03-31T00:00:00", + "ShipVia": 2, + "Freight": "353.0700", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10986, + "CustomerID": "OCEAN", + "EmployeeID": 8, + "OrderDate": "1998-03-30T00:00:00", + "RequiredDate": "1998-04-27T00:00:00", + "ShippedDate": "1998-04-21T00:00:00", + "ShipVia": 2, + "Freight": "217.8600", + "ShipName": "Oc\u00e9ano Atl\u00e1ntico Ltda.", + "ShipAddress": "Ing. Gustavo Moncada 8585 Piso 20-A", + "ShipCity": "Buenos Aires", + "ShipRegion": null, + "ShipPostalCode": "1010", + "ShipCountry": "Argentina" + }, { + "OrderID": 10987, + "CustomerID": "EASTC", + "EmployeeID": 8, + "OrderDate": "1998-03-31T00:00:00", + "RequiredDate": "1998-04-28T00:00:00", + "ShippedDate": "1998-04-06T00:00:00", + "ShipVia": 1, + "Freight": "185.4800", + "ShipName": "Eastern Connection", + "ShipAddress": "35 King George", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "WX3 6FW", + "ShipCountry": "UK" + }, { + "OrderID": 10997, + "CustomerID": "LILAS", + "EmployeeID": 8, + "OrderDate": "1998-04-03T00:00:00", + "RequiredDate": "1998-05-15T00:00:00", + "ShippedDate": "1998-04-13T00:00:00", + "ShipVia": 2, + "Freight": "73.9100", + "ShipName": "LILA-Supermercado", + "ShipAddress": "Carrera 52 con Ave. Bol\u00edvar #65-98 Llano Largo", + "ShipCity": "Barquisimeto", + "ShipRegion": "Lara", + "ShipPostalCode": "3508", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10998, + "CustomerID": "WOLZA", + "EmployeeID": 8, + "OrderDate": "1998-04-03T00:00:00", + "RequiredDate": "1998-04-17T00:00:00", + "ShippedDate": "1998-04-17T00:00:00", + "ShipVia": 2, + "Freight": "20.3100", + "ShipName": "Wolski Zajazd", + "ShipAddress": "ul. Filtrowa 68", + "ShipCity": "Warszawa", + "ShipRegion": null, + "ShipPostalCode": "01-012", + "ShipCountry": "Poland" + }, { + "OrderID": 11007, + "CustomerID": "PRINI", + "EmployeeID": 8, + "OrderDate": "1998-04-08T00:00:00", + "RequiredDate": "1998-05-06T00:00:00", + "ShippedDate": "1998-04-13T00:00:00", + "ShipVia": 2, + "Freight": "202.2400", + "ShipName": "Princesa Isabel Vinhos", + "ShipAddress": "Estrada da sa\u00fade n. 58", + "ShipCity": "Lisboa", + "ShipRegion": null, + "ShipPostalCode": "1756", + "ShipCountry": "Portugal" + }, { + "OrderID": 11034, + "CustomerID": "OLDWO", + "EmployeeID": 8, + "OrderDate": "1998-04-20T00:00:00", + "RequiredDate": "1998-06-01T00:00:00", + "ShippedDate": "1998-04-27T00:00:00", + "ShipVia": 1, + "Freight": "40.3200", + "ShipName": "Old World Delicatessen", + "ShipAddress": "2743 Bering St.", + "ShipCity": "Anchorage", + "ShipRegion": "AK", + "ShipPostalCode": "99508", + "ShipCountry": "USA" + }, { + "OrderID": 11036, + "CustomerID": "DRACD", + "EmployeeID": 8, + "OrderDate": "1998-04-20T00:00:00", + "RequiredDate": "1998-05-18T00:00:00", + "ShippedDate": "1998-04-22T00:00:00", + "ShipVia": 3, + "Freight": "149.4700", + "ShipName": "Drachenblut Delikatessen", + "ShipAddress": "Walserweg 21", + "ShipCity": "Aachen", + "ShipRegion": null, + "ShipPostalCode": "52066", + "ShipCountry": "Germany" + }, { + "OrderID": 11046, + "CustomerID": "WANDK", + "EmployeeID": 8, + "OrderDate": "1998-04-23T00:00:00", + "RequiredDate": "1998-05-21T00:00:00", + "ShippedDate": "1998-04-24T00:00:00", + "ShipVia": 2, + "Freight": "71.6400", + "ShipName": "Die Wandernde Kuh", + "ShipAddress": "Adenauerallee 900", + "ShipCity": "Stuttgart", + "ShipRegion": null, + "ShipPostalCode": "70563", + "ShipCountry": "Germany" + }, { + "OrderID": 11050, + "CustomerID": "FOLKO", + "EmployeeID": 8, + "OrderDate": "1998-04-27T00:00:00", + "RequiredDate": "1998-05-25T00:00:00", + "ShippedDate": "1998-05-05T00:00:00", + "ShipVia": 2, + "Freight": "59.4100", + "ShipName": "Folk och f\u00e4 HB", + "ShipAddress": "\u00c5kergatan 24", + "ShipCity": "Br\u00e4cke", + "ShipRegion": null, + "ShipPostalCode": "S-844 67", + "ShipCountry": "Sweden" + }, { + "OrderID": 11054, + "CustomerID": "CACTU", + "EmployeeID": 8, + "OrderDate": "1998-04-28T00:00:00", + "RequiredDate": "1998-05-26T00:00:00", + "ShippedDate": null, + "ShipVia": 1, + "Freight": "0.3300", + "ShipName": "Cactus Comidas para llevar", + "ShipAddress": "Cerrito 333", + "ShipCity": "Buenos Aires", + "ShipRegion": null, + "ShipPostalCode": "1010", + "ShipCountry": "Argentina" + }, { + "OrderID": 11056, + "CustomerID": "EASTC", + "EmployeeID": 8, + "OrderDate": "1998-04-28T00:00:00", + "RequiredDate": "1998-05-12T00:00:00", + "ShippedDate": "1998-05-01T00:00:00", + "ShipVia": 2, + "Freight": "278.9600", + "ShipName": "Eastern Connection", + "ShipAddress": "35 King George", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "WX3 6FW", + "ShipCountry": "UK" + }, { + "OrderID": 11065, + "CustomerID": "LILAS", + "EmployeeID": 8, + "OrderDate": "1998-05-01T00:00:00", + "RequiredDate": "1998-05-29T00:00:00", + "ShippedDate": null, + "ShipVia": 1, + "Freight": "12.9100", + "ShipName": "LILA-Supermercado", + "ShipAddress": "Carrera 52 con Ave. Bol\u00edvar #65-98 Llano Largo", + "ShipCity": "Barquisimeto", + "ShipRegion": "Lara", + "ShipPostalCode": "3508", + "ShipCountry": "Venezuela" + }, { + "OrderID": 11068, + "CustomerID": "QUEEN", + "EmployeeID": 8, + "OrderDate": "1998-05-04T00:00:00", + "RequiredDate": "1998-06-01T00:00:00", + "ShippedDate": null, + "ShipVia": 2, + "Freight": "81.7500", + "ShipName": "Queen Cozinha", + "ShipAddress": "Alameda dos Can\u00e0rios, 891", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05487-020", + "ShipCountry": "Brazil" + }, { + "OrderID": 11075, + "CustomerID": "RICSU", + "EmployeeID": 8, + "OrderDate": "1998-05-06T00:00:00", + "RequiredDate": "1998-06-03T00:00:00", + "ShippedDate": null, + "ShipVia": 2, + "Freight": "6.1900", + "ShipName": "Richter Supermarkt", + "ShipAddress": "Starenweg 5", + "ShipCity": "Gen\u00e8ve", + "ShipRegion": null, + "ShipPostalCode": "1204", + "ShipCountry": "Switzerland" + }], + "EmployeeID": 8, + "LastName": "Callahan", + "FirstName": "Laura", + "Title": "Inside Sales Coordinator", + "TitleOfCourtesy": "Ms.", + "BirthDate": "1958-01-09T00:00:00", + "HireDate": "1994-03-05T00:00:00", + "Address": "4726 - 11th Ave. N.E.", + "City": "Seattle", + "Region": "WA", + "PostalCode": "98105", + "Country": "USA", + "HomePhone": "(206) 555-1189", + "Extension": "2344", + "Notes": "Laura received a BA in psychology from the University of Washington. She has also completed a course in business French. She reads and writes French.", + "ReportsTo": 2, + "PhotoPath": "http://accweb/emmployees/davolio.bmp" + }, { + "Orders": [{ + "OrderID": 10255, + "CustomerID": "RICSU", + "EmployeeID": 9, + "OrderDate": "1996-07-12T00:00:00", + "RequiredDate": "1996-08-09T00:00:00", + "ShippedDate": "1996-07-15T00:00:00", + "ShipVia": 3, + "Freight": "148.3300", + "ShipName": "Richter Supermarkt", + "ShipAddress": "Starenweg 5", + "ShipCity": "Gen\u00e8ve", + "ShipRegion": null, + "ShipPostalCode": "1204", + "ShipCountry": "Switzerland" + }, { + "OrderID": 10263, + "CustomerID": "ERNSH", + "EmployeeID": 9, + "OrderDate": "1996-07-23T00:00:00", + "RequiredDate": "1996-08-20T00:00:00", + "ShippedDate": "1996-07-31T00:00:00", + "ShipVia": 3, + "Freight": "146.0600", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10324, + "CustomerID": "SAVEA", + "EmployeeID": 9, + "OrderDate": "1996-10-08T00:00:00", + "RequiredDate": "1996-11-05T00:00:00", + "ShippedDate": "1996-10-10T00:00:00", + "ShipVia": 1, + "Freight": "214.2700", + "ShipName": "Save-a-lot Markets", + "ShipAddress": "187 Suffolk Ln.", + "ShipCity": "Boise", + "ShipRegion": "ID", + "ShipPostalCode": "83720", + "ShipCountry": "USA" + }, { + "OrderID": 10331, + "CustomerID": "BONAP", + "EmployeeID": 9, + "OrderDate": "1996-10-16T00:00:00", + "RequiredDate": "1996-11-27T00:00:00", + "ShippedDate": "1996-10-21T00:00:00", + "ShipVia": 1, + "Freight": "10.1900", + "ShipName": "Bon app'", + "ShipAddress": "12, rue des Bouchers", + "ShipCity": "Marseille", + "ShipRegion": null, + "ShipPostalCode": "13008", + "ShipCountry": "France" + }, { + "OrderID": 10386, + "CustomerID": "FAMIA", + "EmployeeID": 9, + "OrderDate": "1996-12-18T00:00:00", + "RequiredDate": "1997-01-01T00:00:00", + "ShippedDate": "1996-12-25T00:00:00", + "ShipVia": 3, + "Freight": "13.9900", + "ShipName": "Familia Arquibaldo", + "ShipAddress": "Rua Or\u00f3s, 92", + "ShipCity": "Sao Paulo", + "ShipRegion": "SP", + "ShipPostalCode": "05442-030", + "ShipCountry": "Brazil" + }, { + "OrderID": 10411, + "CustomerID": "BOTTM", + "EmployeeID": 9, + "OrderDate": "1997-01-10T00:00:00", + "RequiredDate": "1997-02-07T00:00:00", + "ShippedDate": "1997-01-21T00:00:00", + "ShipVia": 3, + "Freight": "23.6500", + "ShipName": "Bottom-Dollar Markets", + "ShipAddress": "23 Tsawassen Blvd.", + "ShipCity": "Tsawassen", + "ShipRegion": "BC", + "ShipPostalCode": "T2F 8M4", + "ShipCountry": "Canada" + }, { + "OrderID": 10475, + "CustomerID": "SUPRD", + "EmployeeID": 9, + "OrderDate": "1997-03-14T00:00:00", + "RequiredDate": "1997-04-11T00:00:00", + "ShippedDate": "1997-04-04T00:00:00", + "ShipVia": 1, + "Freight": "68.5200", + "ShipName": "Supr\u00eames d\u00e9lices", + "ShipAddress": "Boulevard Tirou, 255", + "ShipCity": "Charleroi", + "ShipRegion": null, + "ShipPostalCode": "B-6000", + "ShipCountry": "Belgium" + }, { + "OrderID": 10501, + "CustomerID": "BLAUS", + "EmployeeID": 9, + "OrderDate": "1997-04-09T00:00:00", + "RequiredDate": "1997-05-07T00:00:00", + "ShippedDate": "1997-04-16T00:00:00", + "ShipVia": 3, + "Freight": "8.8500", + "ShipName": "Blauer See Delikatessen", + "ShipAddress": "Forsterstr. 57", + "ShipCity": "Mannheim", + "ShipRegion": null, + "ShipPostalCode": "68306", + "ShipCountry": "Germany" + }, { + "OrderID": 10506, + "CustomerID": "KOENE", + "EmployeeID": 9, + "OrderDate": "1997-04-15T00:00:00", + "RequiredDate": "1997-05-13T00:00:00", + "ShippedDate": "1997-05-02T00:00:00", + "ShipVia": 2, + "Freight": "21.1900", + "ShipName": "K\u00f6niglich Essen", + "ShipAddress": "Maubelstr. 90", + "ShipCity": "Brandenburg", + "ShipRegion": null, + "ShipPostalCode": "14776", + "ShipCountry": "Germany" + }, { + "OrderID": 10538, + "CustomerID": "BSBEV", + "EmployeeID": 9, + "OrderDate": "1997-05-15T00:00:00", + "RequiredDate": "1997-06-12T00:00:00", + "ShippedDate": "1997-05-16T00:00:00", + "ShipVia": 3, + "Freight": "4.8700", + "ShipName": "B's Beverages", + "ShipAddress": "Fauntleroy Circus", + "ShipCity": "London", + "ShipRegion": null, + "ShipPostalCode": "EC2 5NT", + "ShipCountry": "UK" + }, { + "OrderID": 10557, + "CustomerID": "LEHMS", + "EmployeeID": 9, + "OrderDate": "1997-06-03T00:00:00", + "RequiredDate": "1997-06-17T00:00:00", + "ShippedDate": "1997-06-06T00:00:00", + "ShipVia": 2, + "Freight": "96.7200", + "ShipName": "Lehmanns Marktstand", + "ShipAddress": "Magazinweg 7", + "ShipCity": "Frankfurt a.M.", + "ShipRegion": null, + "ShipPostalCode": "60528", + "ShipCountry": "Germany" + }, { + "OrderID": 10566, + "CustomerID": "BLONP", + "EmployeeID": 9, + "OrderDate": "1997-06-12T00:00:00", + "RequiredDate": "1997-07-10T00:00:00", + "ShippedDate": "1997-06-18T00:00:00", + "ShipVia": 1, + "Freight": "88.4000", + "ShipName": "Blondel p\u00e8re et fils", + "ShipAddress": "24, place Kl\u00e9ber", + "ShipCity": "Strasbourg", + "ShipRegion": null, + "ShipPostalCode": "67000", + "ShipCountry": "France" + }, { + "OrderID": 10577, + "CustomerID": "TRAIH", + "EmployeeID": 9, + "OrderDate": "1997-06-23T00:00:00", + "RequiredDate": "1997-08-04T00:00:00", + "ShippedDate": "1997-06-30T00:00:00", + "ShipVia": 2, + "Freight": "25.4100", + "ShipName": "Trail's Head Gourmet Provisioners", + "ShipAddress": "722 DaVinci Blvd.", + "ShipCity": "Kirkland", + "ShipRegion": "WA", + "ShipPostalCode": "98034", + "ShipCountry": "USA" + }, { + "OrderID": 10586, + "CustomerID": "REGGC", + "EmployeeID": 9, + "OrderDate": "1997-07-02T00:00:00", + "RequiredDate": "1997-07-30T00:00:00", + "ShippedDate": "1997-07-09T00:00:00", + "ShipVia": 1, + "Freight": "0.4800", + "ShipName": "Reggiani Caseifici", + "ShipAddress": "Strada Provinciale 124", + "ShipCity": "Reggio Emilia", + "ShipRegion": null, + "ShipPostalCode": "42100", + "ShipCountry": "Italy" + }, { + "OrderID": 10646, + "CustomerID": "HUNGO", + "EmployeeID": 9, + "OrderDate": "1997-08-27T00:00:00", + "RequiredDate": "1997-10-08T00:00:00", + "ShippedDate": "1997-09-03T00:00:00", + "ShipVia": 3, + "Freight": "142.3300", + "ShipName": "Hungry Owl All-Night Grocers", + "ShipAddress": "8 Johnstown Road", + "ShipCity": "Cork", + "ShipRegion": "Co. Cork", + "ShipPostalCode": null, + "ShipCountry": "Ireland" + }, { + "OrderID": 10672, + "CustomerID": "BERGS", + "EmployeeID": 9, + "OrderDate": "1997-09-17T00:00:00", + "RequiredDate": "1997-10-01T00:00:00", + "ShippedDate": "1997-09-26T00:00:00", + "ShipVia": 2, + "Freight": "95.7500", + "ShipName": "Berglunds snabbk\u00f6p", + "ShipAddress": "Berguvsv\u00e4gen 8", + "ShipCity": "Lule\u00e5", + "ShipRegion": null, + "ShipPostalCode": "S-958 22", + "ShipCountry": "Sweden" + }, { + "OrderID": 10687, + "CustomerID": "HUNGO", + "EmployeeID": 9, + "OrderDate": "1997-09-30T00:00:00", + "RequiredDate": "1997-10-28T00:00:00", + "ShippedDate": "1997-10-30T00:00:00", + "ShipVia": 2, + "Freight": "296.4300", + "ShipName": "Hungry Owl All-Night Grocers", + "ShipAddress": "8 Johnstown Road", + "ShipCity": "Cork", + "ShipRegion": "Co. Cork", + "ShipPostalCode": null, + "ShipCountry": "Ireland" + }, { + "OrderID": 10705, + "CustomerID": "HILAA", + "EmployeeID": 9, + "OrderDate": "1997-10-15T00:00:00", + "RequiredDate": "1997-11-12T00:00:00", + "ShippedDate": "1997-11-18T00:00:00", + "ShipVia": 2, + "Freight": "3.5200", + "ShipName": "HILARION-Abastos", + "ShipAddress": "Carrera 22 con Ave. Carlos Soublette #8-35", + "ShipCity": "San Crist\u00f3bal", + "ShipRegion": "T\u00e1chira", + "ShipPostalCode": "5022", + "ShipCountry": "Venezuela" + }, { + "OrderID": 10736, + "CustomerID": "HUNGO", + "EmployeeID": 9, + "OrderDate": "1997-11-11T00:00:00", + "RequiredDate": "1997-12-09T00:00:00", + "ShippedDate": "1997-11-21T00:00:00", + "ShipVia": 2, + "Freight": "44.1000", + "ShipName": "Hungry Owl All-Night Grocers", + "ShipAddress": "8 Johnstown Road", + "ShipCity": "Cork", + "ShipRegion": "Co. Cork", + "ShipPostalCode": null, + "ShipCountry": "Ireland" + }, { + "OrderID": 10745, + "CustomerID": "QUICK", + "EmployeeID": 9, + "OrderDate": "1997-11-18T00:00:00", + "RequiredDate": "1997-12-16T00:00:00", + "ShippedDate": "1997-11-27T00:00:00", + "ShipVia": 1, + "Freight": "3.5200", + "ShipName": "QUICK-Stop", + "ShipAddress": "Taucherstra\u00dfe 10", + "ShipCity": "Cunewalde", + "ShipRegion": null, + "ShipPostalCode": "01307", + "ShipCountry": "Germany" + }, { + "OrderID": 10750, + "CustomerID": "WARTH", + "EmployeeID": 9, + "OrderDate": "1997-11-21T00:00:00", + "RequiredDate": "1997-12-19T00:00:00", + "ShippedDate": "1997-11-24T00:00:00", + "ShipVia": 1, + "Freight": "79.3000", + "ShipName": "Wartian Herkku", + "ShipAddress": "Torikatu 38", + "ShipCity": "Oulu", + "ShipRegion": null, + "ShipPostalCode": "90110", + "ShipCountry": "Finland" + }, { + "OrderID": 10771, + "CustomerID": "ERNSH", + "EmployeeID": 9, + "OrderDate": "1997-12-10T00:00:00", + "RequiredDate": "1998-01-07T00:00:00", + "ShippedDate": "1998-01-02T00:00:00", + "ShipVia": 2, + "Freight": "11.1900", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 10782, + "CustomerID": "CACTU", + "EmployeeID": 9, + "OrderDate": "1997-12-17T00:00:00", + "RequiredDate": "1998-01-14T00:00:00", + "ShippedDate": "1997-12-22T00:00:00", + "ShipVia": 3, + "Freight": "1.1000", + "ShipName": "Cactus Comidas para llevar", + "ShipAddress": "Cerrito 333", + "ShipCity": "Buenos Aires", + "ShipRegion": null, + "ShipPostalCode": "1010", + "ShipCountry": "Argentina" + }, { + "OrderID": 10799, + "CustomerID": "KOENE", + "EmployeeID": 9, + "OrderDate": "1997-12-26T00:00:00", + "RequiredDate": "1998-02-06T00:00:00", + "ShippedDate": "1998-01-05T00:00:00", + "ShipVia": 3, + "Freight": "30.7600", + "ShipName": "K\u00f6niglich Essen", + "ShipAddress": "Maubelstr. 90", + "ShipCity": "Brandenburg", + "ShipRegion": null, + "ShipPostalCode": "14776", + "ShipCountry": "Germany" + }, { + "OrderID": 10828, + "CustomerID": "RANCH", + "EmployeeID": 9, + "OrderDate": "1998-01-13T00:00:00", + "RequiredDate": "1998-01-27T00:00:00", + "ShippedDate": "1998-02-04T00:00:00", + "ShipVia": 1, + "Freight": "90.8500", + "ShipName": "Rancho grande", + "ShipAddress": "Av. del Libertador 900", + "ShipCity": "Buenos Aires", + "ShipRegion": null, + "ShipPostalCode": "1010", + "ShipCountry": "Argentina" + }, { + "OrderID": 10829, + "CustomerID": "ISLAT", + "EmployeeID": 9, + "OrderDate": "1998-01-13T00:00:00", + "RequiredDate": "1998-02-10T00:00:00", + "ShippedDate": "1998-01-23T00:00:00", + "ShipVia": 1, + "Freight": "154.7200", + "ShipName": "Island Trading", + "ShipAddress": "Garden House Crowther Way", + "ShipCity": "Cowes", + "ShipRegion": "Isle of Wight", + "ShipPostalCode": "PO31 7PJ", + "ShipCountry": "UK" + }, { + "OrderID": 10837, + "CustomerID": "BERGS", + "EmployeeID": 9, + "OrderDate": "1998-01-16T00:00:00", + "RequiredDate": "1998-02-13T00:00:00", + "ShippedDate": "1998-01-23T00:00:00", + "ShipVia": 3, + "Freight": "13.3200", + "ShipName": "Berglunds snabbk\u00f6p", + "ShipAddress": "Berguvsv\u00e4gen 8", + "ShipCity": "Lule\u00e5", + "ShipRegion": null, + "ShipPostalCode": "S-958 22", + "ShipCountry": "Sweden" + }, { + "OrderID": 10849, + "CustomerID": "KOENE", + "EmployeeID": 9, + "OrderDate": "1998-01-23T00:00:00", + "RequiredDate": "1998-02-20T00:00:00", + "ShippedDate": "1998-01-30T00:00:00", + "ShipVia": 2, + "Freight": "0.5600", + "ShipName": "K\u00f6niglich Essen", + "ShipAddress": "Maubelstr. 90", + "ShipCity": "Brandenburg", + "ShipRegion": null, + "ShipPostalCode": "14776", + "ShipCountry": "Germany" + }, { + "OrderID": 10853, + "CustomerID": "BLAUS", + "EmployeeID": 9, + "OrderDate": "1998-01-27T00:00:00", + "RequiredDate": "1998-02-24T00:00:00", + "ShippedDate": "1998-02-03T00:00:00", + "ShipVia": 2, + "Freight": "53.8300", + "ShipName": "Blauer See Delikatessen", + "ShipAddress": "Forsterstr. 57", + "ShipCity": "Mannheim", + "ShipRegion": null, + "ShipPostalCode": "68306", + "ShipCountry": "Germany" + }, { + "OrderID": 10871, + "CustomerID": "BONAP", + "EmployeeID": 9, + "OrderDate": "1998-02-05T00:00:00", + "RequiredDate": "1998-03-05T00:00:00", + "ShippedDate": "1998-02-10T00:00:00", + "ShipVia": 2, + "Freight": "112.2700", + "ShipName": "Bon app'", + "ShipAddress": "12, rue des Bouchers", + "ShipCity": "Marseille", + "ShipRegion": null, + "ShipPostalCode": "13008", + "ShipCountry": "France" + }, { + "OrderID": 10889, + "CustomerID": "RATTC", + "EmployeeID": 9, + "OrderDate": "1998-02-16T00:00:00", + "RequiredDate": "1998-03-16T00:00:00", + "ShippedDate": "1998-02-23T00:00:00", + "ShipVia": 3, + "Freight": "280.6100", + "ShipName": "Rattlesnake Canyon Grocery", + "ShipAddress": "2817 Milton Dr.", + "ShipCity": "Albuquerque", + "ShipRegion": "NM", + "ShipPostalCode": "87110", + "ShipCountry": "USA" + }, { + "OrderID": 10893, + "CustomerID": "KOENE", + "EmployeeID": 9, + "OrderDate": "1998-02-18T00:00:00", + "RequiredDate": "1998-03-18T00:00:00", + "ShippedDate": "1998-02-20T00:00:00", + "ShipVia": 2, + "Freight": "77.7800", + "ShipName": "K\u00f6niglich Essen", + "ShipAddress": "Maubelstr. 90", + "ShipCity": "Brandenburg", + "ShipRegion": null, + "ShipPostalCode": "14776", + "ShipCountry": "Germany" + }, { + "OrderID": 10905, + "CustomerID": "WELLI", + "EmployeeID": 9, + "OrderDate": "1998-02-24T00:00:00", + "RequiredDate": "1998-03-24T00:00:00", + "ShippedDate": "1998-03-06T00:00:00", + "ShipVia": 2, + "Freight": "13.7200", + "ShipName": "Wellington Importadora", + "ShipAddress": "Rua do Mercado, 12", + "ShipCity": "Resende", + "ShipRegion": "SP", + "ShipPostalCode": "08737-363", + "ShipCountry": "Brazil" + }, { + "OrderID": 10942, + "CustomerID": "REGGC", + "EmployeeID": 9, + "OrderDate": "1998-03-11T00:00:00", + "RequiredDate": "1998-04-08T00:00:00", + "ShippedDate": "1998-03-18T00:00:00", + "ShipVia": 3, + "Freight": "17.9500", + "ShipName": "Reggiani Caseifici", + "ShipAddress": "Strada Provinciale 124", + "ShipCity": "Reggio Emilia", + "ShipRegion": null, + "ShipPostalCode": "42100", + "ShipCountry": "Italy" + }, { + "OrderID": 10951, + "CustomerID": "RICSU", + "EmployeeID": 9, + "OrderDate": "1998-03-16T00:00:00", + "RequiredDate": "1998-04-27T00:00:00", + "ShippedDate": "1998-04-07T00:00:00", + "ShipVia": 2, + "Freight": "30.8500", + "ShipName": "Richter Supermarkt", + "ShipAddress": "Starenweg 5", + "ShipCity": "Gen\u00e8ve", + "ShipRegion": null, + "ShipPostalCode": "1204", + "ShipCountry": "Switzerland" + }, { + "OrderID": 10953, + "CustomerID": "AROUT", + "EmployeeID": 9, + "OrderDate": "1998-03-16T00:00:00", + "RequiredDate": "1998-03-30T00:00:00", + "ShippedDate": "1998-03-25T00:00:00", + "ShipVia": 2, + "Freight": "23.7200", + "ShipName": "Around the Horn", + "ShipAddress": "Brook Farm Stratford St. Mary", + "ShipCity": "Colchester", + "ShipRegion": "Essex", + "ShipPostalCode": "CO7 6JX", + "ShipCountry": "UK" + }, { + "OrderID": 10963, + "CustomerID": "FURIB", + "EmployeeID": 9, + "OrderDate": "1998-03-19T00:00:00", + "RequiredDate": "1998-04-16T00:00:00", + "ShippedDate": "1998-03-26T00:00:00", + "ShipVia": 3, + "Freight": "2.7000", + "ShipName": "Furia Bacalhau e Frutos do Mar", + "ShipAddress": "Jardim das rosas n. 32", + "ShipCity": "Lisboa", + "ShipRegion": null, + "ShipPostalCode": "1675", + "ShipCountry": "Portugal" + }, { + "OrderID": 10970, + "CustomerID": "BOLID", + "EmployeeID": 9, + "OrderDate": "1998-03-24T00:00:00", + "RequiredDate": "1998-04-07T00:00:00", + "ShippedDate": "1998-04-24T00:00:00", + "ShipVia": 1, + "Freight": "16.1600", + "ShipName": "B\u00f3lido Comidas preparadas", + "ShipAddress": "C/ Araquil, 67", + "ShipCity": "Madrid", + "ShipRegion": null, + "ShipPostalCode": "28023", + "ShipCountry": "Spain" + }, { + "OrderID": 10978, + "CustomerID": "MAISD", + "EmployeeID": 9, + "OrderDate": "1998-03-26T00:00:00", + "RequiredDate": "1998-04-23T00:00:00", + "ShippedDate": "1998-04-23T00:00:00", + "ShipVia": 2, + "Freight": "32.8200", + "ShipName": "Maison Dewey", + "ShipAddress": "Rue Joseph-Bens 532", + "ShipCity": "Bruxelles", + "ShipRegion": null, + "ShipPostalCode": "B-1180", + "ShipCountry": "Belgium" + }, { + "OrderID": 11016, + "CustomerID": "AROUT", + "EmployeeID": 9, + "OrderDate": "1998-04-10T00:00:00", + "RequiredDate": "1998-05-08T00:00:00", + "ShippedDate": "1998-04-13T00:00:00", + "ShipVia": 2, + "Freight": "33.8000", + "ShipName": "Around the Horn", + "ShipAddress": "Brook Farm Stratford St. Mary", + "ShipCity": "Colchester", + "ShipRegion": "Essex", + "ShipPostalCode": "CO7 6JX", + "ShipCountry": "UK" + }, { + "OrderID": 11017, + "CustomerID": "ERNSH", + "EmployeeID": 9, + "OrderDate": "1998-04-13T00:00:00", + "RequiredDate": "1998-05-11T00:00:00", + "ShippedDate": "1998-04-20T00:00:00", + "ShipVia": 2, + "Freight": "754.2600", + "ShipName": "Ernst Handel", + "ShipAddress": "Kirchgasse 6", + "ShipCity": "Graz", + "ShipRegion": null, + "ShipPostalCode": "8010", + "ShipCountry": "Austria" + }, { + "OrderID": 11022, + "CustomerID": "HANAR", + "EmployeeID": 9, + "OrderDate": "1998-04-14T00:00:00", + "RequiredDate": "1998-05-12T00:00:00", + "ShippedDate": "1998-05-04T00:00:00", + "ShipVia": 2, + "Freight": "6.2700", + "ShipName": "Hanari Carnes", + "ShipAddress": "Rua do Pa\u00e7o, 67", + "ShipCity": "Rio de Janeiro", + "ShipRegion": "RJ", + "ShipPostalCode": "05454-876", + "ShipCountry": "Brazil" + }, { + "OrderID": 11058, + "CustomerID": "BLAUS", + "EmployeeID": 9, + "OrderDate": "1998-04-29T00:00:00", + "RequiredDate": "1998-05-27T00:00:00", + "ShippedDate": null, + "ShipVia": 3, + "Freight": "31.1400", + "ShipName": "Blauer See Delikatessen", + "ShipAddress": "Forsterstr. 57", + "ShipCity": "Mannheim", + "ShipRegion": null, + "ShipPostalCode": "68306", + "ShipCountry": "Germany" + }], + "EmployeeID": 9, + "LastName": "Dodsworth", + "FirstName": "Anne", + "Title": "Sales Representative", + "TitleOfCourtesy": "Ms.", + "BirthDate": "1966-01-27T00:00:00", + "HireDate": "1994-11-15T00:00:00", + "Address": "7 Houndstooth Rd.", + "City": "London", + "Region": null, + "PostalCode": "WG2 7LT", + "Country": "UK", + "HomePhone": "(71) 555-4444", + "Extension": "452", + "Notes": "Anne has a BA degree in English from St. Lawrence College. She is fluent in French and German.", + "ReportsTo": 5, + "PhotoPath": "http://accweb/emmployees/davolio.bmp" + }]; From 8cfce6a1d8cc8d8352bdcaf3657f822bf2d40065 Mon Sep 17 00:00:00 2001 From: MKirova Date: Mon, 29 Aug 2022 12:59:58 +0300 Subject: [PATCH 108/149] chore(*): Update the other cell types templates. --- .../grids/grid/expandable-cell.component.html | 105 ++++++++++++----- .../grids/tree-grid/tree-cell.component.html | 111 ++++++++++-------- .../lib/grids/tree-grid/tree-grid.module.ts | 6 +- 3 files changed, 148 insertions(+), 74 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/expandable-cell.component.html b/projects/igniteui-angular/src/lib/grids/grid/expandable-cell.component.html index fb48cb47aa6..151246d0927 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/expandable-cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/grid/expandable-cell.component.html @@ -50,50 +50,66 @@ value ? value : (column.header || column.field) }}
- - - - + + + + - - + + - - + + - - - + + + - - + + - - + + - + {{ currencyCodeSymbol }} - - {{ currencyCodeSymbol }} + + {{ currencyCodeSymbol }} - - + + {{ editValue | percent:column.pipeArgs.digitsInfo:grid.locale }} @@ -110,9 +126,44 @@ + + error +
+
+ +
+
+
+ expand_more chevron_right + + +
+ {{grid.resourceStrings.igx_grid_required_validation_error}} +
+
+ {{grid.resourceStrings.igx_grid_min_length_validation_error | igxStringReplace:'{0}':formGroup.get(column.field).errors.minlength.requiredLength }} +
+
+ {{grid.resourceStrings.igx_grid_max_length_validation_error | igxStringReplace:'{0}':formGroup.get(column.field).errors.maxlength.requiredLength }} +
+
+ {{grid.resourceStrings.igx_grid_min_validation_error | igxStringReplace:'{0}':formGroup.get(column.field).errors.min.min }} +
+
+ {{grid.resourceStrings.igx_grid_max_validation_error | igxStringReplace:'{0}':formGroup.get(column.field).errors.max.max }} +
+
+ {{grid.resourceStrings.igx_grid_email_validation_error }} +
+
+ {{grid.resourceStrings.igx_grid_pattern_validation_error}} +
+
diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.html b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.html index 36829aa672d..6e070e996d2 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.html @@ -71,32 +71,29 @@ }}
- - - - - - - + + - - + + + + + + + - + - - + + - - + + - + {{ currencyCodeSymbol }} - - {{ currencyCodeSymbol }} + + {{ currencyCodeSymbol }} - - + + {{ editValue | percent:column.pipeArgs.digitsInfo:grid.locale }} @@ -193,9 +178,43 @@
+ + error +
+
+ +
+
+
+ expand_more chevron_right + +
+ {{grid.resourceStrings.igx_grid_required_validation_error}} +
+
+ {{grid.resourceStrings.igx_grid_min_length_validation_error | igxStringReplace:'{0}':formGroup.get(column.field).errors.minlength.requiredLength }} +
+
+ {{grid.resourceStrings.igx_grid_max_length_validation_error | igxStringReplace:'{0}':formGroup.get(column.field).errors.maxlength.requiredLength }} +
+
+ {{grid.resourceStrings.igx_grid_min_validation_error | igxStringReplace:'{0}':formGroup.get(column.field).errors.min.min }} +
+
+ {{grid.resourceStrings.igx_grid_max_validation_error | igxStringReplace:'{0}':formGroup.get(column.field).errors.max.max }} +
+
+ {{grid.resourceStrings.igx_grid_email_validation_error }} +
+
+ {{grid.resourceStrings.igx_grid_pattern_validation_error}} +
+
\ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.module.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.module.ts index 9333642d432..10a249ba522 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.module.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.module.ts @@ -10,6 +10,8 @@ import { IgxTreeGridSummaryPipe } from './tree-grid.summary.pipe'; import { IgxRowLoadingIndicatorTemplateDirective } from './tree-grid.directives'; import { IgxTreeGridGroupingPipe } from './tree-grid.grouping.pipe'; import { IgxTreeGridGroupByAreaComponent } from '../grouping/tree-grid-group-by-area.component'; +import { ReactiveFormsModule } from '@angular/forms'; +import { IgxTooltipModule } from '../../directives/tooltip'; /** * @hidden */ @@ -42,7 +44,9 @@ import { IgxTreeGridGroupByAreaComponent } from '../grouping/tree-grid-group-by- IgxTreeGridAddRowPipe ], imports: [ - IgxGridCommonModule + IgxGridCommonModule, + IgxTooltipModule, + ReactiveFormsModule ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) From 2d2a1f264dcce967e7ee6bd59b621ed089fd79a2 Mon Sep 17 00:00:00 2001 From: MKirova Date: Mon, 29 Aug 2022 13:08:58 +0300 Subject: [PATCH 109/149] chore(*): Fix imports for grid module. --- projects/igniteui-angular/src/lib/grids/grid/grid.module.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.module.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.module.ts index 7f3e20a7cea..eb0e9ba0470 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.module.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.module.ts @@ -17,6 +17,8 @@ import { IgxGridSummaryPipe } from './grid.summary.pipe'; import { IgxGridDetailsPipe } from './grid.details.pipe'; import { IgxGridExpandableCellComponent } from './expandable-cell.component'; import { IgxGridGroupByAreaComponent } from '../grouping/grid-group-by-area.component'; +import { IgxTooltipModule } from '../../directives/tooltip'; +import { ReactiveFormsModule } from '@angular/forms'; /** * @hidden */ @@ -54,6 +56,8 @@ import { IgxGridGroupByAreaComponent } from '../grouping/grid-group-by-area.comp ], imports: [ IgxGridCommonModule, + IgxTooltipModule, + ReactiveFormsModule ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) From 3042d386a3882f55406fe2f4ab1d99733bdafc7b Mon Sep 17 00:00:00 2001 From: MKirova Date: Mon, 29 Aug 2022 14:36:11 +0300 Subject: [PATCH 110/149] chore(*): Pass validator array from row island to child grid instances. --- .../grids/hierarchical-grid/hierarchical-grid-base.directive.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid-base.directive.ts index d69f8727956..beb1e66f69d 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid-base.directive.ts @@ -268,6 +268,7 @@ export abstract class IgxHierarchicalGridBaseDirective extends IgxGridBaseDirect ref.instance[propName] = col[propName].constructor; } }); + ref.instance.validators = col.validators; return ref; } From e32ec5f28b8ea0870799ea6b5f3140dfac003786 Mon Sep 17 00:00:00 2001 From: MKirova Date: Mon, 29 Aug 2022 15:34:11 +0300 Subject: [PATCH 111/149] chore(*): Mark updated cells/row as touched. --- .../src/lib/grids/grid-base.directive.ts | 2 ++ .../lib/grids/grid/grid-validation.service.ts | 8 +++---- .../grid-validation.sample.component.html | 2 ++ .../grid-validation.sample.component.ts | 21 +++++++++++++++++++ 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index 321e6e31bf4..d926cb94e53 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -4528,6 +4528,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements const cell = new IgxCell(id, index, col, rowData[col.field], value, rowData, this as any); this.gridAPI.update_cell(cell); + this.validation.markAsTouched(rowSelector, column); this.cdr.detectChanges(); } } @@ -4558,6 +4559,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements } const row = new IgxEditRow(rowSelector, -1, this.gridAPI.getRowData(rowSelector), this as any); this.gridAPI.update_row(row, value); + this.validation.markAsTouched(rowSelector); // TODO: fix for #5934 and probably break for #5763 // consider adding of third optional boolean parameter in updateRow. diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index e344afac80a..99aa2088205 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -126,14 +126,14 @@ export class IgxGridValidationService { * @hidden * @internal */ - public markAsTouched(rowId: any) { + public markAsTouched(rowId: any, field?: string) { const rowGroup = this.getFormGroup(rowId); if (!rowGroup) return; rowGroup.markAsTouched(); for (const col of this.grid.columns) { - const field = col.field; - const control = rowGroup?.get(field); - control.markAsTouched(); + if(!field || (field && col.field === field)) { + rowGroup?.get(col.field).markAsTouched(); + } } } diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index 2cfe4ebafb3..b5750916977 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -45,6 +45,8 @@

Grid with transactions

+ + diff --git a/src/app/grid-validation/grid-validation.sample.component.ts b/src/app/grid-validation/grid-validation.sample.component.ts index 5a8dbe2eb92..939ca88e8c5 100644 --- a/src/app/grid-validation/grid-validation.sample.component.ts +++ b/src/app/grid-validation/grid-validation.sample.component.ts @@ -89,5 +89,26 @@ export class GridValidationSampleComponent { public validationChange(evtArgs: Validity){ alert(evtArgs === Validity.Invalid ? 'state became INVALID' : 'state became VALID'); } + + public updateRow(id) { + this.gridWithTransaction.updateRow({ + ProductID: 1, + ProductName: '', + SupplierID: 1, + CategoryID: 1, + QuantityPerUnit: '10 boxes x 20 bags', + UnitPrice: '18.0000', + UnitsInStock: 39, + UnitsOnOrder: 0, + ReorderLevel: 10.567, + Discontinued: false, + OrderDate: null, + OrderDate2: new Date(1991, 2, 12, 18, 40, 50).toISOString() + }, id) + } + + public updateCell(id) { + this.gridWithTransaction.updateCell('', id, 'ProductName'); + } } From cd160e81185853972e7bd2e5c36890bc7c8ca781 Mon Sep 17 00:00:00 2001 From: MKirova Date: Mon, 29 Aug 2022 15:57:09 +0300 Subject: [PATCH 112/149] chore(*): Simplify check. --- projects/igniteui-angular/src/lib/grids/api.service.ts | 6 ++---- src/app/grid-validation/grid-validation.sample.component.ts | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/api.service.ts b/projects/igniteui-angular/src/lib/grids/api.service.ts index 8bfad870759..b4ae2101e65 100644 --- a/projects/igniteui-angular/src/lib/grids/api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/api.service.ts @@ -148,7 +148,7 @@ export class GridBaseAPIService implements GridServiceType { const data = this.getRowData(cell.id.rowID); const newRowData = reverseMapper(cell.column.field, args.newValue); this.updateData(this.grid, cell.id.rowID, data, cell.rowData, newRowData); - if (!this.grid.rowEditable) { + if (!this.grid.crudService.row) { this.grid.validation.update(cell.id.rowID, newRowData); } if (this.grid.primaryKey === cell.column.field) { @@ -203,9 +203,7 @@ export class GridBaseAPIService implements GridServiceType { } this.updateData(grid, row.id, data[index], args.oldValue, args.newValue); - if (this.grid.rowEditable) { - this.grid.validation.update(row.id, args.newValue); - } + this.grid.validation.update(row.id, args.newValue); const newId = grid.primaryKey ? args.newValue[grid.primaryKey] : args.newValue; if (selected) { grid.selectionService.deselectRow(row.id); diff --git a/src/app/grid-validation/grid-validation.sample.component.ts b/src/app/grid-validation/grid-validation.sample.component.ts index 939ca88e8c5..d33dd0f0101 100644 --- a/src/app/grid-validation/grid-validation.sample.component.ts +++ b/src/app/grid-validation/grid-validation.sample.component.ts @@ -54,6 +54,7 @@ export class GridValidationSampleComponent { if (invalid.length > 0) { if (confirm('There are invalid values about to be submitted. Do you want to continue')) { this.gridWithTransaction.transactions.commit(this.transactionData); + this.gridWithTransaction.validation.clear(); } } else { this.gridWithTransaction.transactions.commit(this.transactionData); From e52bd30d4fbe7f1c6f6389a5dd32bc733bbc93a9 Mon Sep 17 00:00:00 2001 From: MKirova Date: Mon, 29 Aug 2022 17:23:11 +0300 Subject: [PATCH 113/149] chore(*): Fix tests. --- .../src/lib/grids/grid/grid-validation.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index 99aa2088205..3ff51904361 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -132,7 +132,7 @@ export class IgxGridValidationService { rowGroup.markAsTouched(); for (const col of this.grid.columns) { if(!field || (field && col.field === field)) { - rowGroup?.get(col.field).markAsTouched(); + rowGroup?.get(col.field)?.markAsTouched(); } } } From 4b427f883107cd10e7788ad4d23f71f44a8d1adf Mon Sep 17 00:00:00 2001 From: skrustev Date: Mon, 29 Aug 2022 18:42:06 +0300 Subject: [PATCH 114/149] chore(*): Add few more integration tests. --- .../lib/grids/grid/grid-validation.spec.ts | 72 +++++++++++++++++-- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts index 5f021884ceb..70874326d0a 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts @@ -314,7 +314,7 @@ describe('IgxGrid - Validation #grid', () => { }); }); - describe('Trensactions integration - ', () => { + describe('Transactions integration - ', () => { let fixture; beforeEach(fakeAsync(() => { @@ -323,7 +323,37 @@ describe('IgxGrid - Validation #grid', () => { fixture.detectChanges(); })); - it('should allow setting built-in validators via template-driven configuration on the column', () => { + it('should update validity when setting new value through grid API', () => { + const grid = fixture.componentInstance.grid as IgxGridComponent; + let cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + + grid.updateCell('IG', 2,'ProductName'); + fixture.detectChanges(); + + GridFunctions.verifyCellValid(cell, false); + + grid.transactions.undo(); + fixture.detectChanges(); + + GridFunctions.verifyCellValid(cell, true); + + grid.updateRow({ProductID : '2', ProductName: '', UnitPrice: '29.0000', UnitsInStock: '66'}, 2); + fixture.detectChanges(); + + GridFunctions.verifyCellValid(cell, false); + + grid.transactions.undo(); + fixture.detectChanges(); + + GridFunctions.verifyCellValid(cell, true); + + grid.transactions.redo(); + fixture.detectChanges(); + + GridFunctions.verifyCellValid(cell, false); + }); + + it('should update validation status when using undo/redo api', () => { const grid = fixture.componentInstance.grid as IgxGridComponent; let cell = grid.gridAPI.get_cell_by_visible_index(1, 1); @@ -340,13 +370,11 @@ describe('IgxGrid - Validation #grid', () => { grid.transactions.undo(); fixture.detectChanges(); - cell = grid.gridAPI.get_cell_by_visible_index(1, 1); GridFunctions.verifyCellValid(cell, true); grid.transactions.redo(); fixture.detectChanges(); - cell = grid.gridAPI.get_cell_by_visible_index(1, 1); GridFunctions.verifyCellValid(cell, false); grid.transactions.commit(grid.data); @@ -359,7 +387,7 @@ describe('IgxGrid - Validation #grid', () => { fixture.detectChanges(); }); - it('should not invalidate cleared number cell when transactions are enabled', () => { + it('should not invalidate cleared number cell', () => { const grid = fixture.componentInstance.grid as IgxGridComponent; let cell = grid.gridAPI.get_cell_by_visible_index(1, 3); @@ -401,5 +429,39 @@ describe('IgxGrid - Validation #grid', () => { expect((grid.validation as any).getValidity().length).toEqual(0); }); + + it('should not show errors when the row is deleted', () => { + const grid = fixture.componentInstance.grid as IgxGridComponent; + let cell = grid.gridAPI.get_cell_by_visible_index(1, 1); + + UIInteractions.simulateDoubleClickAndSelectEvent(cell.element); + cell.editMode = true; + cell.update('IG'); + fixture.detectChanges(); + + grid.gridAPI.crudService.endEdit(true); + fixture.detectChanges(); + + expect(grid.validation.getInvalid().length).toEqual(1); + GridFunctions.verifyCellValid(cell, false); + + grid.deleteRow(2); + fixture.detectChanges(); + + expect(grid.validation.getInvalid().length).toEqual(0); + GridFunctions.verifyCellValid(cell, true); + + grid.transactions.undo(); + fixture.detectChanges(); + + expect(grid.validation.getInvalid().length).toEqual(1); + GridFunctions.verifyCellValid(cell, false); + + grid.transactions.redo(); + fixture.detectChanges(); + + expect(grid.validation.getInvalid().length).toEqual(0); + GridFunctions.verifyCellValid(cell, true); + }); }); }); From 87d86b86b031a2552db66591c430e427493d8f15 Mon Sep 17 00:00:00 2001 From: skrustev Date: Tue, 30 Aug 2022 10:33:03 +0300 Subject: [PATCH 115/149] chore(*): Add tree grid integration tests. --- .../lib/grids/grid/grid-validation.spec.ts | 83 ++++++++++++++++++- .../grid-validation-samples.spec.ts | 34 +++++++- 2 files changed, 113 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts index 70874326d0a..a67a80802ef 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts @@ -2,7 +2,7 @@ import { fakeAsync, flush, TestBed, tick } from '@angular/core/testing'; import { FormGroup, Validators } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { IgxInputDirective, IgxTooltipTargetDirective } from 'igniteui-angular'; +import { IgxInputDirective, IgxTooltipTargetDirective, IgxTreeGridComponent, IgxTreeGridModule } from 'igniteui-angular'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators' import { configureTestSuite } from '../../test-utils/configure-suite'; @@ -10,7 +10,8 @@ import { GridFunctions, GridSelectionFunctions } from '../../test-utils/grid-fun import { ForbiddenValidatorDirective, IgxGridValidationTestBaseComponent, - IgxGridValidationTestCustomErrorComponent + IgxGridValidationTestCustomErrorComponent, + IgxTreeGridValidationTestComponent } from '../../test-utils/grid-validation-samples.spec'; import { UIInteractions } from '../../test-utils/ui-interactions.spec'; import { Validity } from '../common/grid.interface'; @@ -24,9 +25,10 @@ describe('IgxGrid - Validation #grid', () => { declarations: [ IgxGridValidationTestBaseComponent, IgxGridValidationTestCustomErrorComponent, + IgxTreeGridValidationTestComponent, ForbiddenValidatorDirective ], - imports: [IgxGridModule, NoopAnimationsModule] + imports: [IgxGridModule, IgxTreeGridModule, NoopAnimationsModule] }); })); @@ -464,4 +466,79 @@ describe('IgxGrid - Validation #grid', () => { GridFunctions.verifyCellValid(cell, true); }); }); + + describe('TreeGrid integration - ', () => { + let fixture; + + beforeEach(fakeAsync(() => { + fixture = TestBed.createComponent(IgxTreeGridValidationTestComponent); + fixture.componentInstance.batchEditing = true; + fixture.detectChanges(); + })); + + it('should allow setting built-in validators via template-driven and mark cell invalid', () => { + const treeGrid = fixture.componentInstance.treeGrid as IgxTreeGridComponent; + let cell = treeGrid.gridAPI.get_cell_by_visible_index(4, 1); + + UIInteractions.simulateDoubleClickAndSelectEvent(cell.element); + cell.editMode = true; + cell.update('IG'); + fixture.detectChanges(); + + GridFunctions.verifyCellValid(cell, false); + + treeGrid.gridAPI.crudService.endEdit(true); + fixture.detectChanges(); + + GridFunctions.verifyCellValid(cell, false); + }); + + it('should allow setting custom validators via template-driven and mark cell invalid', () => { + const treeGrid = fixture.componentInstance.treeGrid as IgxTreeGridComponent; + let cell = treeGrid.gridAPI.get_cell_by_visible_index(4, 1); + + UIInteractions.simulateDoubleClickAndSelectEvent(cell.element); + cell.editMode = true; + cell.update('bob'); + fixture.detectChanges(); + + GridFunctions.verifyCellValid(cell, false); + + treeGrid.gridAPI.crudService.endEdit(true); + fixture.detectChanges(); + + GridFunctions.verifyCellValid(cell, false); + }); + + it('should update validation status when using undo/redo/delete api', () => { + const treeGrid = fixture.componentInstance.treeGrid as IgxTreeGridComponent; + let cell = treeGrid.gridAPI.get_cell_by_visible_index(4, 1); + + UIInteractions.simulateDoubleClickAndSelectEvent(cell.element); + cell.editMode = true; + cell.update('IG'); + fixture.detectChanges(); + + treeGrid.gridAPI.crudService.endEdit(true); + fixture.detectChanges(); + + treeGrid.transactions.undo(); + fixture.detectChanges(); + + expect(treeGrid.validation.getInvalid().length).toEqual(0); + GridFunctions.verifyCellValid(cell, true); + + treeGrid.transactions.redo(); + fixture.detectChanges(); + + expect(treeGrid.validation.getInvalid().length).toEqual(1); + GridFunctions.verifyCellValid(cell, false); + + treeGrid.deleteRow(711); + fixture.detectChanges(); + + expect(treeGrid.validation.getInvalid().length).toEqual(0); + GridFunctions.verifyCellValid(cell, true); + }); + }); }); diff --git a/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts b/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts index 4863210b31a..ac367f657cf 100644 --- a/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts @@ -1,7 +1,8 @@ import { Component, Input, ViewChild, Directive } from '@angular/core'; import { AbstractControl, NG_VALIDATORS, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; -import { IgxGridComponent } from 'igniteui-angular'; +import { IgxGridComponent, IgxTreeGridComponent } from 'igniteui-angular'; import { data } from '../../../../../src/app/shared/data'; +import { SampleTestData } from './sample-test-data.spec'; export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { return (control: AbstractControl): ValidationErrors | null => { @@ -80,3 +81,34 @@ export class IgxGridValidationTestCustomErrorComponent { @ViewChild('grid', { read: IgxGridComponent, static: true }) public grid: IgxGridComponent; } + +@Component({ + template: ` + + + +
+ This name is forbidden. +
+
+
+
+ ` +}) +export class IgxTreeGridValidationTestComponent { + public batchEditing = false; + public rowEditable = true; + public columns = [ + { field: 'ID', dataType: 'string' }, + { field: 'Name', dataType: 'string' }, + { field: 'HireDate', dataType: 'string' }, + { field: 'Age', dataType: 'number' } + ]; + public data = [...SampleTestData.employeeSmallTreeData()]; + + @ViewChild(IgxTreeGridComponent, { static: true }) public treeGrid: IgxTreeGridComponent; +} From 4ddae7a79614043a5f7c911fb133331541ee4031 Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 30 Aug 2022 12:06:28 +0300 Subject: [PATCH 116/149] chore(*): Fix error on rapid pointerdown on icon. --- projects/igniteui-angular/src/lib/grids/cell.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index fa5fcef7a89..83a3554469b 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -1040,8 +1040,8 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT this.grid.navigation.setActiveNode({ row: this.rowIndex, column: this.visibleColumnIndex }); - - if (this.isInvalid) { + const isTargetErrorIcon = event.target && event.target === this.errorIcon?.el.nativeElement + if (this.isInvalid && !isTargetErrorIcon) { this.openErrorTooltip(); this.grid.activeNodeChange.pipe(first()).subscribe(() => { this.closeErrorTooltip(); From ae7e40f7848a8811176326414754be337b067dd1 Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 30 Aug 2022 12:19:46 +0300 Subject: [PATCH 117/149] chore(*): Fix check. --- projects/igniteui-angular/src/lib/grids/cell.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 83a3554469b..9e4ab13a63b 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -1040,7 +1040,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT this.grid.navigation.setActiveNode({ row: this.rowIndex, column: this.visibleColumnIndex }); - const isTargetErrorIcon = event.target && event.target === this.errorIcon?.el.nativeElement + const isTargetErrorIcon = event && event.target && event.target === this.errorIcon?.el.nativeElement if (this.isInvalid && !isTargetErrorIcon) { this.openErrorTooltip(); this.grid.activeNodeChange.pipe(first()).subscribe(() => { From db112c46bf7d225887275df815a54b7780bb55fd Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 30 Aug 2022 12:47:03 +0300 Subject: [PATCH 118/149] chore(*): Apply review comment. --- .../src/lib/grids/grid/grid-validation.service.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index 3ff51904361..0b1e0c33e3b 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -130,10 +130,9 @@ export class IgxGridValidationService { const rowGroup = this.getFormGroup(rowId); if (!rowGroup) return; rowGroup.markAsTouched(); - for (const col of this.grid.columns) { - if(!field || (field && col.field === field)) { - rowGroup?.get(col.field)?.markAsTouched(); - } + const fields = field ? [field] : this.grid.columns.map(x => x.field); + for (const currField of fields) { + rowGroup?.get(currField)?.markAsTouched(); } } From 8371787f9b0b2d060c2be3b749e705a9576140e1 Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 30 Aug 2022 16:10:03 +0300 Subject: [PATCH 119/149] chore(*): Do not call markAsTouched on API calls. Expose it as public instead so it can be called if needed. --- .../igniteui-angular/src/lib/grids/grid-base.directive.ts | 2 -- .../src/lib/grids/grid/grid-validation.service.ts | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index d926cb94e53..321e6e31bf4 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -4528,7 +4528,6 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements const cell = new IgxCell(id, index, col, rowData[col.field], value, rowData, this as any); this.gridAPI.update_cell(cell); - this.validation.markAsTouched(rowSelector, column); this.cdr.detectChanges(); } } @@ -4559,7 +4558,6 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements } const row = new IgxEditRow(rowSelector, -1, this.gridAPI.getRowData(rowSelector), this as any); this.gridAPI.update_row(row, value); - this.validation.markAsTouched(rowSelector); // TODO: fix for #5934 and probably break for #5763 // consider adding of third optional boolean parameter in updateRow. diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index 0b1e0c33e3b..47f8f2c6e25 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -122,10 +122,10 @@ export class IgxGridValidationService { this.updateStatus(); } - /** - * @hidden - * @internal - */ + /** Marks the associated record or field as touched. + * @param id The id of the record that will be marked as touched. + * @param field Optional. The field from the record that will be marked as touched. If not provided all fields will be touched. + */ public markAsTouched(rowId: any, field?: string) { const rowGroup = this.getFormGroup(rowId); if (!rowGroup) return; From 0201ebb882f90b45b806c275843fb8704f0bed3a Mon Sep 17 00:00:00 2001 From: MKirova Date: Tue, 30 Aug 2022 16:52:36 +0300 Subject: [PATCH 120/149] chore(*): Ensure error template is passed in all row templates. Add it as public Input. --- .../src/lib/grids/columns/column.component.ts | 26 +++++++++++++++++++ .../lib/grids/grid/grid-row.component.html | 5 +++- .../hierarchical-row.component.html | 2 ++ .../tree-grid/tree-grid-row.component.html | 4 +++ .../grid-validation.sample.component.html | 11 ++++++-- 5 files changed, 45 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/columns/column.component.ts b/projects/igniteui-angular/src/lib/grids/columns/column.component.ts index d91061ec7e7..d6e243738b7 100644 --- a/projects/igniteui-angular/src/lib/grids/columns/column.component.ts +++ b/projects/igniteui-angular/src/lib/grids/columns/column.component.ts @@ -1296,6 +1296,32 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy public set inlineEditorTemplate(template: TemplateRef) { this._inlineEditorTemplate = template; } + + /** + * Returns a reference to the validation error template. + * ```typescript + * let errorTemplate = this.column.errorTemplate; + * ``` + */ + @notifyChanges() + @WatchColumnChanges() + @Input('errorTemplate') + public get errorTemplate(): TemplateRef { + return this._errorTemplate; + } + /** + * Sets the validation error template. + * ```html + * + *
+ * This name is forbidden. + *
+ *
+ */ + public set errorTemplate(template: TemplateRef) { + this._errorTemplate = template; + } + /** * Returns a reference to the `filterCellTemplate`. * ```typescript diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-row.component.html b/projects/igniteui-angular/src/lib/grids/grid/grid-row.component.html index 50fdf79cea0..a18e8d52291 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-row.component.html +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-row.component.html @@ -117,7 +117,7 @@ [visibleColumnIndex]="col.visibleIndex" [value]="data | dataMapper:col.field:grid.pipeTrigger:data[col.field]:col.hasNestedPath" [cellTemplate]="col.bodyTemplate" - [cellValidationErrorTemplate]="col.validationErrorTemplate" + [cellValidationErrorTemplate]="col.errorTemplate" [lastSearchInfo]="grid.lastSearchInfo" [active]="isCellActive(col.visibleIndex)" [cellSelectionMode]="grid.cellSelection" @@ -150,6 +150,7 @@ [visibleColumnIndex]="col.visibleIndex" [value]="data | dataMapper:col.field:grid.pipeTrigger:data[col.field]:col.hasNestedPath" [cellTemplate]="col.bodyTemplate" + [cellValidationErrorTemplate]="col.errorTemplate" [lastSearchInfo]="grid.lastSearchInfo" [active]="isCellActive(col.visibleIndex)" [cellSelectionMode]="grid.cellSelection" @@ -182,6 +183,7 @@ [visibleColumnIndex]="col.visibleIndex" [value]="data | dataMapper:col.field:grid.pipeTrigger:data[col.field]:col.hasNestedPath" [cellTemplate]="col.bodyTemplate" + [cellValidationErrorTemplate]="col.errorTemplate" [lastSearchInfo]="grid.lastSearchInfo" [active]="isCellActive(col.visibleIndex)" [cellSelectionMode]="grid.cellSelection" @@ -214,6 +216,7 @@ [visibleColumnIndex]="col.visibleIndex" [value]="data | dataMapper:col.field:grid.pipeTrigger:data[col.field]:col.hasNestedPath" [cellTemplate]="col.bodyTemplate" + [cellValidationErrorTemplate]="col.errorTemplate" [lastSearchInfo]="grid.lastSearchInfo" [active]="isCellActive(col.visibleIndex)" [cellSelectionMode]="grid.cellSelection" diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-row.component.html b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-row.component.html index 7d4ccfcd02c..1153f849132 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-row.component.html +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-row.component.html @@ -73,6 +73,7 @@ [visibleColumnIndex]="col.visibleIndex" [value]="data | dataMapper:col.field:grid.pipeTrigger:data[col.field]:col.hasNestedPath" [cellTemplate]="col.bodyTemplate" + [cellValidationErrorTemplate]="col.errorTemplate" [lastSearchInfo]="grid.lastSearchInfo" [cellSelectionMode]="grid.cellSelection" [displayPinnedChip]="shouldDisplayPinnedChip(col.visibleIndex)"> @@ -122,6 +123,7 @@ [visibleColumnIndex]="col.visibleIndex" [value]="data | dataMapper:col.field:grid.pipeTrigger:data[col.field]:col.hasNestedPath" [cellTemplate]="col.bodyTemplate" + [cellValidationErrorTemplate]="col.errorTemplate" [lastSearchInfo]="grid.lastSearchInfo" [cellSelectionMode]="grid.cellSelection" [displayPinnedChip]="shouldDisplayPinnedChip(col.visibleIndex)"> diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-row.component.html b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-row.component.html index c331f293888..e13f3596bd6 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-row.component.html +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-row.component.html @@ -45,6 +45,7 @@ [visibleColumnIndex]="col.visibleIndex" [value]="data | dataMapper:col.field:grid.pipeTrigger:data[col.field]:col.hasNestedPath" [cellTemplate]="col.bodyTemplate" + [cellValidationErrorTemplate]="col.errorTemplate" [lastSearchInfo]="grid.lastSearchInfo" [active]="isCellActive(col.visibleIndex)" [cellSelectionMode]="grid.cellSelection" @@ -77,6 +78,7 @@ [value]="data | dataMapper:col.field:grid.pipeTrigger:data[col.field]:col.hasNestedPath" [isLoading]="isLoading" [cellTemplate]="col.bodyTemplate" + [cellValidationErrorTemplate]="col.errorTemplate" [lastSearchInfo]="grid.lastSearchInfo" [active]="isCellActive(col.visibleIndex)" [cellSelectionMode]="grid.cellSelection" @@ -130,6 +132,7 @@ [visibleColumnIndex]="col.visibleIndex" [value]="data | dataMapper:col.field:grid.pipeTrigger:data[col.field]:col.hasNestedPath" [cellTemplate]="col.bodyTemplate" + [cellValidationErrorTemplate]="col.errorTemplate" [lastSearchInfo]="grid.lastSearchInfo" [active]="isCellActive(col.visibleIndex)" [cellSelectionMode]="grid.cellSelection" @@ -164,6 +167,7 @@ [value]="data | dataMapper:col.field:grid.pipeTrigger:data[col.field]:col.hasNestedPath" [isLoading]="isLoading" [cellTemplate]="col.bodyTemplate" + [cellValidationErrorTemplate]="col.errorTemplate" [lastSearchInfo]="grid.lastSearchInfo" [active]="isCellActive(col.visibleIndex)" [cellSelectionMode]="grid.cellSelection" diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index d5c9f01b6d1..69a8a8ead39 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -75,8 +75,15 @@

Hierarchical Grid

- - + + + + +
+ This name is forbidden. +
+
+
\ No newline at end of file From 3db594ee5c6657282823b5221bf81f5e628e9071 Mon Sep 17 00:00:00 2001 From: skrustev Date: Wed, 31 Aug 2022 10:05:27 +0300 Subject: [PATCH 121/149] chore(*): Update xit-ed validation test and fix failing one. --- .../lib/grids/grid/grid-validation.spec.ts | 27 ++++++++++++++----- .../grid-validation-samples.spec.ts | 2 +- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts index a67a80802ef..f0e1dff4d4b 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts @@ -260,7 +260,7 @@ describe('IgxGrid - Validation #grid', () => { expect(grid.validationStatusChange.emit).toHaveBeenCalledWith(Validity.Valid); }); - xit('should return invalid transaction using the transaction`s getInvalidTransactionLog method', () => { + it('should return invalid transaction using the transaction service API', () => { const grid = fixture.componentInstance.grid as IgxGridComponent; @@ -269,10 +269,10 @@ describe('IgxGrid - Validation #grid', () => { cell.update('asd'); fixture.detectChanges(); cell = grid.gridAPI.get_cell_by_visible_index(1, 1); - let transactionLog = grid.validation.getInvalid(); + let invalidRecords = grid.validation.getInvalid(); GridFunctions.verifyCellValid(cell, false); - expect(transactionLog[0].state[0].valid).toBeFalse(); + expect(invalidRecords[0].state[1].valid).toBeFalse(); cell.editMode = true; @@ -281,8 +281,8 @@ describe('IgxGrid - Validation #grid', () => { cell = grid.gridAPI.get_cell_by_visible_index(1, 1); GridFunctions.verifyCellValid(cell, true); - transactionLog = grid.validation.getInvalid(); - expect(transactionLog[1].state[0].valid).toBeTrue(); + invalidRecords = grid.validation.getInvalid(); + expect(invalidRecords.length).toEqual(0); }); }); @@ -330,6 +330,7 @@ describe('IgxGrid - Validation #grid', () => { let cell = grid.gridAPI.get_cell_by_visible_index(1, 1); grid.updateCell('IG', 2,'ProductName'); + grid.validation.markAsTouched(2); fixture.detectChanges(); GridFunctions.verifyCellValid(cell, false); @@ -339,7 +340,21 @@ describe('IgxGrid - Validation #grid', () => { GridFunctions.verifyCellValid(cell, true); - grid.updateRow({ProductID : '2', ProductName: '', UnitPrice: '29.0000', UnitsInStock: '66'}, 2); + grid.updateRow({ + ProductID: 2, + ProductName: '', + SupplierID: 1, + CategoryID: 1, + QuantityPerUnit: '24 - 12 oz bottles', + UnitPrice: '19.0000', + UnitsInStock: 66, + UnitsOnOrder: 40, + ReorderLevel: 25, + Discontinued: false, + OrderDate: new Date('2003-03-17').toISOString(), + OrderDate2: new Date('2003-03-17').toISOString() + }, 2); + grid.validation.markAsTouched(2); fixture.detectChanges(); GridFunctions.verifyCellValid(cell, false); diff --git a/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts b/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts index ac367f657cf..b7f9f8672e7 100644 --- a/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts @@ -86,7 +86,7 @@ export class IgxGridValidationTestCustomErrorComponent { template: ` - From 9f22cdff01d893713be89077b1b153e39f7c70e8 Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 31 Aug 2022 11:31:00 +0300 Subject: [PATCH 122/149] chore(*): Fix issue with undo of deleted row that has error. --- projects/igniteui-angular/src/lib/grids/grid-base.directive.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index 321e6e31bf4..1a1b47de2a5 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -6210,6 +6210,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements if (value) { this.validation.create(x.transaction.id, value ?? x.recordRef); this.validation.update(x.transaction.id, value ?? x.recordRef); + this.validation.markAsTouched(x.transaction.id); } else { this.validation.clear(x.transaction.id); } From cf206898af8d645017a2a0f7a170385715f2b4d1 Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 31 Aug 2022 13:25:53 +0300 Subject: [PATCH 123/149] chore(*): Fix issue with treegrid delete row not clearing validation state. --- .../src/lib/grids/tree-grid/tree-grid-api.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts index 0dae7c38559..0af61c111b6 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts @@ -147,6 +147,7 @@ export class IgxTreeGridAPIService extends GridBaseAPIService { } else { collection.splice(index, 1); } + this.grid.validation.clear(rowID); } } From df6e4b5761fad14f48b0f6c6c61e66a5b493e6e2 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 1 Sep 2022 10:58:02 +0300 Subject: [PATCH 124/149] chore(*): Add missed get. --- projects/igniteui-angular/src/lib/grids/grid-public-row.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid-public-row.ts b/projects/igniteui-angular/src/lib/grids/grid-public-row.ts index 49a3b2ee271..011047e6e81 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-public-row.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-public-row.ts @@ -57,7 +57,7 @@ abstract class BaseRow implements RowType { * let errors = row.errors; * ``` */ - public errors(): ValidationErrors { + public get errors(): ValidationErrors { const formGroup = this.grid.validation.getFormGroup(this.key); return formGroup?.errors; } From 8e9ec2afd2fe2ed8217ecb0f9dcef956837c9069 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 1 Sep 2022 11:57:57 +0300 Subject: [PATCH 125/149] chore(*): Copy resource strings for languages from JQuery. Note that KO is missing since we don't have it in JQuery. --- .../src/i18n/BG/grid-resources.ts | 14 +++++++------- .../src/i18n/CS/grid-resources.ts | 14 +++++++------- .../src/i18n/DA/grid-resources.ts | 14 +++++++------- .../src/i18n/DE/grid-resources.ts | 14 +++++++------- .../src/i18n/ES/grid-resources.ts | 14 +++++++------- .../src/i18n/FR/grid-resources.ts | 14 +++++++------- .../src/i18n/HU/grid-resources.ts | 14 +++++++------- .../src/i18n/IT/grid-resources.ts | 14 +++++++------- .../src/i18n/JA/grid-resources.ts | 14 +++++++------- .../src/i18n/NB/grid-resources.ts | 14 +++++++------- .../src/i18n/NL/grid-resources.ts | 14 +++++++------- .../src/i18n/PL/grid-resources.ts | 14 +++++++------- .../src/i18n/PT/grid-resources.ts | 14 +++++++------- .../src/i18n/RO/grid-resources.ts | 14 +++++++------- .../src/i18n/SV/grid-resources.ts | 14 +++++++------- .../src/i18n/TR/grid-resources.ts | 14 +++++++------- .../src/i18n/ZH-HANS/grid-resources.ts | 14 +++++++------- .../src/i18n/ZH-HANT/grid-resources.ts | 14 +++++++------- 18 files changed, 126 insertions(+), 126 deletions(-) diff --git a/projects/igniteui-angular-i18n/src/i18n/BG/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/BG/grid-resources.ts index 41697ec395e..6b8c7a2f92c 100644 --- a/projects/igniteui-angular-i18n/src/i18n/BG/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/BG/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsBG_: ExpandRequire = { igx_grid_pivot_selector_values: 'Стойнoсти', igx_grid_pivot_selector_panel_empty: 'Привлачи тук', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'This field is required', - igx_grid_min_validation_error: 'A value of at least {0} should be entered', - igx_grid_max_validation_error: 'A value no more than {0} should be entered', - igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', - igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', - igx_grid_email_validation_error: 'A valid email address should be entered', - igx_grid_pattern_validation_error: 'Entry does not match the required pattern' + igx_grid_required_validation_error: 'Това поле е задължително', + igx_grid_min_validation_error: 'Моля попълнете стойност по-голяма или равна на {0}', + igx_grid_max_validation_error: 'Моля попълнете стойност по-малка или равна на {0}', + igx_grid_min_length_validation_error: 'Входните данни трябва да са дълги поне {0} знака.', + igx_grid_max_length_validation_error: 'Входните данни не трябва да са дълги повече от {0} знака.', + igx_grid_email_validation_error: 'Трябва да бъде въведен валиден имейл адрес.', + igx_grid_pattern_validation_error: 'Въведените данни не спазват зададения образец.' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/CS/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/CS/grid-resources.ts index f5b934e33d2..57a054fb478 100644 --- a/projects/igniteui-angular-i18n/src/i18n/CS/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/CS/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsCS_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'This field is required', - igx_grid_min_validation_error: 'A value of at least {0} should be entered', - igx_grid_max_validation_error: 'A value no more than {0} should be entered', - igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', - igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', - igx_grid_email_validation_error: 'A valid email address should be entered', - igx_grid_pattern_validation_error: 'Entry does not match the required pattern' + igx_grid_required_validation_error: 'Toto pole je povinné', + igx_grid_min_validation_error: 'Je třeba zadat hodnotu alespoň {0}', + igx_grid_max_validation_error: 'Měla by být zadána hodnota nejvýše {0}', + igx_grid_min_length_validation_error: 'Záznam by měl mít alespoň {0} znaků', + igx_grid_max_length_validation_error: 'Záznam by neměl mít více než {0} znaků', + igx_grid_email_validation_error: 'Je třeba zadat platnou e-mailovou adresu', + igx_grid_pattern_validation_error: 'Položka neodpovídá požadovanému vzoru' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/DA/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/DA/grid-resources.ts index 7edc467d7f0..9c4c8dd6ccb 100644 --- a/projects/igniteui-angular-i18n/src/i18n/DA/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/DA/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsDA_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'This field is required', - igx_grid_min_validation_error: 'A value of at least {0} should be entered', - igx_grid_max_validation_error: 'A value no more than {0} should be entered', - igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', - igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', - igx_grid_email_validation_error: 'A valid email address should be entered', - igx_grid_pattern_validation_error: 'Entry does not match the required pattern' + igx_grid_required_validation_error: 'Dette felt er påkrævet', + igx_grid_min_validation_error: 'Der skal indtastes en værdi på mindst {0}', + igx_grid_max_validation_error: 'Der skal indtastes en værdi, der ikke mere end {0}', + igx_grid_min_length_validation_error: 'Indtastningen skal være mindst {0} tegn', + igx_grid_max_length_validation_error: 'Indtastningen må højst være {0} tegn', + igx_grid_email_validation_error: 'Der skal indtastes en gyldig e-mailadresse', + igx_grid_pattern_validation_error: 'Indtastning stemmer ikke overens med det krævede mønster' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/DE/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/DE/grid-resources.ts index fabeaefa8f7..8b2b423b78f 100644 --- a/projects/igniteui-angular-i18n/src/i18n/DE/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/DE/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsDE_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'This field is required', - igx_grid_min_validation_error: 'A value of at least {0} should be entered', - igx_grid_max_validation_error: 'A value no more than {0} should be entered', - igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', - igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', - igx_grid_email_validation_error: 'A valid email address should be entered', - igx_grid_pattern_validation_error: 'Entry does not match the required pattern' + igx_grid_required_validation_error: 'Dieses Feld ist erforderlich', + igx_grid_min_validation_error: 'Bitte geben Sie einen Wert größer oder gleich {0} ein', + igx_grid_max_validation_error: 'Bitte geben Sie einen Wert kleiner oder gleich {0} ein', + igx_grid_min_length_validation_error: 'Bitte geben Sie mindestens {0} Zeichen ein', + igx_grid_max_length_validation_error: 'Bitte geben Sie nicht mehr als {0} Zeichen ein', + igx_grid_email_validation_error: 'Eine gültige E-Mail-Adresse sollte eingegeben werden', + igx_grid_pattern_validation_error: 'Eintrag entspricht nicht dem erforderlichen Muster' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/ES/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/ES/grid-resources.ts index 4fa9d0e758b..afb09cbf61a 100644 --- a/projects/igniteui-angular-i18n/src/i18n/ES/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/ES/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsES_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'This field is required', - igx_grid_min_validation_error: 'A value of at least {0} should be entered', - igx_grid_max_validation_error: 'A value no more than {0} should be entered', - igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', - igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', - igx_grid_email_validation_error: 'A valid email address should be entered', - igx_grid_pattern_validation_error: 'Entry does not match the required pattern' + igx_grid_required_validation_error: 'Este campo es obligatorio', + igx_grid_min_validation_error: 'Escriba un valor mayor o igual a {0}', + igx_grid_max_validation_error: 'Escriba un valor menor o igual a {0}', + igx_grid_min_length_validation_error: 'Escriba {0} caracteres como mínimo', + igx_grid_max_length_validation_error: 'No escriba más de {0} caracteres', + igx_grid_email_validation_error: 'Debe introducirse una dirección de correo electrónico válida.', + igx_grid_pattern_validation_error: 'La entrada no coincide con el patrón necesario.' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/FR/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/FR/grid-resources.ts index a5370f437e1..d3b3f13985b 100644 --- a/projects/igniteui-angular-i18n/src/i18n/FR/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/FR/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsFR_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'This field is required', - igx_grid_min_validation_error: 'A value of at least {0} should be entered', - igx_grid_max_validation_error: 'A value no more than {0} should be entered', - igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', - igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', - igx_grid_email_validation_error: 'A valid email address should be entered', - igx_grid_pattern_validation_error: 'Entry does not match the required pattern' + igx_grid_required_validation_error: 'Ce champ est obligatoire', + igx_grid_min_validation_error: 'Veuillez entrer une valeur supérieure ou égale à {0}', + igx_grid_max_validation_error: 'Veuillez entrer une valeur inférieure ou égale à {0}', + igx_grid_min_length_validation_error: 'Veuillez entrer au moins {0} caractères', + igx_grid_max_length_validation_error: 'Veuillez sélectionner au maximum {0} caractères', + igx_grid_email_validation_error: 'Une adresse e-mail valide doit être saisie', + igx_grid_pattern_validation_error: 'La valeur entrée ne correspond pas au schéma requis' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/HU/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/HU/grid-resources.ts index 639012d0272..d2667abf21e 100644 --- a/projects/igniteui-angular-i18n/src/i18n/HU/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/HU/grid-resources.ts @@ -159,13 +159,13 @@ const GridResourceStringsHU_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'This field is required', - igx_grid_min_validation_error: 'A value of at least {0} should be entered', - igx_grid_max_validation_error: 'A value no more than {0} should be entered', - igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', - igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', - igx_grid_email_validation_error: 'A valid email address should be entered', - igx_grid_pattern_validation_error: 'Entry does not match the required pattern' + igx_grid_required_validation_error: 'Ez a mező kötelező', + igx_grid_min_validation_error: 'A megadott érték legalább {0} kell, hogy legyen', + igx_grid_max_validation_error: 'A megadott érték legfeljebb {0} lehet', + igx_grid_min_length_validation_error: 'A bejegyzésnek legalább {0} karakter hosszúságúnak kell lennie', + igx_grid_max_length_validation_error: 'A bejegyzés legfeljebb {0} karakter hosszúságú lehet', + igx_grid_email_validation_error: 'Érvényes e-mail címet kell megadni', + igx_grid_pattern_validation_error: 'A bejegyzés nem felel meg a szükséges sémának' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/IT/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/IT/grid-resources.ts index 9f8ae4362b8..360b003cff3 100644 --- a/projects/igniteui-angular-i18n/src/i18n/IT/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/IT/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsIT_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'This field is required', - igx_grid_min_validation_error: 'A value of at least {0} should be entered', - igx_grid_max_validation_error: 'A value no more than {0} should be entered', - igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', - igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', - igx_grid_email_validation_error: 'A valid email address should be entered', - igx_grid_pattern_validation_error: 'Entry does not match the required pattern' + igx_grid_required_validation_error: 'Questo campo è obbligatorio', + igx_grid_min_validation_error: 'È necessario immettere un valore di almeno {0}', + igx_grid_max_validation_error: 'È necessario immettere un valore non superiore a {0}', + igx_grid_min_length_validation_error: 'La voce deve contenere almeno {0} caratteri', + igx_grid_max_length_validation_error: 'La voce non deve contenere più di {0} caratteri', + igx_grid_email_validation_error: 'È necessario inserire un indirizzo e-mail valido', + igx_grid_pattern_validation_error: 'La voce non corrisponde al modello richiesto', }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/JA/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/JA/grid-resources.ts index 42c7def3dc8..fe501bc813f 100644 --- a/projects/igniteui-angular-i18n/src/i18n/JA/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/JA/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsJA_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'This field is required', - igx_grid_min_validation_error: 'A value of at least {0} should be entered', - igx_grid_max_validation_error: 'A value no more than {0} should be entered', - igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', - igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', - igx_grid_email_validation_error: 'A valid email address should be entered', - igx_grid_pattern_validation_error: 'Entry does not match the required pattern' + igx_grid_required_validation_error: 'このフィールドは必須フィールドです。', + igx_grid_min_validation_error: "{0} 以上の値を入力してください", + igx_grid_max_validation_error: "{0} 以下の値を入力してください", + igx_grid_min_length_validation_error: '入力の長さは少なくとも {0} 文字である必要があります', + igx_grid_max_length_validation_error: '入力の長さは {0} 文字以下である必要があります', + igx_grid_email_validation_error: '有効なメール アドレスを入力してください', + igx_grid_pattern_validation_error: '入力が所定のパターンに一致しません', }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/NB/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/NB/grid-resources.ts index 89e76aa158e..64794ecbf33 100644 --- a/projects/igniteui-angular-i18n/src/i18n/NB/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/NB/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsNB_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'This field is required', - igx_grid_min_validation_error: 'A value of at least {0} should be entered', - igx_grid_max_validation_error: 'A value no more than {0} should be entered', - igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', - igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', - igx_grid_email_validation_error: 'A valid email address should be entered', - igx_grid_pattern_validation_error: 'Entry does not match the required pattern' + igx_grid_required_validation_error: 'Dette feltet er obligatorisk', + igx_grid_min_validation_error: 'Du må angi en verdi på minst {0}', + igx_grid_max_validation_error: 'Du må angi en verdi som ikke er mer enn {0}', + igx_grid_min_length_validation_error: 'Oppføringen skal være minst {0} tegn(er) lang', + igx_grid_max_length_validation_error: 'Oppføringen må ikke være mer enn {0} tegn(er) lang', + igx_grid_email_validation_error: 'Du må angi en gyldig e-postadresse', + igx_grid_pattern_validation_error: 'Oppføringen samsvarer ikke med det nødvendige mønsteret', }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/NL/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/NL/grid-resources.ts index 774b5bebad9..e3afb948ebd 100644 --- a/projects/igniteui-angular-i18n/src/i18n/NL/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/NL/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsNL_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'This field is required', - igx_grid_min_validation_error: 'A value of at least {0} should be entered', - igx_grid_max_validation_error: 'A value no more than {0} should be entered', - igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', - igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', - igx_grid_email_validation_error: 'A valid email address should be entered', - igx_grid_pattern_validation_error: 'Entry does not match the required pattern' + igx_grid_required_validation_error: 'Dit veld is verplicht', + igx_grid_min_validation_error: 'Er moet een waarde van minimaal {0} worden ingevoerd', + igx_grid_max_validation_error: 'Er moet een waarde van niet meer dan {0} worden ingevoerd', + igx_grid_min_length_validation_error: 'Invoer moet minimaal {0} teken(s) lang zijn', + igx_grid_max_length_validation_error: 'Invoer mag niet meer dan {0} teken(s) lang zijn', + igx_grid_email_validation_error: 'Er moet een geldig e-mailadres worden ingevoerd', + igx_grid_pattern_validation_error: 'Invoer komt niet overeen met het vereiste patroon', }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/PL/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/PL/grid-resources.ts index f64db242a5a..0f1f5633d8f 100644 --- a/projects/igniteui-angular-i18n/src/i18n/PL/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/PL/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsPL_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'This field is required', - igx_grid_min_validation_error: 'A value of at least {0} should be entered', - igx_grid_max_validation_error: 'A value no more than {0} should be entered', - igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', - igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', - igx_grid_email_validation_error: 'A valid email address should be entered', - igx_grid_pattern_validation_error: 'Entry does not match the required pattern' + igx_grid_required_validation_error: 'To pole jest wymagane', + igx_grid_min_validation_error: 'Należy wprowadzić wartość równą co najmniej {0}', + igx_grid_max_validation_error: 'Należy wprowadzić wartość nie większą niż {0}', + igx_grid_min_length_validation_error: 'Wpis powinien mieć co najmniej {0} znaków', + igx_grid_max_length_validation_error: 'Długość wpisu nie może przekraczać {0} znaków', + igx_grid_email_validation_error: 'Należy podać prawidłowy adres e-mail', + igx_grid_pattern_validation_error: 'Wpis nie pasuje do wymaganego wzorca', }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/PT/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/PT/grid-resources.ts index a20db7de344..5e9e97a713b 100644 --- a/projects/igniteui-angular-i18n/src/i18n/PT/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/PT/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsPT_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'This field is required', - igx_grid_min_validation_error: 'A value of at least {0} should be entered', - igx_grid_max_validation_error: 'A value no more than {0} should be entered', - igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', - igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', - igx_grid_email_validation_error: 'A valid email address should be entered', - igx_grid_pattern_validation_error: 'Entry does not match the required pattern' + igx_grid_required_validation_error: 'Este campo é obrigatório', + igx_grid_min_validation_error: 'Um valor de pelo menos {0} deve ser inserido', + igx_grid_max_validation_error: 'Um valor não superior a {0} deve ser introduzido', + igx_grid_min_length_validation_error: 'A entrada deve ter pelo menos {0} caracteres', + igx_grid_max_length_validation_error: 'A entrada não deve ter mais de {0} caracteres', + igx_grid_email_validation_error: 'Deve ser introduzido um endereço de e-mail válido', + igx_grid_pattern_validation_error: 'A entrada não corresponde ao padrão necessário' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/RO/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/RO/grid-resources.ts index 39e809cae16..6e272cbffcd 100644 --- a/projects/igniteui-angular-i18n/src/i18n/RO/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/RO/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsRO_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'This field is required', - igx_grid_min_validation_error: 'A value of at least {0} should be entered', - igx_grid_max_validation_error: 'A value no more than {0} should be entered', - igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', - igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', - igx_grid_email_validation_error: 'A valid email address should be entered', - igx_grid_pattern_validation_error: 'Entry does not match the required pattern' + igx_grid_required_validation_error: 'Acest câmp este obligatoriu', + igx_grid_min_validation_error: 'Trebuie introdusă o valoare de cel puțin {0}', + igx_grid_max_validation_error: 'Trebuie introdusă o valoare de maximum {0}', + igx_grid_min_length_validation_error: 'Intrarea trebuie să aibă cel puțin {0} caractere', + igx_grid_max_length_validation_error: 'Intrarea nu trebuie să aibă mai mult de {0} caractere', + igx_grid_email_validation_error: 'Ar trebui introdusă o adresă de e-mail validă', + igx_grid_pattern_validation_error: 'Intrarea nu se potrivește cu modelul cerut', }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/SV/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/SV/grid-resources.ts index bb8f8242781..d6ed70225e3 100644 --- a/projects/igniteui-angular-i18n/src/i18n/SV/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/SV/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsSV_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'This field is required', - igx_grid_min_validation_error: 'A value of at least {0} should be entered', - igx_grid_max_validation_error: 'A value no more than {0} should be entered', - igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', - igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', - igx_grid_email_validation_error: 'A valid email address should be entered', - igx_grid_pattern_validation_error: 'Entry does not match the required pattern' + igx_grid_required_validation_error: 'Detta fält krävs', + igx_grid_min_validation_error: 'Ett värde på minst {0} borde anges', + igx_grid_max_validation_error: 'Ett värde högst {0} borde anges', + igx_grid_min_length_validation_error: 'Inmatningen ska innehålla minst {0} tecken', + igx_grid_max_length_validation_error: 'Inmatningen får inte vara längre än {0} tecken', + igx_grid_email_validation_error: 'En giltig e-postadress borde anges', + igx_grid_pattern_validation_error: 'Inmatningen matchar inte det önskade mönstret' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/TR/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/TR/grid-resources.ts index 5f3a51cb421..ff85f8db085 100644 --- a/projects/igniteui-angular-i18n/src/i18n/TR/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/TR/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsTR_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'This field is required', - igx_grid_min_validation_error: 'A value of at least {0} should be entered', - igx_grid_max_validation_error: 'A value no more than {0} should be entered', - igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', - igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', - igx_grid_email_validation_error: 'A valid email address should be entered', - igx_grid_pattern_validation_error: 'Entry does not match the required pattern' + igx_grid_required_validation_error: 'Bu alan gereklidir', + igx_grid_min_validation_error: 'En az {0} değeri girilmelidir', + igx_grid_max_validation_error: 'En fazla {0} değeri girilmelidir', + igx_grid_min_length_validation_error: 'Giriş en az {0} karakter uzunluğunda olmalıdır', + igx_grid_max_length_validation_error: 'Giriş, {0} karakterden uzun olmamalıdır', + igx_grid_email_validation_error: 'Geçerli bir e-posta adresi girilmelidir', + igx_grid_pattern_validation_error: 'Giriş, gerekli modelle eşleşmiyor' }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/ZH-HANS/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/ZH-HANS/grid-resources.ts index eefb0f1ab53..c9797fd1e81 100644 --- a/projects/igniteui-angular-i18n/src/i18n/ZH-HANS/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/ZH-HANS/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsZHHANS_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'This field is required', - igx_grid_min_validation_error: 'A value of at least {0} should be entered', - igx_grid_max_validation_error: 'A value no more than {0} should be entered', - igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', - igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', - igx_grid_email_validation_error: 'A valid email address should be entered', - igx_grid_pattern_validation_error: 'Entry does not match the required pattern' + igx_grid_required_validation_error: '此字段为必填项', + igx_grid_min_validation_error: '至少应输入 {0} 的值', + igx_grid_max_validation_error: '必须输入不超过 {0} 的值', + igx_grid_min_length_validation_error: '输入项的字符长度至少应为 {0} 个字符', + igx_grid_max_length_validation_error: '输入项的字符不得超过 {0} 个字符', + igx_grid_email_validation_error: '必须输入有效的电子邮件地址', + igx_grid_pattern_validation_error: '输入项与要求的模式不匹配', }; /** diff --git a/projects/igniteui-angular-i18n/src/i18n/ZH-HANT/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/ZH-HANT/grid-resources.ts index 65dba53ac85..6480d1ccd67 100644 --- a/projects/igniteui-angular-i18n/src/i18n/ZH-HANT/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/ZH-HANT/grid-resources.ts @@ -158,13 +158,13 @@ const GridResourceStringsZHHANT_: ExpandRequire = { igx_grid_pivot_selector_values: 'Values', igx_grid_pivot_selector_panel_empty: 'Drag Items Here', igx_grid_pivot_empty_message: 'Pivot grid has no dimensions and values.', - igx_grid_required_validation_error: 'This field is required', - igx_grid_min_validation_error: 'A value of at least {0} should be entered', - igx_grid_max_validation_error: 'A value no more than {0} should be entered', - igx_grid_min_length_validation_error: 'Entry should be at least {0} character(s) long', - igx_grid_max_length_validation_error: 'Entry should be no more than {0} character(s) long', - igx_grid_email_validation_error: 'A valid email address should be entered', - igx_grid_pattern_validation_error: 'Entry does not match the required pattern' + igx_grid_required_validation_error: '此欄位為必填項', + igx_grid_min_validation_error: '必須輸入至少 {0} 的值', + igx_grid_max_validation_error: '必須輸入不超過 {0} 的值', + igx_grid_min_length_validation_error: '輸入內容必須至少 {0} 個字元', + igx_grid_max_length_validation_error: '輸入內容不得超過 {0} 個字元', + igx_grid_email_validation_error: '必須輸入有效的電子郵件地址', + igx_grid_pattern_validation_error: '輸入的內容不符合要求的格式' }; /** From 2613981e0f9daa7bfffaa15de4c78058dfa8e60c Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 1 Sep 2022 16:39:20 +0300 Subject: [PATCH 126/149] chore(*): Apply review comments. --- CHANGELOG.md | 4 +- .../src/lib/grids/api.service.ts | 20 ++++----- .../src/lib/grids/cell.component.html | 2 +- .../src/lib/grids/cell.component.ts | 45 +++++++++---------- .../src/lib/grids/common/grid.interface.ts | 4 +- .../grids/grid/expandable-cell.component.html | 2 +- .../src/lib/grids/public_api.ts | 1 + .../grids/tree-grid/tree-cell.component.html | 2 +- .../services/transaction/base-transaction.ts | 8 ++-- .../services/transaction/igx-transaction.ts | 6 --- .../grid-validation.sample.component.html | 2 +- .../grid-validation.sample.component.ts | 3 +- 12 files changed, 46 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d764585c05..805d9539bce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,7 +71,7 @@ All notable changes for each version of this project will be documented in this - Added the `IgcFormControl` directive that, when imported with its `IgcFormsModule`, is designed to seamlessly attach to form components from the Ignite UI for WebComponents package and allows using them in Angular templates and reactive forms with support for `ngModel` and `formControlName` directives. Currently the only Web Component with support through the directive is `igc-rating`. - - Added reactive forms style validation for grid editing. This extends the [Angular's reactive forms](https://angular.io/guide/form-validation#validating-input-in-reactive-forms) validation functionality + - Added built-in validation mechanism for Grid Editing. Extends the [Angular Form validation](https://angular.io/guide/form-validation) functionality You can configure it in 2 ways: 1. Via template-driven configuration on the `igx-column` of the grid: ```html @@ -91,7 +91,7 @@ All notable changes for each version of this project will be documented in this } ``` - Edited cells wil enter an invalid state when validation fails and will be show an error icon and message. Cell will remain invalid until the value is edited to a valid value or the related transaction in the transaction service are commited or cleared. + Edited cells wil enter an invalid state when validation fails and will show an error icon and message. Cell will remain invalid until the value is edited to a valid value or the related state in the validation service is cleared. You can refer to the documentation for more details: https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/validation diff --git a/projects/igniteui-angular/src/lib/grids/api.service.ts b/projects/igniteui-angular/src/lib/grids/api.service.ts index b4ae2101e65..5690032152d 100644 --- a/projects/igniteui-angular/src/lib/grids/api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/api.service.ts @@ -294,19 +294,19 @@ export class GridBaseAPIService implements GridServiceType { return this.grid.filteredData; } - public addRowToData(rowData: any, _parentID?: any, validity?) { + public addRowToData(rowData: any, _parentID?: any) { // Add row goes to transactions and if rowEditable is properly implemented, added rows will go to pending transactions // If there is a row in edit - > commit and close const grid = this.grid; - const transactionId = grid.primaryKey ? rowData[grid.primaryKey] : rowData; + const rowId = grid.primaryKey ? rowData[grid.primaryKey] : rowData; if (grid.transactions.enabled) { - const transaction: Transaction = { id: transactionId, type: TransactionType.ADD, newValue: rowData }; + const transaction: Transaction = { id: rowId, type: TransactionType.ADD, newValue: rowData }; grid.transactions.add(transaction); } else { grid.data.push(rowData); } - grid.validation.markAsTouched(transactionId); - grid.validation.update(transactionId, rowData); + grid.validation.markAsTouched(rowId); + grid.validation.update(rowId, rowData); } public deleteRowFromData(rowID: any, index: number) { @@ -553,12 +553,12 @@ export class GridBaseAPIService implements GridServiceType { * @param rowNewValue New value of the row */ protected updateData(grid, rowID, rowValueInDataSource: any, rowCurrentValue: any, rowNewValue: { [x: string]: any }) { - const transaction: Transaction = { - id: rowID, - type: TransactionType.UPDATE, - newValue: rowNewValue - }; if (grid.transactions.enabled) { + const transaction: Transaction = { + id: rowID, + type: TransactionType.UPDATE, + newValue: rowNewValue + }; grid.transactions.add(transaction, rowCurrentValue); } else { mergeObjects(rowValueInDataSource, rowNewValue); diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.html b/projects/igniteui-angular/src/lib/grids/cell.component.html index 9df5a1c8358..ea51d697cf2 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/cell.component.html @@ -116,7 +116,7 @@ error -
diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index b0870edd283..aa7babff369 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -14,10 +14,8 @@ OnChanges, SimpleChanges, Inject, - ContentChildren, ViewChildren, QueryList, - AfterContentInit, AfterViewInit } from '@angular/core'; import { formatPercent } from '@angular/common'; @@ -26,7 +24,7 @@ import { formatCurrency, formatDate, PlatformUtil } from '../core/utils'; import { IgxGridSelectionService } from './selection/selection.service'; import { HammerGesturesManager } from '../core/touch'; import { GridSelectionMode } from './common/enums'; -import { CellType, ColumnType, GridType, IGX_GRID_BASE, RowType, Validity } from './common/grid.interface'; +import { CellType, ColumnType, GridType, IGX_GRID_BASE, RowType } from './common/grid.interface'; import { getCurrencySymbol, getLocaleCurrencyCode } from '@angular/common'; import { GridColumnDataType } from '../data-operations/data-util'; import { IgxRowDirective } from './row.directive'; @@ -34,10 +32,11 @@ import { ISearchInfo } from './common/events'; import { IgxGridCell } from './grid-public-cell'; import { ISelectionNode } from './common/types'; import { IgxTooltipDirective } from '../directives/tooltip'; -import { AutoPositionStrategy, HorizontalAlignment, VerticalAlignment } from '../services/public_api'; +import { AutoPositionStrategy, HorizontalAlignment } from '../services/public_api'; import { IgxIconComponent } from '../icon/icon.component'; -import { first, takeWhile } from 'rxjs/operators'; +import { first, takeUntil, takeWhile } from 'rxjs/operators'; import { FormControl, FormGroup } from '@angular/forms'; +import { Subject } from 'rxjs'; /** * Providing reference to `IgxGridCellComponent`: @@ -59,6 +58,7 @@ import { FormControl, FormGroup } from '@angular/forms'; providers: [HammerGesturesManager] }) export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellType, AfterViewInit { + private _destroy$ = new Subject(); /** * @hidden * @internal @@ -82,13 +82,6 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT @ViewChild('errorIcon', { read: IgxIconComponent, static: false }) public errorIcon: IgxIconComponent; - /** - * @hidden - * @internal - */ - @ViewChild('error', { read: ElementRef, static: false }) - public errorDiv: ElementRef; - /** * Gets the default error template. */ @@ -111,7 +104,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT * @hidden * @internal */ - public get formGroup(): FormGroup { + protected get formGroup(): FormGroup { return this.grid.validation.getFormGroup(this.intRow.key); } @@ -220,10 +213,14 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT public get context(): any { const ctx = { $implicit: this.value, - additionalTemplateContext: this.column.additionalTemplateContext, - formControl: this.editMode ? this.formControl : undefined, - defaultErrorTemplate: this.defaultErrorTemplate + additionalTemplateContext: this.column.additionalTemplateContext }; + if (this.editMode) { + ctx['formControl'] = this.formControl; + } + if (this.isInvalid) { + ctx['defaultErrorTemplate'] = this.defaultErrorTemplate; + } /* Turns the `cell` property from the template context object into lazy-evaluated one. * Otherwise on each detection cycle the cell template is recreating N cell instances where * N = number of visible cells in the grid, leading to massive performance degradation in large grids. @@ -508,8 +505,8 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT /** * Gets the formControl responsible for value changes and validation for this cell. */ - public get formControl(): FormControl { - return (this.formGroup?.get(this.column.field) || new FormControl(this.column.field)) as FormControl; + protected get formControl(): FormControl { + return this.formGroup?.get(this.column.field) as FormControl; } public get gridRowSpan(): number { @@ -816,7 +813,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT } public ngAfterViewInit() { - this.errorTooltip.changes.subscribe(() => { + this.errorTooltip.changes.pipe(takeUntil(this._destroy$)).subscribe(() => { if (this.errorTooltip.length > 0 && this.active) { // error ocurred this.cdr.detectChanges(); @@ -832,7 +829,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT public errorShowing = false; private openErrorTooltip() { - const tooltip = this.errorTooltip.toArray()[0]; + const tooltip = this.errorTooltip.first; tooltip.open( { target: this.errorIcon.el.nativeElement, @@ -859,6 +856,8 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT this.removePointerListeners(this.cellSelectionMode); }); this.touchManager.destroy(); + this._destroy$.next(); + this._destroy$.complete(); } /** @@ -875,8 +874,8 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT this.formControl.statusChanges.pipe(takeWhile(x => this.editMode)).subscribe(status => { if (status === 'INVALID' && this.errorTooltip.length > 0) { this.cdr.detectChanges(); - const tooltip = this.errorTooltip.toArray()[0]; - this.grid.resizeAndRepositionOverlayById(tooltip.overlayId, this.errorDiv.nativeElement.offsetWidth); + const tooltip = this.errorTooltip.first; + this.grid.resizeAndRepositionOverlayById(tooltip.overlayId, this.errorTooltip.first.element.offsetWidth); } }); } @@ -988,7 +987,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT } private closeErrorTooltip() { - const tooltip = this.errorTooltip.toArray()[0]; + const tooltip = this.errorTooltip.first; if (tooltip) { tooltip.close(); } diff --git a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts index 73ef74adee2..a6e2b90fb65 100644 --- a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts @@ -65,7 +65,7 @@ export interface CellType { grid: GridType; id?: { rowID: any; columnID: number; rowIndex: number }; cellID?: any; - errors?: ValidationErrors; + readonly errors?: ValidationErrors; readonly?: boolean; title?: any; width: string; @@ -87,7 +87,7 @@ export interface RowType { summaries?: Map; groupRow?: IGroupByRecord; key?: any; - errors?: ValidationErrors; + readonly errors?: ValidationErrors; data?: any; cells?: QueryList | CellType[]; disabled?: boolean; diff --git a/projects/igniteui-angular/src/lib/grids/grid/expandable-cell.component.html b/projects/igniteui-angular/src/lib/grids/grid/expandable-cell.component.html index 151246d0927..941d74ec580 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/expandable-cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/grid/expandable-cell.component.html @@ -129,7 +129,7 @@ error -
diff --git a/projects/igniteui-angular/src/lib/grids/public_api.ts b/projects/igniteui-angular/src/lib/grids/public_api.ts index 46ab8e10a86..3df294437be 100644 --- a/projects/igniteui-angular/src/lib/grids/public_api.ts +++ b/projects/igniteui-angular/src/lib/grids/public_api.ts @@ -21,6 +21,7 @@ export * from './row-drag.directive'; export * from './column-actions/column-actions.module'; export * from './state.directive'; export * from './toolbar/toolbar.module'; +export * from './grid/grid-validation.service'; export { IgxGridCellComponent as ϴIgxGridCellComponent } from './cell.component'; diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.html b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.html index 6e070e996d2..04893d71c62 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.html @@ -181,7 +181,7 @@ error -
diff --git a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts index 597d751b471..53738308b81 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts @@ -53,7 +53,7 @@ export class IgxBaseTransactionService i * @inheritdoc */ public add(transaction: T, recordRef?: any): void { - if (this.enabled) { + if (this._isPending) { this.updateState(this._pendingStates, transaction, recordRef); this._pendingTransactions.push(transaction); } @@ -112,14 +112,12 @@ export class IgxBaseTransactionService i /** * @inheritdoc */ - public commit(_data: any[], _id?: any): void { - this.clear(_id); - } + public commit(_data: any[], _id?: any): void { } /** * @inheritdoc */ - public clear(id?: any): void { + public clear(_id?: any): void { this._pendingStates.clear(); this._pendingTransactions = []; } diff --git a/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts index 7788fa4ce1d..6d8ac728112 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/igx-transaction.ts @@ -21,11 +21,6 @@ export class IgxTransactionService exten return this._undoStack.length > 0; } - /** - * @inheritdoc - */ - public autoCommit: boolean = false; - /** * @inheritdoc */ @@ -144,7 +139,6 @@ export class IgxTransactionService exten * @inheritdoc */ public clear(id?: any): void { - super.clear(id); if (id !== undefined) { this._transactions = this._transactions.filter(t => t.id !== id); this._states.delete(id); diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index e17560e99fb..53d36d3d669 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -2,7 +2,7 @@

Grid without transactions

Row edit - diff --git a/src/app/grid-validation/grid-validation.sample.component.ts b/src/app/grid-validation/grid-validation.sample.component.ts index fb0ec9850dd..05798f3abef 100644 --- a/src/app/grid-validation/grid-validation.sample.component.ts +++ b/src/app/grid-validation/grid-validation.sample.component.ts @@ -1,7 +1,7 @@ import { Component, Directive, ViewChild, Input } from '@angular/core'; import { data } from '../shared/data'; -import { IgxGridComponent, IgxTransactionService, Validity, IRecordValidationState } from 'igniteui-angular'; +import { IgxGridComponent, IgxTransactionService, Validity, IRecordValidationState, IgxGridValidationService } from 'igniteui-angular'; import { AbstractControl, FormGroup, NG_VALIDATORS, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; import { HIERARCHICAL_DATA } from '../shared/hierarchicalData'; @@ -121,6 +121,7 @@ public hColumns2 = [ if (invalid.length > 0) { if (confirm('There are invalid values about to be submitted. Do you want to continue')) { this.gridWithTransaction.transactions.commit(this.transactionData); + const validator : IgxGridValidationService = this.gridWithTransaction.validation; this.gridWithTransaction.validation.clear(); } } else { From 4da4fd11fd00eaeee2b94c7859c5d1124c91c099 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 1 Sep 2022 17:12:02 +0300 Subject: [PATCH 127/149] chore(*): More review comments. --- .../src/lib/grids/api.service.ts | 2 +- .../src/lib/grids/cell.component.ts | 18 ++++++++++++++++-- .../src/lib/grids/columns/column.component.ts | 17 +---------------- .../src/lib/grids/common/crud.service.ts | 1 + .../src/lib/grids/common/events.ts | 2 +- .../src/lib/grids/common/grid.interface.ts | 1 - .../src/lib/grids/grid-base.directive.ts | 14 ++------------ .../grids/grid/expandable-cell.component.ts | 4 +++- .../lib/grids/grid/grid-validation.service.ts | 6 +----- .../hierarchical-cell.component.ts | 4 +++- .../grids/tree-grid/tree-grid-api.service.ts | 2 +- .../lib/grids/tree-grid/tree-grid.component.ts | 3 ++- .../lib/services/transaction/transaction.ts | 2 -- .../grid-validation.sample.component.html | 2 +- 14 files changed, 33 insertions(+), 45 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/api.service.ts b/projects/igniteui-angular/src/lib/grids/api.service.ts index 5690032152d..50c472d1b0a 100644 --- a/projects/igniteui-angular/src/lib/grids/api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/api.service.ts @@ -5,7 +5,7 @@ import { DataUtil, GridColumnDataType } from '../data-operations/data-util'; import { IFilteringExpressionsTree } from '../data-operations/filtering-expressions-tree'; import { Transaction, TransactionType, State } from '../services/transaction/transaction'; import { IgxCell, IgxGridCRUDService, IgxEditRow } from './common/crud.service'; -import { CellType, ColumnType, GridServiceType, GridType, IFieldValid, RowType } from './common/grid.interface'; +import { CellType, ColumnType, GridServiceType, GridType, RowType } from './common/grid.interface'; import { IGridEditEventArgs, IRowToggleEventArgs } from './common/events'; import { IgxColumnMovingService } from './moving/moving.service'; import { IGroupingExpression } from '../data-operations/grouping-expression.interface'; diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index aa7babff369..c826b2b3978 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -32,7 +32,7 @@ import { ISearchInfo } from './common/events'; import { IgxGridCell } from './grid-public-cell'; import { ISelectionNode } from './common/types'; import { IgxTooltipDirective } from '../directives/tooltip'; -import { AutoPositionStrategy, HorizontalAlignment } from '../services/public_api'; +import { AutoPositionStrategy, HorizontalAlignment, IgxOverlayService } from '../services/public_api'; import { IgxIconComponent } from '../icon/icon.component'; import { first, takeUntil, takeWhile } from 'rxjs/operators'; import { FormControl, FormGroup } from '@angular/forms'; @@ -744,6 +744,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT constructor( protected selectionService: IgxGridSelectionService, @Inject(IGX_GRID_BASE) public grid: GridType, + @Inject(IgxOverlayService) protected overlayService: IgxOverlayService, public cdr: ChangeDetectorRef, private element: ElementRef, protected zone: NgZone, @@ -875,7 +876,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT if (status === 'INVALID' && this.errorTooltip.length > 0) { this.cdr.detectChanges(); const tooltip = this.errorTooltip.first; - this.grid.resizeAndRepositionOverlayById(tooltip.overlayId, this.errorTooltip.first.element.offsetWidth); + this.resizeAndRepositionOverlayById(tooltip.overlayId, this.errorTooltip.first.element.offsetWidth); } }); } @@ -888,6 +889,19 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT } } + + + /** + * @hidden @internal + */ + private resizeAndRepositionOverlayById(overlayId: string, newSize: number) { + const overlay = this.overlayService.getOverlayById(overlayId); + if(!overlay) return; + overlay.initialSize.width = newSize; + overlay.elementRef.nativeElement.parentElement.style.width = newSize + 'px'; + this.overlayService.reposition(overlayId); + } + /** * Starts/ends edit mode for the cell. * diff --git a/projects/igniteui-angular/src/lib/grids/columns/column.component.ts b/projects/igniteui-angular/src/lib/grids/columns/column.component.ts index d6e243738b7..65a54692b58 100644 --- a/projects/igniteui-angular/src/lib/grids/columns/column.component.ts +++ b/projects/igniteui-angular/src/lib/grids/columns/column.component.ts @@ -97,10 +97,7 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy /** - * Sets/gets the validators used when editing cell from this column. - * ```typescript - * let validators = this.column.validators; - * ``` + * @hidden @internal */ public validators: Validator[] = []; @@ -1183,18 +1180,6 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy this._summaryTemplate = template; } - /** - * Returns a reference to the `errorTemplate`. - * ```typescript - * let errorTemplate = this.column.errorTemplate; - * ``` - * - * @memberof IgxColumnComponent - */ - public get validationErrorTemplate(): TemplateRef { - return this._errorTemplate; - } - /** * Returns a reference to the `bodyTemplate`. * ```typescript diff --git a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts index bb9c2934ee4..23722784834 100644 --- a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts +++ b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts @@ -44,6 +44,7 @@ export class IgxEditRow { newValue: updatedData, owner: this.grid, isAddRow: false, + valid: true, event }; diff --git a/projects/igniteui-angular/src/lib/grids/common/events.ts b/projects/igniteui-angular/src/lib/grids/common/events.ts index 3f1c4a71356..e603f9afa6f 100644 --- a/projects/igniteui-angular/src/lib/grids/common/events.ts +++ b/projects/igniteui-angular/src/lib/grids/common/events.ts @@ -31,7 +31,7 @@ export interface IGridEditDoneEventArgs extends IBaseEventArgs { column?: ColumnType; owner?: GridType; isAddRow?: boolean; - valid?: boolean; + valid: boolean; } export interface IGridEditEventArgs extends CancelableEventArgs, IGridEditDoneEventArgs { diff --git a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts index a6e2b90fb65..f11823614a8 100644 --- a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts @@ -616,7 +616,6 @@ export interface GridType extends IGridDataBindable { resetColumnCollections(): void; triggerPipes(): void; repositionRowEditingOverlay(row: RowType): void; - resizeAndRepositionOverlayById(overlayId: string, newSize: number): void; closeRowEditingOverlay(): void; reflow(): void; diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index 1a1b47de2a5..e21489a3a51 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -4481,7 +4481,8 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements cancel: false, rowData: this.getRowData(rowId), oldValue: null, - owner: this + owner: this, + valid: true }; this.rowDelete.emit(args); if (args.cancel) { @@ -5930,17 +5931,6 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements } } - /** - * @hidden @internal - */ - public resizeAndRepositionOverlayById(overlayId: string, newSize: number) { - const overlay = this.overlayService.getOverlayById(overlayId); - if(!overlay) return; - overlay.initialSize.width = newSize; - overlay.elementRef.nativeElement.parentElement.style.width = newSize + 'px'; - this.overlayService.reposition(overlayId); - } - /** * @hidden @internal */ diff --git a/projects/igniteui-angular/src/lib/grids/grid/expandable-cell.component.ts b/projects/igniteui-angular/src/lib/grids/grid/expandable-cell.component.ts index 597d3fae70f..d85c85c68a7 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/expandable-cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/expandable-cell.component.ts @@ -16,6 +16,7 @@ import { DOCUMENT } from '@angular/common'; import { IgxGridSelectionService } from '../selection/selection.service'; import { HammerGesturesManager } from '../../core/touch'; import { GridType, IGX_GRID_BASE } from '../common/grid.interface'; +import { IgxOverlayService } from '../../services/public_api'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -50,13 +51,14 @@ export class IgxGridExpandableCellComponent extends IgxGridCellComponent impleme constructor(selectionService: IgxGridSelectionService, @Inject(IGX_GRID_BASE) grid: GridType, + @Inject(IgxOverlayService) protected overlayService: IgxOverlayService, cdr: ChangeDetectorRef, element: ElementRef, protected zone: NgZone, touchManager: HammerGesturesManager, @Inject(DOCUMENT) public document, protected platformUtil: PlatformUtil) { - super(selectionService, grid, cdr, element, zone, touchManager, platformUtil); + super(selectionService, grid, overlayService, cdr, element, zone, touchManager, platformUtil); } /** diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index 47f8f2c6e25..9a04fbee5fc 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -19,10 +19,6 @@ export class IgxGridValidationService { return this._valid; } - public set valid(value: boolean) { - this._valid = value; - } - /** * @hidden * @internal @@ -142,7 +138,7 @@ export class IgxGridValidationService { */ private updateStatus() { const currentValid = this.valid; - this.valid = this.getInvalid().length === 0; + this._valid = this.getInvalid().length === 0; if (this.valid !== currentValid) { this.grid.validationStatusChange.emit(this.valid ? Validity.Valid : Validity.Invalid); } diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-cell.component.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-cell.component.ts index a7e344cdc18..386aa9f8486 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-cell.component.ts @@ -4,6 +4,7 @@ import { IgxGridSelectionService } from '../selection/selection.service'; import { HammerGesturesManager } from '../../core/touch'; import { PlatformUtil } from '../../core/utils'; import { GridType, IGX_GRID_BASE } from '../common/grid.interface'; +import { IgxOverlayService } from '../../services/public_api'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -18,13 +19,14 @@ export class IgxHierarchicalGridCellComponent extends IgxGridCellComponent imple constructor( protected selectionService: IgxGridSelectionService, @Inject(IGX_GRID_BASE) public grid: GridType, + @Inject(IgxOverlayService) protected overlayService: IgxOverlayService, public cdr: ChangeDetectorRef, helement: ElementRef, protected zone: NgZone, touchManager: HammerGesturesManager, protected platformUtil: PlatformUtil ) { - super(selectionService, grid, cdr, helement, zone, touchManager, platformUtil); + super(selectionService, grid, overlayService, cdr, helement, zone, touchManager, platformUtil); } public ngOnInit() { diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts index 0af61c111b6..e87dd74848f 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts @@ -288,6 +288,7 @@ export class IgxTreeGridAPIService extends GridBaseAPIService { rowValueInDataSource: any, rowCurrentValue: any, rowNewValue: { [x: string]: any }) { + if (grid.transactions.enabled) { const path = grid.generateRowPath(rowID); const transaction: HierarchicalTransaction = { id: rowID, @@ -295,7 +296,6 @@ export class IgxTreeGridAPIService extends GridBaseAPIService { newValue: rowNewValue, path }; - if (grid.transactions.enabled) { grid.transactions.add(transaction, rowCurrentValue); } else { mergeObjects(rowValueInDataSource, rowNewValue); diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts index ee179ffd9a6..db99ae5b20c 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts @@ -729,7 +729,8 @@ export class IgxTreeGridComponent extends IgxGridBaseDirective implements GridTy cancel: false, rowData: this.getRowData(rowId), oldValue: null, - owner: this + owner: this, + valid: true }; this.rowDelete.emit(args); if (args.cancel) { diff --git a/projects/igniteui-angular/src/lib/services/transaction/transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/transaction.ts index 941fd07ee09..db13c1c23ec 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/transaction.ts @@ -1,7 +1,5 @@ import { EventEmitter } from '@angular/core'; -import { FormGroup } from '@angular/forms'; import { IDataCloneStrategy } from '../../data-operations/data-clone-strategy'; -import { IFieldValid } from '../../grids/common/grid.interface'; export enum TransactionType { ADD = 'add', diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index 53d36d3d669..e17560e99fb 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -2,7 +2,7 @@

Grid without transactions

Row edit - From 2ddb7c6ff92a7e727dd0f2735723beca2fe70150 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 1 Sep 2022 17:28:28 +0300 Subject: [PATCH 128/149] chore(*): Fix test to not access formControl. Update due to more comments. --- .../src/lib/grids/cell.component.html | 78 ++++++++++++------- .../src/lib/grids/common/grid.interface.ts | 6 -- .../grids/tree-grid/tree-cell.component.html | 2 +- .../src/lib/test-utils/grid-functions.spec.ts | 2 +- 4 files changed, 53 insertions(+), 35 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.html b/projects/igniteui-angular/src/lib/grids/cell.component.html index ea51d697cf2..a5cac226b21 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/cell.component.html @@ -1,11 +1,21 @@ - {{ grid.resourceStrings.igx_grid_pinned_row_indicator }} + {{ grid.resourceStrings.igx_grid_pinned_row_indicator }} -
{{ - formatter - ? (value | columnFormatter:formatter:rowData:columnData) - : column.dataType === "number" - ? (value | number:column.pipeArgs.digitsInfo:grid.locale) - : (column.dataType === 'date' || column.dataType === 'time' || column.dataType === 'dateTime') - ? (value | date:column.pipeArgs.format:column.pipeArgs.timezone:grid.locale) - : column.dataType === 'currency' - ? (value | currency:currencyCode:column.pipeArgs.display:column.pipeArgs.digitsInfo:grid.locale) - : column.dataType === 'percent' - ? (value | percent:column.pipeArgs.digitsInfo:grid.locale) - : value + " + [row]="rowData" + [column]="this.column.field" + [containerClass]="'igx-grid__td-text'" + [metadata]="searchMetadata" + >{{ + formatter + ? (value | columnFormatter:formatter:rowData:columnData) + : column.dataType === "number" + ? (value | number:column.pipeArgs.digitsInfo:grid.locale) + : (column.dataType === 'date' || column.dataType === 'time' || column.dataType === 'dateTime') + ? (value | date:column.pipeArgs.format:column.pipeArgs.timezone:grid.locale) + : column.dataType === 'currency' + ? (value | currency:currencyCode:column.pipeArgs.display:column.pipeArgs.digitsInfo:grid.locale) + : column.dataType === 'percent' + ? (value | percent:column.pipeArgs.digitsInfo:grid.locale) + : value }}
- {{ value ? "check" : "close" }} + {{ value ? "check" : "close" }}
-
{{ + (value | percent:column.pipeArgs.digitsInfo:grid.locale) : value" + [row]="rowData" + [column]="this.column.field" + [containerClass]="'igx-grid__td-text'" + [metadata]="searchMetadata">{{ !isEmptyAddRowCell ? value : (column.header || column.field) - }}
+ }}
@@ -113,6 +136,7 @@ + error @@ -146,4 +170,4 @@
{{grid.resourceStrings.igx_grid_pattern_validation_error}}
-
\ No newline at end of file + diff --git a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts index f11823614a8..8346e935e2a 100644 --- a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts @@ -205,12 +205,6 @@ export interface ColumnType { populateVisibleIndexes?(): void; } -export interface IFieldValid { - field: string; - valid: boolean; - formGroup: FormGroup; -} - export enum Validity { Valid, Invalid diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.html b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.html index 04893d71c62..9a1b84f8563 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.html @@ -217,4 +217,4 @@
{{grid.resourceStrings.igx_grid_pattern_validation_error}}
- \ No newline at end of file + diff --git a/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts b/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts index 9d1cce9efaf..6512dfe4352 100644 --- a/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts @@ -2081,7 +2081,7 @@ export class GridFunctions { } public static verifyCellValid(cell: IgxGridCellComponent, valid = true) { - expect(cell.formControl.valid).toEqual(valid); + expect(cell.isInvalid).toEqual(!valid); expect(cell.nativeElement.classList.contains(CELL_INVALID_CSS_CLASS)).not.toEqual(valid); } } From 5511c6235df0edb1221ea59eaa9f54f8f375de02 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 1 Sep 2022 17:57:38 +0300 Subject: [PATCH 129/149] chore(*): Make valid optional since args type is reused for adding/deleting etc. --- projects/igniteui-angular/src/lib/grids/common/events.ts | 2 +- projects/igniteui-angular/src/lib/grids/grid-base.directive.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/common/events.ts b/projects/igniteui-angular/src/lib/grids/common/events.ts index e603f9afa6f..3f1c4a71356 100644 --- a/projects/igniteui-angular/src/lib/grids/common/events.ts +++ b/projects/igniteui-angular/src/lib/grids/common/events.ts @@ -31,7 +31,7 @@ export interface IGridEditDoneEventArgs extends IBaseEventArgs { column?: ColumnType; owner?: GridType; isAddRow?: boolean; - valid: boolean; + valid?: boolean; } export interface IGridEditEventArgs extends CancelableEventArgs, IGridEditDoneEventArgs { diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index e21489a3a51..324676f41e6 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -4481,8 +4481,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements cancel: false, rowData: this.getRowData(rowId), oldValue: null, - owner: this, - valid: true + owner: this }; this.rowDelete.emit(args); if (args.cancel) { From 92e7d74eff0c8a4753674a2bdcebd1ae8118bbd3 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 1 Sep 2022 18:09:24 +0300 Subject: [PATCH 130/149] chore(*): Remove optional for delete. --- .../src/lib/grids/tree-grid/tree-grid.component.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts index db99ae5b20c..ee179ffd9a6 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts @@ -729,8 +729,7 @@ export class IgxTreeGridComponent extends IgxGridBaseDirective implements GridTy cancel: false, rowData: this.getRowData(rowId), oldValue: null, - owner: this, - valid: true + owner: this }; this.rowDelete.emit(args); if (args.cancel) { From 666e39a2e7b7cb85d54ece24907493247103d1bd Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 1 Sep 2022 19:02:02 +0300 Subject: [PATCH 131/149] chore(*): Add handling for nested properties where the column key is separated with "." --- .../src/lib/grids/cell.component.ts | 2 +- .../lib/grids/grid/grid-validation.service.ts | 29 ++++++++++++++----- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index c826b2b3978..9ea256eb4f3 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -506,7 +506,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT * Gets the formControl responsible for value changes and validation for this cell. */ protected get formControl(): FormControl { - return this.formGroup?.get(this.column.field) as FormControl; + return this.grid.validation.getFormControl(this.intRow.key, this.column.field) as FormControl; } public get gridRowSpan(): number { diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index 9a04fbee5fc..a7e4d421be1 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; +import { resolveNestedPath } from '../../core/utils'; import { GridType, IFieldValidationState, IRecordValidationState, Validity } from '../common/grid.interface'; @Injectable() @@ -28,8 +29,9 @@ export class IgxGridValidationService { if (!formGroup) { formGroup = new FormGroup({}); for (const col of this.grid.columns) { - const field = col.field; - const control = new FormControl(data ? data[field] : undefined, { updateOn: this.grid.validationTrigger }); + const value = resolveNestedPath(data, col.field); + const field = this.getFieldKey(col.field); + const control = new FormControl(value, { updateOn: this.grid.validationTrigger }); control.addValidators(col.validators); formGroup.addControl(field, control); } @@ -48,6 +50,15 @@ export class IgxGridValidationService { return formGroup; } + /** + * @hidden + * @internal + */ + private getFieldKey(path: string) { + const parts = path?.split('.') ?? []; + return parts.join('_'); + } + /** * @hidden * @internal @@ -62,7 +73,8 @@ export class IgxGridValidationService { */ public getFormControl(rowId: any, columnKey: string) { const formControl = this.getFormGroup(rowId); - return formControl.get(columnKey); + const field = this.getFieldKey(columnKey); + return formControl.get(field); } /** @@ -82,9 +94,10 @@ export class IgxGridValidationService { this._validityStates.forEach((formGroup, key) => { const state: IFieldValidationState[] = []; for (const col of this.grid.columns) { - const control = formGroup.get(col.field); + const colKey = this.getFieldKey(col.field); + const control = formGroup.get(colKey); if (control) { - state.push({ field: col.field, valid: control.valid, errors: control.errors }) + state.push({ field: colKey, valid: control.valid, errors: control.errors }) } } states.push({ id: key, valid: formGroup.valid, state: state }); @@ -109,7 +122,8 @@ export class IgxGridValidationService { const keys = Object.keys(rowData); const rowGroup = this.getFormGroup(rowId); for (const key of keys) { - const control = rowGroup?.get(key); + const colKey = this.getFieldKey(key); + const control = rowGroup?.get(colKey); if (control) { control.setValue(rowData[key], { emitEvent: false }); } @@ -128,7 +142,8 @@ export class IgxGridValidationService { rowGroup.markAsTouched(); const fields = field ? [field] : this.grid.columns.map(x => x.field); for (const currField of fields) { - rowGroup?.get(currField)?.markAsTouched(); + const colKey = this.getFieldKey(currField); + rowGroup?.get(colKey)?.markAsTouched(); } } From ee7c4e320480e3b7d5574f5c8e06a1666c9cb725 Mon Sep 17 00:00:00 2001 From: MKirova Date: Thu, 1 Sep 2022 19:23:55 +0300 Subject: [PATCH 132/149] chore(*): Update some tests. --- .../lib/grids/grid/grid-row-editing.spec.ts | 18 ++++++++++++------ .../lib/grids/grid/grid-validation.service.ts | 4 ++-- .../lib/grids/grid/grid.nested.props.spec.ts | 2 -- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts index 1578e29abb2..2a89d8fa845 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-row-editing.spec.ts @@ -193,7 +193,8 @@ describe('IgxGrid - Row Editing #grid', () => { oldValue: row.data, owner: grid, isAddRow: row.addRowUI, - event: jasmine.anything() as any + event: jasmine.anything() as any, + valid: true }; expect(grid.cellEditExit.emit).toHaveBeenCalledWith(cellEditExitArgs); @@ -253,7 +254,8 @@ describe('IgxGrid - Row Editing #grid', () => { newValue: Object.assign({}, row.data, { ProductName: newCellValue }), owner: grid, isAddRow: row.addRowUI, - event: jasmine.anything() as any + event: jasmine.anything() as any, + valid: true }; UIInteractions.triggerEventHandlerKeyDown('enter', gridContent); @@ -1818,7 +1820,8 @@ describe('IgxGrid - Row Editing #grid', () => { oldValue: initialData, owner: grid, isAddRow: false, - event: jasmine.anything() as any + event: jasmine.anything() as any, + valid: true }); }); @@ -1908,7 +1911,8 @@ describe('IgxGrid - Row Editing #grid', () => { oldValue: initialData, owner: grid, isAddRow: false, - event: undefined + event: undefined, + valid: true }); }); @@ -1938,7 +1942,8 @@ describe('IgxGrid - Row Editing #grid', () => { oldValue: initialData, owner: grid, isAddRow: false, - event: undefined + event: undefined, + valid: true }); }); @@ -2171,7 +2176,8 @@ describe('IgxGrid - Row Editing #grid', () => { newValue: Object.assign({}, row.data, { ProductName: newCellValue }), owner: grid, isAddRow: row.addRowUI, - event: jasmine.anything() as any + event: jasmine.anything() as any, + valid: true }; UIInteractions.triggerEventHandlerKeyDown('enter', gridContent); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index a7e4d421be1..af53b20a61e 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -29,7 +29,7 @@ export class IgxGridValidationService { if (!formGroup) { formGroup = new FormGroup({}); for (const col of this.grid.columns) { - const value = resolveNestedPath(data, col.field); + const value = resolveNestedPath(data || {}, col.field); const field = this.getFieldKey(col.field); const control = new FormControl(value, { updateOn: this.grid.validationTrigger }); control.addValidators(col.validators); @@ -74,7 +74,7 @@ export class IgxGridValidationService { public getFormControl(rowId: any, columnKey: string) { const formControl = this.getFormGroup(rowId); const field = this.getFieldKey(columnKey); - return formControl.get(field); + return formControl?.get(field); } /** diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts index 6d12d975ad3..3893033b8ce 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.nested.props.spec.ts @@ -701,7 +701,6 @@ describe('Edit cell with data of type Array #grid', () => { expect(rowArgs.newValue.locations.length).toEqual(3); expect(rowArgs.oldValue.locations.length).toEqual(3); - delete rowArgs.valid; expect(grid.rowEditExit.emit).toHaveBeenCalledTimes(1); expect(grid.rowEditExit.emit).toHaveBeenCalledWith(rowArgs); @@ -770,7 +769,6 @@ describe('Edit cell with data of type Array #grid', () => { delete rowArgs.cancel; rowArgs.rowData = initialRowData; - delete rowArgs.valid; expect(grid.rowEditDone.emit).toHaveBeenCalledTimes(1); expect(grid.rowEditDone.emit).toHaveBeenCalledWith(rowArgs); From 211940742ee6f4edd51e68cb9c2386f3956b8e70 Mon Sep 17 00:00:00 2001 From: MKirova Date: Fri, 2 Sep 2022 11:40:33 +0300 Subject: [PATCH 133/149] chore(*): Remove focusout. --- .../igniteui-angular/src/lib/grids/cell.component.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 9ea256eb4f3..82ae7d4b9dd 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -992,14 +992,6 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT } }; - /** - * @hidden - * @internal - */ - public focusout = () => { - this.closeErrorTooltip(); - } - private closeErrorTooltip() { const tooltip = this.errorTooltip.first; if (tooltip) { @@ -1172,7 +1164,6 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT } this.nativeElement.addEventListener('pointerenter', this.pointerenter); this.nativeElement.addEventListener('pointerup', this.pointerup); - this.nativeElement.addEventListener('focusout', this.focusout); } private removePointerListeners(selection) { @@ -1181,7 +1172,6 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT } this.nativeElement.removeEventListener('pointerenter', this.pointerenter); this.nativeElement.removeEventListener('pointerup', this.pointerup); - this.nativeElement.removeEventListener('focusout', this.focusout); } private getCellType(useRow?: boolean): CellType { From d70d50f5eb30edcd4e1b2049b39c93b378538304 Mon Sep 17 00:00:00 2001 From: MKirova Date: Fri, 2 Sep 2022 12:03:17 +0300 Subject: [PATCH 134/149] chore(*): Update test. --- .../igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts index f0e1dff4d4b..8586fcfbfeb 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts @@ -187,7 +187,7 @@ describe('IgxGrid - Validation #grid', () => { const erorrMessage = cell.errorTooltip.first.elementRef.nativeElement.children[0].textContent; expect(erorrMessage).toEqual(' Entry should be at least 4 character(s) long '); - cell.focusout(); + cell.errorTooltip.first.close(); tick(); fixture.detectChanges(); expect(cell.errorTooltip.first.collapsed).toBeTrue(); From 9ac32658dff9495998f00af53e846dbb78be6212 Mon Sep 17 00:00:00 2001 From: MKirova Date: Fri, 2 Sep 2022 12:26:00 +0300 Subject: [PATCH 135/149] chore(*): Make Grid status event arg more similar to FormControlStatus. --- .../src/lib/grids/common/grid.interface.ts | 7 ++----- .../igniteui-angular/src/lib/grids/grid-base.directive.ts | 4 ++-- .../src/lib/grids/grid/grid-validation.service.ts | 4 ++-- projects/igniteui-angular/src/lib/grids/public_api.ts | 2 +- .../grid-validation/grid-validation.sample.component.ts | 6 +++--- 5 files changed, 10 insertions(+), 13 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts index 8346e935e2a..660cf5d7ee8 100644 --- a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts @@ -205,10 +205,7 @@ export interface ColumnType { populateVisibleIndexes?(): void; } -export enum Validity { -Valid, -Invalid -} +export declare type ValidityStatus = 'VALID' | 'INVALID'; export interface IRecordValidationState { id: any; @@ -509,7 +506,7 @@ export interface GridType extends IGridDataBindable { rowDragEnd: EventEmitter; rowToggle: EventEmitter; formGroupCreated: EventEmitter; - validationStatusChange: EventEmitter; + validationStatusChange: EventEmitter; toolbarExporting: EventEmitter; rendered$: Observable; diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index 324676f41e6..09a973f3f3d 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -124,7 +124,7 @@ import { IPinColumnCancellableEventArgs } from './common/events'; import { IgxAdvancedFilteringDialogComponent } from './filtering/advanced-filtering/advanced-filtering-dialog.component'; -import { ColumnType, GridServiceType, GridType, IGX_GRID_SERVICE_BASE, ISizeInfo, RowType, Validity } from './common/grid.interface'; +import { ColumnType, GridServiceType, GridType, IGX_GRID_SERVICE_BASE, ISizeInfo, RowType, ValidityStatus } from './common/grid.interface'; import { DropPosition } from './moving/moving.service'; import { IgxHeadSelectorDirective, IgxRowSelectorDirective } from './selection/row-selectors'; import { IgxColumnComponent } from './columns/column.component'; @@ -508,7 +508,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements public formGroupCreated = new EventEmitter(); @Output() - public validationStatusChange = new EventEmitter(); + public validationStatusChange = new EventEmitter(); /** * Emitted when a cell is selected. diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index af53b20a61e..347c4c5da9a 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { resolveNestedPath } from '../../core/utils'; -import { GridType, IFieldValidationState, IRecordValidationState, Validity } from '../common/grid.interface'; +import { GridType, IFieldValidationState, IRecordValidationState, ValidityStatus } from '../common/grid.interface'; @Injectable() export class IgxGridValidationService { @@ -155,7 +155,7 @@ export class IgxGridValidationService { const currentValid = this.valid; this._valid = this.getInvalid().length === 0; if (this.valid !== currentValid) { - this.grid.validationStatusChange.emit(this.valid ? Validity.Valid : Validity.Invalid); + this.grid.validationStatusChange.emit(this.valid ? 'VALID' : 'INVALID'); } } diff --git a/projects/igniteui-angular/src/lib/grids/public_api.ts b/projects/igniteui-angular/src/lib/grids/public_api.ts index 3df294437be..a208863f9ed 100644 --- a/projects/igniteui-angular/src/lib/grids/public_api.ts +++ b/projects/igniteui-angular/src/lib/grids/public_api.ts @@ -13,7 +13,7 @@ export * from './grid-base.directive'; export * from './grid.common'; export * from './grid-public-row'; export * from './grid-public-cell'; -export { CellType, RowType, IGX_GRID_BASE, Validity, IRecordValidationState, IFieldValidationState } from './common/grid.interface'; +export { CellType, RowType, IGX_GRID_BASE, ValidityStatus, IRecordValidationState, IFieldValidationState } from './common/grid.interface'; export * from './summaries/grid-summary'; export * from './grid-common.module'; export * from './grid.rowEdit.directive'; diff --git a/src/app/grid-validation/grid-validation.sample.component.ts b/src/app/grid-validation/grid-validation.sample.component.ts index 05798f3abef..4746003c562 100644 --- a/src/app/grid-validation/grid-validation.sample.component.ts +++ b/src/app/grid-validation/grid-validation.sample.component.ts @@ -1,7 +1,7 @@ import { Component, Directive, ViewChild, Input } from '@angular/core'; import { data } from '../shared/data'; -import { IgxGridComponent, IgxTransactionService, Validity, IRecordValidationState, IgxGridValidationService } from 'igniteui-angular'; +import { IgxGridComponent, ValidityStatus, IRecordValidationState, IgxGridValidationService } from 'igniteui-angular'; import { AbstractControl, FormGroup, NG_VALIDATORS, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; import { HIERARCHICAL_DATA } from '../shared/hierarchicalData'; @@ -155,8 +155,8 @@ public hColumns2 = [ // prodName.addValidators(forbiddenNameValidator(/bob/i)); } - public validationChange(evtArgs: Validity){ - alert(evtArgs === Validity.Invalid ? 'state became INVALID' : 'state became VALID'); + public validationChange(evtArgs: ValidityStatus){ + alert(evtArgs === 'INVALID' ? 'state became INVALID' : 'state became VALID'); } public updateRow(id) { From a6cf8dd3864232a3462030b10aa49b03c66d2a61 Mon Sep 17 00:00:00 2001 From: MKirova Date: Fri, 2 Sep 2022 12:36:21 +0300 Subject: [PATCH 136/149] chore(*): Restore format for inputs. --- .../src/lib/grids/cell.component.html | 63 ++++++++++++++----- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.html b/projects/igniteui-angular/src/lib/grids/cell.component.html index a5cac226b21..6fe4fef35b1 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/cell.component.html @@ -84,13 +84,24 @@ - + - + - - + - + {{ currencyCodeSymbol }} - + {{ currencyCodeSymbol }} - + {{ editValue | percent:column.pipeArgs.digitsInfo:grid.locale }} From 97196a6e2d69f38b6249f3064da39a0686c706a0 Mon Sep 17 00:00:00 2001 From: MKirova Date: Fri, 2 Sep 2022 12:40:42 +0300 Subject: [PATCH 137/149] chore(*): Fix tabs. Remove leftover change event. --- .../src/lib/grids/cell.component.html | 69 +++++++++---------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.html b/projects/igniteui-angular/src/lib/grids/cell.component.html index 6fe4fef35b1..6b9edf9c091 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/cell.component.html @@ -85,10 +85,10 @@ @@ -96,11 +96,10 @@ @@ -118,25 +117,25 @@ @@ -144,12 +143,12 @@ {{ currencyCodeSymbol }} {{ currencyCodeSymbol }} @@ -157,12 +156,12 @@ {{ editValue | percent:column.pipeArgs.digitsInfo:grid.locale }} From 3293bc1e6c03d19525e396c15150723510d0d540 Mon Sep 17 00:00:00 2001 From: MKirova Date: Fri, 2 Sep 2022 12:43:31 +0300 Subject: [PATCH 138/149] chore(*): Fix few more tabs. --- projects/igniteui-angular/src/lib/grids/cell.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.html b/projects/igniteui-angular/src/lib/grids/cell.component.html index 6b9edf9c091..40837dfd5eb 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/cell.component.html @@ -89,8 +89,8 @@ [attr.aria-errormessage]="ariaErrorMessage" [igxFocus]="true" [step]="step" - type="number" - [formControl]="formControl" + type="number" + [formControl]="formControl" /> From a119a2c09a1c2df0f7ad1cf5c68db8fa4d2dabce Mon Sep 17 00:00:00 2001 From: MKirova Date: Fri, 2 Sep 2022 12:48:16 +0300 Subject: [PATCH 139/149] chore(*): Update formatting in other cell templates also. --- .../grids/grid/expandable-cell.component.html | 62 ++++++++++++++----- .../grids/tree-grid/tree-cell.component.html | 62 ++++++++++++++----- 2 files changed, 96 insertions(+), 28 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/expandable-cell.component.html b/projects/igniteui-angular/src/lib/grids/grid/expandable-cell.component.html index 941d74ec580..2b246079ec6 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/expandable-cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/grid/expandable-cell.component.html @@ -65,13 +65,23 @@ - + - + - - + - + {{ currencyCodeSymbol }} - + {{ currencyCodeSymbol }} - + {{ editValue | percent:column.pipeArgs.digitsInfo:grid.locale }} diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.html b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.html index 9a1b84f8563..2ba46b0050a 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.html @@ -85,13 +85,23 @@ - + - + - - + - + {{ currencyCodeSymbol }} - + {{ currencyCodeSymbol }} - + {{ editValue | percent:column.pipeArgs.digitsInfo:grid.locale }} From ae2ff020bfd28c17bbdec56ad276f55a0f2941d0 Mon Sep 17 00:00:00 2001 From: MKirova Date: Fri, 2 Sep 2022 12:55:27 +0300 Subject: [PATCH 140/149] chore(*): Update html docs. --- .../src/lib/grids/columns/column.component.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/columns/column.component.ts b/projects/igniteui-angular/src/lib/grids/columns/column.component.ts index 65a54692b58..2595a9615ad 100644 --- a/projects/igniteui-angular/src/lib/grids/columns/column.component.ts +++ b/projects/igniteui-angular/src/lib/grids/columns/column.component.ts @@ -1295,13 +1295,19 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy return this._errorTemplate; } /** - * Sets the validation error template. + * Sets the error template. * ```html - * + * *
* This name is forbidden. *
*
+ * ``` + * ```typescript + * @ViewChild("'errorTemplate'", {read: TemplateRef }) + * public errorTemplate: TemplateRef; + * this.column.errorTemplate = this.errorTemplate; + * ``` */ public set errorTemplate(template: TemplateRef) { this._errorTemplate = template; From 79c46ce64fa840b3b841848283d2eb7f8b10163f Mon Sep 17 00:00:00 2001 From: MKirova Date: Fri, 2 Sep 2022 12:57:05 +0300 Subject: [PATCH 141/149] chore(*): Update event arg test. --- .../src/lib/grids/grid/grid-validation.spec.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts index 8586fcfbfeb..9c9d21eed6d 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts @@ -14,7 +14,6 @@ import { IgxTreeGridValidationTestComponent } from '../../test-utils/grid-validation-samples.spec'; import { UIInteractions } from '../../test-utils/ui-interactions.spec'; -import { Validity } from '../common/grid.interface'; import { IgxGridComponent } from './grid.component'; import { IgxGridModule } from './grid.module'; @@ -245,7 +244,7 @@ describe('IgxGrid - Validation #grid', () => { fixture.detectChanges(); GridFunctions.verifyCellValid(cell, false); - expect(grid.validationStatusChange.emit).toHaveBeenCalledWith(Validity.Invalid); + expect(grid.validationStatusChange.emit).toHaveBeenCalledWith('Invalid'); UIInteractions.simulateDoubleClickAndSelectEvent(cell.element); cell.editMode = true; @@ -257,7 +256,7 @@ describe('IgxGrid - Validation #grid', () => { fixture.detectChanges(); GridFunctions.verifyCellValid(cell, true); - expect(grid.validationStatusChange.emit).toHaveBeenCalledWith(Validity.Valid); + expect(grid.validationStatusChange.emit).toHaveBeenCalledWith('INVALID'); }); it('should return invalid transaction using the transaction service API', () => { From dc01bc4a13760eaf11b3f78d8ca947110eb4c537 Mon Sep 17 00:00:00 2001 From: MKirova Date: Fri, 2 Sep 2022 13:00:35 +0300 Subject: [PATCH 142/149] chore(*): Update events API docs. --- .../src/lib/grids/grid-base.directive.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index 09a973f3f3d..f0931eed148 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -504,9 +504,26 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements @Output() public cellClick = new EventEmitter(); + + /** + * Emitted when formGroup is created on edit of row/cell. + * + * @example + * ```html + * + * ``` + */ @Output() public formGroupCreated = new EventEmitter(); + /** + * Emitted when grid's validation status changes. + * + * @example + * ```html + * + * ``` + */ @Output() public validationStatusChange = new EventEmitter(); From 831402fe1a642092fd1355130441616862e9e904 Mon Sep 17 00:00:00 2001 From: MKirova Date: Fri, 2 Sep 2022 13:05:38 +0300 Subject: [PATCH 143/149] chore(*): Fix test. --- .../igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts index 9c9d21eed6d..9d3d2701f74 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts @@ -244,7 +244,7 @@ describe('IgxGrid - Validation #grid', () => { fixture.detectChanges(); GridFunctions.verifyCellValid(cell, false); - expect(grid.validationStatusChange.emit).toHaveBeenCalledWith('Invalid'); + expect(grid.validationStatusChange.emit).toHaveBeenCalledWith('INVALID'); UIInteractions.simulateDoubleClickAndSelectEvent(cell.element); cell.editMode = true; From 9d29a72f75e39ca1f38d4507a5e87ed22182e289 Mon Sep 17 00:00:00 2001 From: MKirova Date: Fri, 2 Sep 2022 14:22:40 +0300 Subject: [PATCH 144/149] Revert "chore(*): Remove focusout." This reverts commit 211940742ee6f4edd51e68cb9c2386f3956b8e70. --- .../igniteui-angular/src/lib/grids/cell.component.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 82ae7d4b9dd..9ea256eb4f3 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -992,6 +992,14 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT } }; + /** + * @hidden + * @internal + */ + public focusout = () => { + this.closeErrorTooltip(); + } + private closeErrorTooltip() { const tooltip = this.errorTooltip.first; if (tooltip) { @@ -1164,6 +1172,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT } this.nativeElement.addEventListener('pointerenter', this.pointerenter); this.nativeElement.addEventListener('pointerup', this.pointerup); + this.nativeElement.addEventListener('focusout', this.focusout); } private removePointerListeners(selection) { @@ -1172,6 +1181,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT } this.nativeElement.removeEventListener('pointerenter', this.pointerenter); this.nativeElement.removeEventListener('pointerup', this.pointerup); + this.nativeElement.removeEventListener('focusout', this.focusout); } private getCellType(useRow?: boolean): CellType { From 9e42a0e49f490d703840148c021f76ea7fa50c2f Mon Sep 17 00:00:00 2001 From: MKirova Date: Fri, 2 Sep 2022 16:15:48 +0300 Subject: [PATCH 145/149] chore(*): Update the IRecordValidationState to be more similar to RowType. Add interfaces for new grid events that have owner. Fix small issue in clear API. --- .../src/lib/grids/common/grid.interface.ts | 19 +++++++++++++++---- .../src/lib/grids/grid-base.directive.ts | 6 +++--- .../lib/grids/grid/grid-validation.service.ts | 14 +++++++++----- .../lib/grids/grid/grid-validation.spec.ts | 6 +++--- 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts index 660cf5d7ee8..98ecb89047d 100644 --- a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts @@ -205,12 +205,23 @@ export interface ColumnType { populateVisibleIndexes?(): void; } +export interface IGridFormGroupCreatedEventArgs { + formGroup: FormGroup, + owner: GridType +} + +export interface IGridValidityStatusEventArgs { + status: ValidityStatus, + owner: GridType +} + export declare type ValidityStatus = 'VALID' | 'INVALID'; export interface IRecordValidationState { - id: any; + key: any; valid: boolean; - state: IFieldValidationState[]; + errors: ValidationErrors; + cells: IFieldValidationState[]; } export interface IFieldValidationState { @@ -505,8 +516,8 @@ export interface GridType extends IGridDataBindable { rowDragStart: EventEmitter; rowDragEnd: EventEmitter; rowToggle: EventEmitter; - formGroupCreated: EventEmitter; - validationStatusChange: EventEmitter; + formGroupCreated: EventEmitter; + validationStatusChange: EventEmitter; toolbarExporting: EventEmitter; rendered$: Observable; diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index f0931eed148..a4b7ce0f798 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -124,7 +124,7 @@ import { IPinColumnCancellableEventArgs } from './common/events'; import { IgxAdvancedFilteringDialogComponent } from './filtering/advanced-filtering/advanced-filtering-dialog.component'; -import { ColumnType, GridServiceType, GridType, IGX_GRID_SERVICE_BASE, ISizeInfo, RowType, ValidityStatus } from './common/grid.interface'; +import { ColumnType, GridServiceType, GridType, IGridFormGroupCreatedEventArgs, IGridValidityStatusEventArgs, IGX_GRID_SERVICE_BASE, ISizeInfo, RowType } from './common/grid.interface'; import { DropPosition } from './moving/moving.service'; import { IgxHeadSelectorDirective, IgxRowSelectorDirective } from './selection/row-selectors'; import { IgxColumnComponent } from './columns/column.component'; @@ -514,7 +514,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements * ``` */ @Output() - public formGroupCreated = new EventEmitter(); + public formGroupCreated = new EventEmitter(); /** * Emitted when grid's validation status changes. @@ -525,7 +525,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements * ``` */ @Output() - public validationStatusChange = new EventEmitter(); + public validationStatusChange = new EventEmitter(); /** * Emitted when a cell is selected. diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts index 347c4c5da9a..bc580c28e11 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { resolveNestedPath } from '../../core/utils'; -import { GridType, IFieldValidationState, IRecordValidationState, ValidityStatus } from '../common/grid.interface'; +import { GridType, IFieldValidationState, IGridFormGroupCreatedEventArgs, IRecordValidationState } from '../common/grid.interface'; @Injectable() export class IgxGridValidationService { @@ -35,7 +35,11 @@ export class IgxGridValidationService { control.addValidators(col.validators); formGroup.addControl(field, control); } - this.grid.formGroupCreated.emit(formGroup); + const args: IGridFormGroupCreatedEventArgs = { + formGroup, + owner: this.grid + }; + this.grid.formGroupCreated.emit(args); this.add(rowId, formGroup); } else { // reset to pristine. @@ -100,7 +104,7 @@ export class IgxGridValidationService { state.push({ field: colKey, valid: control.valid, errors: control.errors }) } } - states.push({ id: key, valid: formGroup.valid, state: state }); + states.push({ key: key, valid: formGroup.valid, cells: state, errors: formGroup.errors }); }); return states; } @@ -155,7 +159,7 @@ export class IgxGridValidationService { const currentValid = this.valid; this._valid = this.getInvalid().length === 0; if (this.valid !== currentValid) { - this.grid.validationStatusChange.emit(this.valid ? 'VALID' : 'INVALID'); + this.grid.validationStatusChange.emit({ status: this.valid ? 'VALID' : 'INVALID', owner: this.grid }); } } @@ -164,7 +168,7 @@ export class IgxGridValidationService { * @returns Array of IRecordValidationState. */ public clear(rowId?: any) { - if (rowId) { + if (rowId !== undefined) { this._validityStates.delete(rowId); } else { this._validityStates.clear(); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts index 9d3d2701f74..2b04a9c80d0 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts @@ -244,7 +244,7 @@ describe('IgxGrid - Validation #grid', () => { fixture.detectChanges(); GridFunctions.verifyCellValid(cell, false); - expect(grid.validationStatusChange.emit).toHaveBeenCalledWith('INVALID'); + expect(grid.validationStatusChange.emit).toHaveBeenCalledWith({ status: 'INVALID', owner: grid}); UIInteractions.simulateDoubleClickAndSelectEvent(cell.element); cell.editMode = true; @@ -256,7 +256,7 @@ describe('IgxGrid - Validation #grid', () => { fixture.detectChanges(); GridFunctions.verifyCellValid(cell, true); - expect(grid.validationStatusChange.emit).toHaveBeenCalledWith('INVALID'); + expect(grid.validationStatusChange.emit).toHaveBeenCalledWith({ status: 'INVALID', owner: grid}); }); it('should return invalid transaction using the transaction service API', () => { @@ -271,7 +271,7 @@ describe('IgxGrid - Validation #grid', () => { let invalidRecords = grid.validation.getInvalid(); GridFunctions.verifyCellValid(cell, false); - expect(invalidRecords[0].state[1].valid).toBeFalse(); + expect(invalidRecords[0].cells[1].valid).toBeFalse(); cell.editMode = true; From 24dec19af0f0f13e68534fa2abe51252f768e719 Mon Sep 17 00:00:00 2001 From: MKirova Date: Fri, 2 Sep 2022 16:26:50 +0300 Subject: [PATCH 146/149] chore(*): Fix test. --- .../src/lib/grids/grid/grid-validation.spec.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts index 2b04a9c80d0..78fc865ae26 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts @@ -14,6 +14,7 @@ import { IgxTreeGridValidationTestComponent } from '../../test-utils/grid-validation-samples.spec'; import { UIInteractions } from '../../test-utils/ui-interactions.spec'; +import { IGridFormGroupCreatedEventArgs } from '../common/grid.interface'; import { IgxGridComponent } from './grid.component'; import { IgxGridModule } from './grid.module'; @@ -102,8 +103,8 @@ describe('IgxGrid - Validation #grid', () => { it('should allow setting validators on the exposed FormGroup object', () => { const grid = fixture.componentInstance.grid as IgxGridComponent; - grid.formGroupCreated.pipe(takeUntil($destroyer)).subscribe((formGroup: FormGroup) => { - const prodName = formGroup.get('ProductName'); + grid.formGroupCreated.pipe(takeUntil($destroyer)).subscribe((args: IGridFormGroupCreatedEventArgs) => { + const prodName = args.formGroup.get('ProductName'); prodName.addValidators(Validators.email); }); From 03375f1decf7c8998e373c96e10b1e3d1cf069f2 Mon Sep 17 00:00:00 2001 From: MKirova Date: Fri, 2 Sep 2022 17:03:49 +0300 Subject: [PATCH 147/149] chore(*): Fix issue with discarded added row. --- .../igniteui-angular/src/lib/grids/common/crud.service.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts index 23722784834..c167af6c712 100644 --- a/projects/igniteui-angular/src/lib/grids/common/crud.service.ts +++ b/projects/igniteui-angular/src/lib/grids/common/crud.service.ts @@ -354,10 +354,12 @@ export class IgxRowCrudState extends IgxCellCrudState { if (!commit) { this.grid.transactions.endPending(false); const isAddRow = this.row && this.row.getClassName() === IgxAddRow.name; - if (!isAddRow) { - const id = this.row ? this.row.id : this.cell.id.rowID; + const id = this.row ? this.row.id : this.cell.id.rowID; + if (isAddRow) { + this.grid.validation.clear(id); + } else { this.grid.validation.update(id, rowEditArgs.oldValue); - } + } } else if (this.row.getClassName() === IgxEditRow.name) { rowEditArgs = this.grid.gridAPI.update_row(this.row, this.row.newData, event); nonCancelableArgs = this.rowEditDone(rowEditArgs.oldValue, event); From ec833ec8964c3a1a4dc8701eec0fa3fa6b7f6ba1 Mon Sep 17 00:00:00 2001 From: Damyan Petev Date: Fri, 2 Sep 2022 18:36:58 +0300 Subject: [PATCH 148/149] chore(grid,validation): type export and minor formatting --- .../igniteui-angular/src/lib/grids/common/grid.interface.ts | 2 +- .../src/lib/services/transaction/base-transaction.ts | 5 ++--- .../src/lib/services/transaction/transaction.ts | 2 +- .../grid-validation/grid-validation.sample.component.html | 4 ++-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts index 98ecb89047d..2be2a36de92 100644 --- a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts @@ -215,7 +215,7 @@ export interface IGridValidityStatusEventArgs { owner: GridType } -export declare type ValidityStatus = 'VALID' | 'INVALID'; +export type ValidityStatus = 'VALID' | 'INVALID'; export interface IRecordValidationState { key: any; diff --git a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts index 53738308b81..29109c44b61 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/base-transaction.ts @@ -46,7 +46,6 @@ export class IgxBaseTransactionService i protected _isPending = false; protected _pendingTransactions: T[] = []; protected _pendingStates: Map = new Map(); - private _cloneStrategy: IDataCloneStrategy = new DefaultDataCloneStrategy(); /** @@ -112,12 +111,12 @@ export class IgxBaseTransactionService i /** * @inheritdoc */ - public commit(_data: any[], _id?: any): void { } + public commit(_data: any[], _id?: any): void { } /** * @inheritdoc */ - public clear(_id?: any): void { + public clear(_id?: any): void { this._pendingStates.clear(); this._pendingTransactions = []; } diff --git a/projects/igniteui-angular/src/lib/services/transaction/transaction.ts b/projects/igniteui-angular/src/lib/services/transaction/transaction.ts index db13c1c23ec..3c877f49ee6 100644 --- a/projects/igniteui-angular/src/lib/services/transaction/transaction.ts +++ b/projects/igniteui-angular/src/lib/services/transaction/transaction.ts @@ -87,7 +87,7 @@ export interface TransactionService { */ add(transaction: T, recordRef?: any): void; - /** + /** * Returns all recorded transactions in chronological order * * @param id Optional record id to get transactions for diff --git a/src/app/grid-validation/grid-validation.sample.component.html b/src/app/grid-validation/grid-validation.sample.component.html index e17560e99fb..1ed2646dc4a 100644 --- a/src/app/grid-validation/grid-validation.sample.component.html +++ b/src/app/grid-validation/grid-validation.sample.component.html @@ -85,7 +85,7 @@

Hierarchical Grid

This name is forbidden.
- + - \ No newline at end of file + From aa93478a4ff80310a34b8c39924fd99f39cce0d8 Mon Sep 17 00:00:00 2001 From: Deyan Kamburov Date: Mon, 5 Sep 2022 15:57:47 +0300 Subject: [PATCH 149/149] Update CHANGELOG.md --- CHANGELOG.md | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 805d9539bce..120bf14410c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,37 +63,39 @@ All notable changes for each version of this project will be documented in this ``` - `igxGrid` + - Added built-in validation mechanism for Grid Editing. Extends the [Angular Form validation](https://angular.io/guide/form-validation) functionality + You can configure it in 2 ways: + 1. Via template-driven configuration on the `igx-column` of the grid: + ```html + + ``` + 2. Via reactive forms using the FormGroup exposed via the `formGroupCreated` event of the grid: + + ```html + + ``` + + ```ts + public formCreateHandler(formGr: FormGroup) { + // add a validator + const prodName = formGr.get('UserName'); + prodName.addValidators(forbiddenNameValidator(/bob/i)) + } + ``` + + Edited cells will enter an invalid state when validation fails and will show an error icon and message. Cell will remain invalid until the value is edited to a valid value or the related state in the validation service is cleared. + + You can refer to the documentation for more details: https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/validation - Added ability to auto-size columns to the size of their cells and header content on initialization by setting width `auto`: ``` ``` - Added support for restoring filtering expressions with custom filtering operands for the `IgxGridStateDirective`. + - Added the `IgcFormControl` directive that, when imported with its `IgcFormsModule`, is designed to seamlessly attach to form components from the Ignite UI for WebComponents package and allows using them in Angular templates and reactive forms with support for `ngModel` and `formControlName` directives. Currently the only Web Component with support through the directive is `igc-rating`. - - Added built-in validation mechanism for Grid Editing. Extends the [Angular Form validation](https://angular.io/guide/form-validation) functionality - You can configure it in 2 ways: - 1. Via template-driven configuration on the `igx-column` of the grid: - ```html - - ``` - 2. Via reactive forms using the FormGroup exposed via the `formGroupCreated` event of the grid: - - ```html - - ``` - - ```ts - public formCreateHandler(formGr: FormGroup) { - // add a validator - const prodName = formGr.get('UserName'); - prodName.addValidators(forbiddenNameValidator(/bob/i)) - } - ``` - Edited cells wil enter an invalid state when validation fails and will show an error icon and message. Cell will remain invalid until the value is edited to a valid value or the related state in the validation service is cleared. - - You can refer to the documentation for more details: https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/validation ### General