From 7562cc5f56f7a600d80d1465025acf7deae73854 Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 2 Jul 2025 15:02:21 +0300 Subject: [PATCH 1/2] chore(*): Cell merge POC. --- .../src/lib/grids/common/enums.ts | 14 ++ .../src/lib/grids/common/grid.interface.ts | 4 +- .../src/lib/grids/grid-base.directive.ts | 19 +- .../lib/grids/grid/grid-row.component.html | 48 ++++- .../src/lib/grids/grid/grid.component.html | 3 +- .../src/lib/grids/grid/grid.component.ts | 5 +- .../src/lib/grids/grid/grid.pipes.ts | 40 ++++ .../src/lib/grids/row.directive.ts | 10 + src/app/app.component.ts | 5 + src/app/app.routes.ts | 5 + .../grid-cellMerging.component.html | 15 ++ .../grid-cellMerging.component.scss | 19 ++ .../grid-cellMerging.component.ts | 190 ++++++++++++++++++ 13 files changed, 371 insertions(+), 6 deletions(-) create mode 100644 src/app/grid-cellMerging/grid-cellMerging.component.html create mode 100644 src/app/grid-cellMerging/grid-cellMerging.component.scss create mode 100644 src/app/grid-cellMerging/grid-cellMerging.component.ts diff --git a/projects/igniteui-angular/src/lib/grids/common/enums.ts b/projects/igniteui-angular/src/lib/grids/common/enums.ts index 88d277ccd39..bd66328c97e 100644 --- a/projects/igniteui-angular/src/lib/grids/common/enums.ts +++ b/projects/igniteui-angular/src/lib/grids/common/enums.ts @@ -73,6 +73,20 @@ export const GridSelectionMode = { } as const; export type GridSelectionMode = (typeof GridSelectionMode)[keyof typeof GridSelectionMode]; + +/** + * Enumeration representing different cell merging modes for the grid elements. + * - 'never': Never merge cells. + * - 'always': Always merge adjacent cells based on merge strategy. + * - 'onSort': Only merge cells in column that are sorted. + */ +export const GridCellMergeMode = { + never: 'never', + always: 'always', + onSort: 'onSort' +} as const; +export type GridCellMergeMode = (typeof GridCellMergeMode)[keyof typeof GridCellMergeMode]; + /** Enumeration representing different column display order options. */ export const ColumnDisplayOrder = { Alphabetical: 'Alphabetical', 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 3c8d8aa4f38..3b8d8d2e098 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 { ColumnPinningPosition, FilterMode, GridPagingMode, GridSelectionMode, GridSummaryCalculationMode, GridSummaryPosition, GridValidationTrigger, RowPinningPosition, Size } from './enums'; +import { ColumnPinningPosition, FilterMode, GridCellMergeMode, GridPagingMode, GridSelectionMode, GridSummaryCalculationMode, GridSummaryPosition, GridValidationTrigger, RowPinningPosition, Size } from './enums'; import { ISearchInfo, IGridCellEventArgs, IRowSelectionEventArgs, IColumnSelectionEventArgs, IPinColumnCancellableEventArgs, IColumnVisibilityChangedEventArgs, IColumnVisibilityChangingEventArgs, @@ -690,6 +690,7 @@ export interface GridServiceType { export interface GridType extends IGridDataBindable { /** Represents the locale of the grid: `USD`, `EUR`, `GBP`, `CNY`, `JPY`, etc. */ locale: string; + cellMergeMode: GridCellMergeMode; resourceStrings: IGridResourceStrings; /* blazorSuppress */ /** Represents the native HTML element itself */ @@ -1180,6 +1181,7 @@ export interface GridType extends IGridDataBindable { getEmptyRecordObjectFor(inRow: RowType): any; isSummaryRow(rec: any): boolean; isRecordPinned(rec: any): boolean; + isRecordMerged(rec: any): boolean; getInitialPinnedIndex(rec: any): number; isRecordPinnedByViewIndex(rowIndex: number): boolean; isColumnGrouped(fieldName: string): boolean; 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 5213151e54e..3398421a771 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -93,7 +93,8 @@ import { RowPinningPosition, GridPagingMode, GridValidationTrigger, - Size + Size, + GridCellMergeMode } from './common/enums'; import { IGridCellEventArgs, @@ -2911,6 +2912,14 @@ export abstract class IgxGridBaseDirective implements GridType, // } } + /** + * Gets/Sets cell merge mode. + * + */ + @WatchChanges() + @Input() + public cellMergeMode: GridCellMergeMode = GridCellMergeMode.never; + /** * Gets/Sets row selection mode * @@ -3635,6 +3644,14 @@ export abstract class IgxGridBaseDirective implements GridType, return this.getInitialPinnedIndex(rec) !== -1; } + /** + * @hidden + * @internal + */ + public isRecordMerged(rec) { + return rec.cellMergeMeta; + } + /** * @hidden * @internal 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 7289bc16f29..99124fd2124 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 @@ -31,7 +31,19 @@ } } - + @if (this.hasMergedCells) { +
+ +
+ } + @else { + + } +
@if (pinnedColumns.length > 0 && !grid.isPinningToStart) { @for (col of pinnedColumns | igxNotGrouped; track trackPinnedColumn(col)) { @@ -158,6 +170,40 @@ + + + + + () }; + for (const col of visibleColumns) { + recData.cellMergeMeta.set(col.field, { rowSpan: 1 }); + //TODO condition can be a strategy or some callback that the user can set. + //TODO can also be limited to only sorted columns + if ( prev && prev.recordRef[col.field] === rec[col.field]) { + const root = prev.cellMergeMeta.get(col.field)?.root ?? prev; + root.cellMergeMeta.get(col.field).rowSpan += 1; + recData.cellMergeMeta.get(col.field).root = root; + } + } + prev = recData; + result.push(recData); + } + return result; + } +} + +export interface IMergeByResult { + rowSpan: number; + root?: any; +} + /** * @hidden */ diff --git a/projects/igniteui-angular/src/lib/grids/row.directive.ts b/projects/igniteui-angular/src/lib/grids/row.directive.ts index b2118b1f15e..a353b728c6e 100644 --- a/projects/igniteui-angular/src/lib/grids/row.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/row.directive.ts @@ -27,6 +27,7 @@ import { mergeWith } from 'lodash-es'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { trackByIdentity } from '../core/utils'; +import { IMergeByResult } from './grid/grid.pipes'; @Directive({ selector: '[igxRowBaseComponent]', @@ -117,6 +118,10 @@ export class IgxRowDirective implements DoCheck, AfterViewInit, OnDestroy { return this.grid.isRecordPinned(this.data); } + public get hasMergedCells(): boolean { + return this.grid.isRecordMerged(this.data); + } + /** * Gets the expanded state of the row. * ```typescript @@ -592,6 +597,11 @@ export class IgxRowDirective implements DoCheck, AfterViewInit, OnDestroy { this.addAnimationEnd.emit(this); } + protected getMergeCellSpan(col: ColumnType){ + const rowCount = this.data.cellMergeMeta.get(col.field).rowSpan; + return `repeat(${rowCount},51px)`; + } + /** * @hidden */ diff --git a/src/app/app.component.ts b/src/app/app.component.ts index c4637552c6b..c3d2d4d104b 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -211,6 +211,11 @@ export class AppComponent implements OnInit { icon: 'view_column', name: 'Grid Cell Editing' }, + { + link: '/gridCellMerging', + icon: 'view_column', + name: 'Grid Cell Merging' + }, { link: '/gridClipboard', icon: 'insert_comment', diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 992516f1574..5234d396dc1 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -51,6 +51,7 @@ import { TimePickerSampleComponent } from './time-picker/time-picker.sample'; import { ToastShowcaseSampleComponent } from './toast-showcase/toast-showcase.sample'; import { VirtualForSampleComponent } from './virtual-for-directive/virtual-for.sample'; import { GridCellEditingComponent } from './grid-cellEditing/grid-cellEditing.component'; +import { GridCellMergingComponent } from './grid-cellMerging/grid-cellMerging.component'; import { GridSampleComponent } from './grid/grid.sample'; import { GridColumnMovingSampleComponent } from './grid-column-moving/grid-column-moving.sample'; import { GridColumnSelectionSampleComponent } from './grid-column-selection/grid-column-selection.sample'; @@ -419,6 +420,10 @@ export const appRoutes: Routes = [ path: 'gridCellEditing', component: GridCellEditingComponent }, + { + path: 'gridCellMerging', + component: GridCellMergingComponent + }, { path: 'gridConditionalCellStyling', component: GridCellStylingSampleComponent diff --git a/src/app/grid-cellMerging/grid-cellMerging.component.html b/src/app/grid-cellMerging/grid-cellMerging.component.html new file mode 100644 index 00000000000..6b2628ac345 --- /dev/null +++ b/src/app/grid-cellMerging/grid-cellMerging.component.html @@ -0,0 +1,15 @@ +

Grid with cell merge

+ + + + + + + + + + + + + + diff --git a/src/app/grid-cellMerging/grid-cellMerging.component.scss b/src/app/grid-cellMerging/grid-cellMerging.component.scss new file mode 100644 index 00000000000..3d4037836d0 --- /dev/null +++ b/src/app/grid-cellMerging/grid-cellMerging.component.scss @@ -0,0 +1,19 @@ +.sample-actions { + display: flex; + flex-wrap: wrap; + margin: 1rem 0; + gap: 0.5rem; +} + +.density-chooser { + margin-bottom: 1rem; + + igx-buttongroup { + display: block; + width: 500px; + } +} + +.grid-size { + --ig-size: var(--ig-size-small); +} diff --git a/src/app/grid-cellMerging/grid-cellMerging.component.ts b/src/app/grid-cellMerging/grid-cellMerging.component.ts new file mode 100644 index 00000000000..c2b9d9c0a88 --- /dev/null +++ b/src/app/grid-cellMerging/grid-cellMerging.component.ts @@ -0,0 +1,190 @@ +import { Component, HostBinding, ViewChild } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { + IgxColumnComponent, + IgxGridComponent, +} from 'igniteui-angular'; + +import { data, dataWithoutPK } from '../shared/data'; + +@Component({ + selector: 'app-grid-cellMerging', + templateUrl: 'grid-cellMerging.component.html', + styleUrl: 'grid-cellMerging.component.scss', + imports: [ + FormsModule, + IgxColumnComponent, + IgxGridComponent, + ] +}) +export class GridCellMergingComponent { + public data = [{ + ProductID: 1, + ProductName: 'Chai', + 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() + }, { + ProductID: 2, + ProductName: 'Chai', + SupplierID: 1, + CategoryID: 1, + QuantityPerUnit: '24 - 12 oz bottles', + UnitPrice: '19.0000', + UnitsInStock: 17, + UnitsOnOrder: 40, + ReorderLevel: 25, + Discontinued: false, + OrderDate: new Date('2003-03-17').toISOString(), + OrderDate2: new Date('2003-03-17').toISOString() + }, + { + ProductID: 3, + ProductName: 'Chai', + SupplierID: 1, + CategoryID: 1, + QuantityPerUnit: '24 - 12 oz bottles', + UnitPrice: '19.0000', + UnitsInStock: 17, + UnitsOnOrder: 40, + ReorderLevel: 25, + Discontinued: false, + OrderDate: new Date('2003-03-17').toISOString(), + OrderDate2: new Date('2003-03-17').toISOString() + }, + { + ProductID: 4, + ProductName: 'Chai', + SupplierID: 1, + CategoryID: 1, + QuantityPerUnit: '24 - 12 oz bottles', + UnitPrice: '20.0000', + UnitsInStock: 20, + UnitsOnOrder: 40, + ReorderLevel: 25, + Discontinued: false, + OrderDate: new Date('2003-03-17').toISOString(), + OrderDate2: new Date('2003-03-17').toISOString() + }, + { + ProductID: 5, + ProductName: 'Chai', + SupplierID: 1, + CategoryID: 1, + QuantityPerUnit: '24 - 12 oz bottles', + UnitPrice: '19.0000', + UnitsInStock: 17, + UnitsOnOrder: 40, + ReorderLevel: 25, + Discontinued: false, + OrderDate: new Date('2003-03-17').toISOString(), + OrderDate2: new Date('2003-03-17').toISOString() + }, + { + ProductID: 6, + ProductName: 'Chang', + SupplierID: 1, + CategoryID: 1, + QuantityPerUnit: '24 - 12 oz bottles', + UnitPrice: '19.0000', + UnitsInStock: 17, + UnitsOnOrder: 40, + ReorderLevel: 25, + Discontinued: false, + OrderDate: new Date('2003-03-17').toISOString(), + OrderDate2: new Date('2003-03-17').toISOString() + }, + { + ProductID: 7, + ProductName: 'Chang', + SupplierID: 1, + CategoryID: 1, + QuantityPerUnit: '24 - 12 oz bottles', + UnitPrice: '19.0000', + UnitsInStock: 17, + UnitsOnOrder: 40, + ReorderLevel: 25, + Discontinued: false, + OrderDate: new Date('2003-03-17').toISOString(), + OrderDate2: new Date('2003-03-17').toISOString() + }, + { + ProductID: 8, + ProductName: 'Chang', + SupplierID: 1, + CategoryID: 1, + QuantityPerUnit: '24 - 12 oz bottles', + UnitPrice: '19.0000', + UnitsInStock: 17, + UnitsOnOrder: 40, + ReorderLevel: 30, + Discontinued: false, + OrderDate: new Date('2003-03-17').toISOString(), + OrderDate2: new Date('2003-03-17').toISOString() + }, + { + ProductID: 9, + ProductName: 'Aniseed Syrup', + SupplierID: 1, + CategoryID: 2, + QuantityPerUnit: '12 - 550 ml bottles', + UnitPrice: '10.0000', + UnitsInStock: 13, + UnitsOnOrder: 70, + ReorderLevel: 25, + Discontinued: false, + OrderDate: new Date('2006-03-17').toISOString(), + OrderDate2: new Date(1991, 2, 12, 15, 40, 50).toISOString() + }, + { + ProductID: 10, + ProductName: 'Chang', + SupplierID: 1, + CategoryID: 2, + QuantityPerUnit: '12 - 550 ml bottles', + UnitPrice: '10.0000', + UnitsInStock: 13, + UnitsOnOrder: 70, + ReorderLevel: 25, + Discontinued: false, + OrderDate: new Date('2006-03-17').toISOString(), + OrderDate2: new Date(1991, 2, 12, 15, 40, 50).toISOString() + }, + { + ProductID: 11, + ProductName: 'Chai', + SupplierID: 1, + CategoryID: 2, + QuantityPerUnit: '12 - 550 ml bottles', + UnitPrice: '10.0000', + UnitsInStock: 13, + UnitsOnOrder: 70, + ReorderLevel: 25, + Discontinued: false, + OrderDate: new Date('2006-03-17').toISOString(), + OrderDate2: new Date(1991, 2, 12, 15, 40, 50).toISOString() + }, + { + ProductID: 12, + ProductName: 'Chai', + SupplierID: 1, + CategoryID: 2, + QuantityPerUnit: '12 - 550 ml bottles', + UnitPrice: '10.0000', + UnitsInStock: 12, + UnitsOnOrder: 70, + ReorderLevel: 30, + Discontinued: false, + OrderDate: new Date('2006-03-17').toISOString(), + OrderDate2: new Date(1991, 2, 12, 15, 40, 50).toISOString() + }]; + +} + From f42bb3c7d6f7f94938351138d24c046bf0ec4422 Mon Sep 17 00:00:00 2001 From: MKirova Date: Wed, 2 Jul 2025 16:47:10 +0300 Subject: [PATCH 2/2] chore(*): Minor tweaks to suggestion. --- .../lib/grids/grid/grid-row.component.html | 2 +- .../src/lib/grids/grid/grid.pipes.ts | 13 +- .../grid-cellMerging.component.ts | 167 ++++++++++++++++++ 3 files changed, 178 insertions(+), 4 deletions(-) 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 99124fd2124..e3b00c36d28 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 @@ -194,7 +194,7 @@ [cellSelectionMode]="grid.cellSelection" [displayPinnedChip]="shouldDisplayPinnedChip(col.visibleIndex)" [style.height.px]="data.cellMergeMeta.get(col.field).rowSpan * (this.grid.rowHeight + 1)" - [style.zIndex]="100" + [style.zIndex]="data.cellMergeMeta.get(col.field).rowSpan" [style.min-width]="col.resolvedWidth" [style.max-width]="col.resolvedWidth" [style.flex-basis]="col.resolvedWidth" diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.pipes.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.pipes.ts index 2b6304904ff..2aa8eafd495 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.pipes.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.pipes.ts @@ -99,9 +99,15 @@ export class IgxGridCellMergePipe implements PipeTransform { //TODO condition can be a strategy or some callback that the user can set. //TODO can also be limited to only sorted columns if ( prev && prev.recordRef[col.field] === rec[col.field]) { - const root = prev.cellMergeMeta.get(col.field)?.root ?? prev; - root.cellMergeMeta.get(col.field).rowSpan += 1; - recData.cellMergeMeta.get(col.field).root = root; + // const root = prev.cellMergeMeta.get(col.field)?.root ?? prev; + // root.cellMergeMeta.get(col.field).rowSpan += 1; + // recData.cellMergeMeta.get(col.field).root = root; + recData.cellMergeMeta.get(col.field).prev = prev; + let curr = prev; + while(curr) { + curr.cellMergeMeta.get(col.field).rowSpan += 1; + curr = curr.cellMergeMeta.get(col.field).prev; + } } } prev = recData; @@ -114,6 +120,7 @@ export class IgxGridCellMergePipe implements PipeTransform { export interface IMergeByResult { rowSpan: number; root?: any; + prev?: any; } /** diff --git a/src/app/grid-cellMerging/grid-cellMerging.component.ts b/src/app/grid-cellMerging/grid-cellMerging.component.ts index c2b9d9c0a88..c4184a997ae 100644 --- a/src/app/grid-cellMerging/grid-cellMerging.component.ts +++ b/src/app/grid-cellMerging/grid-cellMerging.component.ts @@ -171,6 +171,173 @@ export class GridCellMergingComponent { OrderDate: new Date('2006-03-17').toISOString(), OrderDate2: new Date(1991, 2, 12, 15, 40, 50).toISOString() }, + { + ProductID: 12, + ProductName: 'Chai', + SupplierID: 1, + CategoryID: 2, + QuantityPerUnit: '12 - 550 ml bottles', + UnitPrice: '10.0000', + UnitsInStock: 12, + UnitsOnOrder: 70, + ReorderLevel: 30, + Discontinued: false, + OrderDate: new Date('2006-03-17').toISOString(), + OrderDate2: new Date(1991, 2, 12, 15, 40, 50).toISOString() + }, + { + ProductID: 1, + ProductName: 'Chai', + 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() + }, { + ProductID: 2, + ProductName: 'Chai', + SupplierID: 1, + CategoryID: 1, + QuantityPerUnit: '24 - 12 oz bottles', + UnitPrice: '19.0000', + UnitsInStock: 17, + UnitsOnOrder: 40, + ReorderLevel: 25, + Discontinued: false, + OrderDate: new Date('2003-03-17').toISOString(), + OrderDate2: new Date('2003-03-17').toISOString() + }, + { + ProductID: 3, + ProductName: 'Chai', + SupplierID: 1, + CategoryID: 1, + QuantityPerUnit: '24 - 12 oz bottles', + UnitPrice: '19.0000', + UnitsInStock: 17, + UnitsOnOrder: 40, + ReorderLevel: 25, + Discontinued: false, + OrderDate: new Date('2003-03-17').toISOString(), + OrderDate2: new Date('2003-03-17').toISOString() + }, + { + ProductID: 4, + ProductName: 'Chai', + SupplierID: 1, + CategoryID: 1, + QuantityPerUnit: '24 - 12 oz bottles', + UnitPrice: '20.0000', + UnitsInStock: 20, + UnitsOnOrder: 40, + ReorderLevel: 25, + Discontinued: false, + OrderDate: new Date('2003-03-17').toISOString(), + OrderDate2: new Date('2003-03-17').toISOString() + }, + { + ProductID: 5, + ProductName: 'Chai', + SupplierID: 1, + CategoryID: 1, + QuantityPerUnit: '24 - 12 oz bottles', + UnitPrice: '19.0000', + UnitsInStock: 17, + UnitsOnOrder: 40, + ReorderLevel: 25, + Discontinued: false, + OrderDate: new Date('2003-03-17').toISOString(), + OrderDate2: new Date('2003-03-17').toISOString() + }, + { + ProductID: 6, + ProductName: 'Chang', + SupplierID: 1, + CategoryID: 1, + QuantityPerUnit: '24 - 12 oz bottles', + UnitPrice: '19.0000', + UnitsInStock: 17, + UnitsOnOrder: 40, + ReorderLevel: 25, + Discontinued: false, + OrderDate: new Date('2003-03-17').toISOString(), + OrderDate2: new Date('2003-03-17').toISOString() + }, + { + ProductID: 7, + ProductName: 'Chang', + SupplierID: 1, + CategoryID: 1, + QuantityPerUnit: '24 - 12 oz bottles', + UnitPrice: '19.0000', + UnitsInStock: 17, + UnitsOnOrder: 40, + ReorderLevel: 25, + Discontinued: false, + OrderDate: new Date('2003-03-17').toISOString(), + OrderDate2: new Date('2003-03-17').toISOString() + }, + { + ProductID: 8, + ProductName: 'Chang', + SupplierID: 1, + CategoryID: 1, + QuantityPerUnit: '24 - 12 oz bottles', + UnitPrice: '19.0000', + UnitsInStock: 17, + UnitsOnOrder: 40, + ReorderLevel: 30, + Discontinued: false, + OrderDate: new Date('2003-03-17').toISOString(), + OrderDate2: new Date('2003-03-17').toISOString() + }, + { + ProductID: 9, + ProductName: 'Aniseed Syrup', + SupplierID: 1, + CategoryID: 2, + QuantityPerUnit: '12 - 550 ml bottles', + UnitPrice: '10.0000', + UnitsInStock: 13, + UnitsOnOrder: 70, + ReorderLevel: 25, + Discontinued: false, + OrderDate: new Date('2006-03-17').toISOString(), + OrderDate2: new Date(1991, 2, 12, 15, 40, 50).toISOString() + }, + { + ProductID: 10, + ProductName: 'Chang', + SupplierID: 1, + CategoryID: 2, + QuantityPerUnit: '12 - 550 ml bottles', + UnitPrice: '10.0000', + UnitsInStock: 13, + UnitsOnOrder: 70, + ReorderLevel: 25, + Discontinued: false, + OrderDate: new Date('2006-03-17').toISOString(), + OrderDate2: new Date(1991, 2, 12, 15, 40, 50).toISOString() + }, + { + ProductID: 11, + ProductName: 'Chai', + SupplierID: 1, + CategoryID: 2, + QuantityPerUnit: '12 - 550 ml bottles', + UnitPrice: '10.0000', + UnitsInStock: 13, + UnitsOnOrder: 70, + ReorderLevel: 25, + Discontinued: false, + OrderDate: new Date('2006-03-17').toISOString(), + OrderDate2: new Date(1991, 2, 12, 15, 40, 50).toISOString() + }, { ProductID: 12, ProductName: 'Chai',