Skip to content

Commit 1836226

Browse files
Fix: Angular implementation
1 parent 559a620 commit 1836226

File tree

10 files changed

+254
-220
lines changed

10 files changed

+254
-220
lines changed

Angular/angular.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
"devextreme-quill",
2222
"jszip",
2323
"devexpress-diagram",
24-
"devexpress-gantt"
24+
"devexpress-gantt",
25+
"devextreme-aspnet-data-nojquery"
2526
],
2627
"outputPath": "dist/Angular",
2728
"index": "src/index.html",

Angular/package-lock.json

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Angular/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"@angular/router": "^18.0.3",
2525
"devextreme": "25.1.3",
2626
"devextreme-angular": "25.1.3",
27+
"devextreme-aspnet-data-nojquery": "^5.1.0",
2728
"rxjs": "~7.8.0",
2829
"tslib": "^2.3.0",
2930
"zone.js": "~0.14.7"

Angular/src/app/app.component.html

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,68 @@
1-
<div class="default-style">
2-
<dx-button [text]="buttonText" (onClick)="onClick($event)"></dx-button>
1+
<div class="dx-viewport demo-container">
2+
<div class="dx-fieldset">
3+
<div class="dx-field">
4+
<div class="dx-field-label"
5+
>DropDownBox with search and embedded DataGrid</div
6+
>
7+
<div class="dx-field-value">
8+
<dx-drop-down-box
9+
#dropDownBox
10+
[dataSource]="dropDownBoxDataSource"
11+
[(value)]="gridBoxValue"
12+
(onValueChanged)="onDropDownValueChanged($event)"
13+
valueExpr="OrderNumber"
14+
[openOnFieldClick]="false"
15+
[displayExpr]="gridBoxDisplayExpr"
16+
[showClearButton]="true"
17+
placeholder="Select a value..."
18+
[acceptCustomValue]="true"
19+
[(opened)]="gridBoxOpened"
20+
valueChangeEvent=""
21+
(onKeyDown)="onDropDownBoxKeyDown($event)"
22+
(onInput)="onInput($event)"
23+
(onOpened)="onOpened($event)"
24+
(onClosed)="onClosed($event)"
25+
>
26+
<dxo-drop-down-options [height]="300"></dxo-drop-down-options>
27+
<div *dxTemplate="let data of 'content'">
28+
<dx-data-grid
29+
height="100%"
30+
width="100%"
31+
[columnWidth]="100"
32+
[(selectedRowKeys)]="gridBoxValue"
33+
[focusedRowIndex]="focusedRowIndex"
34+
[(focusedRowKey)]="focusedRowKey"
35+
[dataSource]="dataSource"
36+
[remoteOperations]="true"
37+
[focusedRowEnabled]="true"
38+
[hoverStateEnabled]="true"
39+
[autoNavigateToFocusedRow]="false"
40+
(onKeyDown)="dataGridKeyDown($event)"
41+
(onContentReady)="dataGridContentReady($event)"
42+
>
43+
<dxi-column
44+
dataField="OrderNumber"
45+
caption="ID"
46+
dataType="number"
47+
></dxi-column>
48+
<dxi-column
49+
dataField="OrderDate"
50+
dataType="date"
51+
format="shortDate"
52+
></dxi-column>
53+
<dxi-column dataField="StoreState" dataType="string"></dxi-column>
54+
<dxi-column dataField="StoreCity" dataType="string"></dxi-column>
55+
<dxi-column dataField="Employee" dataType="string"></dxi-column>
56+
<dxi-column dataField="SaleAmount" dataType="number">
57+
<dxo-format type="currency" [precision]="2"></dxo-format>
58+
</dxi-column>
59+
<dxo-paging [enabled]="true" [pageSize]="10"></dxo-paging>
60+
<dxo-selection mode="single"></dxo-selection>
61+
<dxo-scrolling mode="virtual"></dxo-scrolling>
62+
</dx-data-grid>
63+
</div>
64+
</dx-drop-down-box>
65+
</div>
66+
</div>
67+
</div>
368
</div>

Angular/src/app/app.component.ts

Lines changed: 168 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,179 @@
1-
import { Component } from '@angular/core';
2-
import { ClickEvent } from 'devextreme/ui/button';
1+
import { AfterViewInit, Component, ViewChild } from '@angular/core';
2+
import DataSource from 'devextreme/data/data_source';
3+
import * as AspNetData from 'devextreme-aspnet-data-nojquery';
4+
import { DxDropDownBoxComponent, DxDataGridComponent } from 'devextreme-angular';
5+
import type { DxDataGridTypes } from 'devextreme-angular/ui/data-grid';
6+
import type { DxDropDownBoxTypes } from 'devextreme-angular/ui/drop-down-box';
7+
import type dxDropDownBox from 'devextreme/ui/drop_down_box';
8+
9+
interface OrderItem {
10+
OrderNumber: number;
11+
Employee: string;
12+
StoreState: string;
13+
StoreCity: string;
14+
OrderDate: string;
15+
SaleAmount: number;
16+
}
317

418
@Component({
519
selector: 'app-root',
620
templateUrl: './app.component.html',
721
styleUrls: ['./app.component.scss'],
822
})
9-
export class AppComponent {
10-
title = 'Angular';
23+
export class AppComponent implements AfterViewInit {
24+
@ViewChild('dropDownBox', { static: false }) dropDownBox!: DxDropDownBoxComponent;
25+
26+
@ViewChild(DxDataGridComponent) dataGrid!: DxDataGridComponent<OrderItem, number>;
27+
28+
dataSource: DataSource;
29+
30+
dropDownBoxDataSource: DataSource;
31+
32+
searchTimer: ReturnType<typeof setTimeout> | null = null;
33+
34+
gridBoxValue: number[] = [35711];
35+
36+
gridBoxOpened = false;
37+
38+
focusedRowIndex = 0;
1139

12-
counter = 0;
40+
focusedRowKey: number | null = null;
41+
42+
private firstLoadCompleted = false;
43+
44+
constructor() {
45+
this.dataSource = new DataSource({
46+
store: this.makeAsyncDataSource(),
47+
searchExpr: ['StoreCity', 'StoreState', 'Employee'],
48+
} as any);
49+
this.dropDownBoxDataSource = new DataSource({
50+
store: this.makeAsyncDataSource(),
51+
} as any);
52+
}
53+
54+
ngAfterViewInit(): void {
55+
56+
}
57+
58+
private makeAsyncDataSource(): unknown {
59+
return AspNetData.createStore({
60+
key: 'OrderNumber',
61+
loadUrl: 'https://js.devexpress.com/Demos/WidgetsGalleryDataService/api/orders',
62+
});
63+
}
1364

14-
buttonText = 'Click count: 0';
65+
gridBoxDisplayExpr = (item: OrderItem | null): string => (
66+
item ? `${item.Employee}: ${item.StoreState} - ${item.StoreCity} <${item.OrderNumber}>` : ''
67+
);
68+
69+
onDropDownValueChanged(_e: DxDropDownBoxTypes.ValueChangedEvent): void {
70+
if (this.searchTimer) clearTimeout(this.searchTimer);
71+
this.gridBoxOpened = false;
72+
}
73+
74+
private isSearchIncomplete(dropDownBox: any): boolean {
75+
const displayValue = dropDownBox.option('displayValue');
76+
const text = dropDownBox.option('text');
77+
const textValue = text?.length ? text : undefined;
78+
const displayFirst = displayValue?.length ? displayValue[0] : undefined;
79+
return textValue !== displayFirst;
80+
}
81+
82+
onInput(e: DxDropDownBoxTypes.InputEvent): void {
83+
if (this.searchTimer) clearTimeout(this.searchTimer);
84+
this.searchTimer = setTimeout(() => {
85+
const text = e.component.option('text');
86+
this.dataSource.searchValue(text ?? null);
87+
if (this.gridBoxOpened && this.isSearchIncomplete(e.component)) {
88+
this.dataSource.load().then((items: OrderItem[]) => {
89+
if (items.length > 0 && this.dataGrid?.instance) {
90+
this.focusedRowKey = items[0].OrderNumber;
91+
this.focusedRowIndex = 0;
92+
}
93+
}).catch(() => {});
94+
} else {
95+
this.gridBoxOpened = true;
96+
}
97+
}, 500);
98+
}
99+
100+
onOpened(e: DxDropDownBoxTypes.OpenedEvent): void {
101+
const ddbInstance = e.component as dxDropDownBox & { isKeyDown?: boolean };
102+
if (ddbInstance.isKeyDown) {
103+
if (!this.dataGrid?.instance) return;
104+
const inst = this.dataGrid.instance;
105+
const contentReadyHandler = (args: DxDataGridTypes.ContentReadyEvent<OrderItem, number>): void => {
106+
const gridInstance = args.component;
107+
gridInstance.focus();
108+
gridInstance.off('contentReady', contentReadyHandler as any);
109+
};
110+
if (!this.firstLoadCompleted) {
111+
inst.on('contentReady', contentReadyHandler as any);
112+
} else {
113+
const optionChangedHandler = (args: DxDataGridTypes.OptionChangedEvent<OrderItem, number>): void => {
114+
const gridInstance = args.component;
115+
if (args.name === 'focusedRowKey' || args.name === 'focusedColumnIndex') {
116+
gridInstance.off('optionChanged', optionChangedHandler as any);
117+
gridInstance.focus();
118+
}
119+
};
120+
inst.on('optionChanged', optionChangedHandler as any);
121+
this.focusedRowIndex = 0;
122+
}
123+
ddbInstance.isKeyDown = false;
124+
} else if (this.firstLoadCompleted && this.isSearchIncomplete(ddbInstance)) {
125+
this.dataSource.load().then((items: OrderItem[]) => {
126+
if (items.length > 0) this.focusedRowKey = items[0].OrderNumber;
127+
ddbInstance.focus();
128+
}).catch(() => {});
129+
}
130+
}
131+
132+
onClosed(e: DxDropDownBoxTypes.ClosedEvent): void {
133+
const ddbInstance = e.component;
134+
const searchValue = this.dataSource.searchValue();
135+
if (this.isSearchIncomplete(ddbInstance)) {
136+
this.gridBoxValue = [];
137+
}
138+
if (searchValue) {
139+
this.dataSource.searchValue(null);
140+
}
141+
}
142+
143+
onKeyDown(e: DxDataGridTypes.KeyDownEvent<OrderItem, number>): void {
144+
if (!e.event || e.event.keyCode !== 40) return;
145+
if (!this.gridBoxOpened) {
146+
if (this.dropDownBox?.instance) {
147+
(this.dropDownBox.instance as any).isKeyDown = true;
148+
}
149+
this.gridBoxOpened = true;
150+
} else if (this.dataGrid?.instance) {
151+
this.dataGrid.instance.focus();
152+
}
153+
}
154+
155+
dataGridContentReady(e: DxDataGridTypes.ContentReadyEvent<OrderItem, number>): void {
156+
if (!this.firstLoadCompleted) {
157+
this.firstLoadCompleted = true;
158+
this.dropDownBox.instance.focus();
159+
}
160+
}
161+
162+
dataGridKeyDown(e: DxDataGridTypes.KeyDownEvent<OrderItem, number>): void {
163+
if (e.event?.keyCode === 13 && this.focusedRowKey != null) {
164+
this.focusedRowIndex = (e.component as any).option('focusedRowIndex');
165+
this.gridBoxValue = [this.focusedRowKey];
166+
this.gridBoxOpened = false;
167+
}
168+
}
15169

16-
onClick(e: ClickEvent): void {
17-
this.counter++;
18-
this.buttonText = `Click count: ${this.counter}`;
170+
onDropDownBoxKeyDown(e: DxDropDownBoxTypes.KeyDownEvent): void {
171+
if (e.event?.keyCode !== 40) return;
172+
if (!this.gridBoxOpened && this.dropDownBox?.instance) {
173+
(this.dropDownBox.instance as any).isKeyDown = true;
174+
this.gridBoxOpened = true;
175+
} else if (this.dataGrid?.instance) {
176+
this.dataGrid.instance.focus();
177+
}
19178
}
20179
}

Angular/src/app/app.module.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { NgModule } from '@angular/core';
22
import { BrowserModule } from '@angular/platform-browser';
3-
import { DxButtonModule } from 'devextreme-angular/ui/button';
3+
import { DxDropDownBoxModule } from 'devextreme-angular/ui/drop-down-box';
4+
import { DxDataGridModule } from 'devextreme-angular/ui/data-grid';
5+
import { DxTemplateModule } from 'devextreme-angular/core';
46
import { AppRoutingModule } from './app-routing.module';
57
import { AppComponent } from './app.component';
68

@@ -11,7 +13,9 @@ import { AppComponent } from './app.component';
1113
imports: [
1214
BrowserModule,
1315
AppRoutingModule,
14-
DxButtonModule,
16+
DxDropDownBoxModule,
17+
DxDataGridModule,
18+
DxTemplateModule,
1519
],
1620
providers: [],
1721
bootstrap: [AppComponent],

Angular/src/app/orig_app.component.html

Lines changed: 0 additions & 59 deletions
This file was deleted.

Angular/src/app/orig_app.component.scss

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)