Skip to content

Commit c228fa9

Browse files
Angular: TypeScript migration with DevExtreme types and kebab-case CSS
- Migrated to TypeScript with proper DevExtreme Angular types (DxDataGridTypes, DxFileUploaderTypes, DxButtonTypes) - Updated app.component.ts with framework-specific event types - Converted app.component.css to app.component.scss with kebab-case classes (.retry-button, .uploaded-image) - Created app.service.ts for Employee data service - Updated app.module.ts imports - Added proper type safety with generics <Employee, number> - Removed orig_ files - Build and lint passing with 0 errors
1 parent 7cda9c1 commit c228fa9

File tree

9 files changed

+288
-290
lines changed

9 files changed

+288
-290
lines changed

Angular/src/app/app.component.html

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,70 @@
1-
<div class="default-style">
2-
<dx-button [text]="buttonText" (onClick)="onClick($event)"></dx-button>
3-
</div>
1+
<dx-data-grid
2+
id="grid-container"
3+
[dataSource]="employees"
4+
keyExpr="ID"
5+
[showBorders]="true"
6+
(onEditCanceled)="onEditCanceled($event)"
7+
(onSaved)="onSaved($event)"
8+
>
9+
<dxo-editing mode="popup" [allowUpdating]="true">
10+
<dxo-popup title="Employee Info" [showTitle]="true" [width]="700">
11+
</dxo-popup>
12+
<dxo-form>
13+
<dxi-item itemType="group" [colCount]="2" [colSpan]="2">
14+
<dxi-item dataField="Prefix"></dxi-item>
15+
<dxi-item dataField="FirstName"></dxi-item>
16+
<dxi-item dataField="LastName"></dxi-item>
17+
<dxi-item dataField="Position"></dxi-item>
18+
<dxi-item dataField="BirthDate"></dxi-item>
19+
<dxi-item dataField="HireDate"></dxi-item>
20+
</dxi-item>
21+
<dxi-item itemType="group" caption="Photo" [colCount]="2" [colSpan]="2">
22+
<dxi-item dataField="Picture" [colSpan]="2"></dxi-item>
23+
</dxi-item>
24+
</dxo-form>
25+
</dxo-editing>
26+
27+
<dxi-column
28+
dataField="Picture"
29+
[width]="70"
30+
[allowFiltering]="false"
31+
[allowSorting]="false"
32+
cellTemplate="cellTemplate"
33+
editCellTemplate="editCellTemplate"
34+
>
35+
</dxi-column>
36+
<dxi-column dataField="Prefix" [width]="70" caption="Title"></dxi-column>
37+
<dxi-column dataField="FirstName"></dxi-column>
38+
<dxi-column dataField="LastName"></dxi-column>
39+
<dxi-column dataField="Position"></dxi-column>
40+
<dxi-column dataField="BirthDate" dataType="date"></dxi-column>
41+
<dxi-column dataField="HireDate" dataType="date"></dxi-column>
42+
43+
<div *dxTemplate="let data of 'cellTemplate'">
44+
<img [src]="backendURL + data.value" alt="Employee picture" />
45+
</div>
46+
<div *dxTemplate="let data of 'editCellTemplate'">
47+
<img
48+
#uploadedImage
49+
class="uploaded-image"
50+
[src]="backendURL + data.value"
51+
alt="Employee picture"
52+
/>
53+
<dx-file-uploader
54+
#fileUploader
55+
[multiple]="false"
56+
accept="image/*"
57+
uploadMode="instantly"
58+
[uploadUrl]="backendURL + 'FileUpload/post'"
59+
(onValueChanged)="onValueChanged($event)"
60+
(onUploaded)="onUploaded($event, data)"
61+
(onUploadError)="onUploadError($event)"
62+
></dx-file-uploader>
63+
<dx-button
64+
class="retry-button"
65+
text="Retry"
66+
[visible]="retryButtonVisible"
67+
(onClick)="onClick($event)"
68+
></dx-button>
69+
</div>
70+
</dx-data-grid>

Angular/src/app/app.component.scss

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
1-
.default-style {
2-
margin: 50px;
3-
width: 90vw;
1+
::ng-deep #grid-container {
2+
min-height: 530px;
3+
width: 1000px;
4+
}
5+
6+
::ng-deep .dx-row img {
7+
height: 50px;
8+
}
9+
10+
::ng-deep .retry-button {
11+
margin-left: 7px;
12+
}
13+
14+
::ng-deep .uploaded-image {
15+
height: 50px;
16+
margin-left: 7px;
17+
margin-bottom: 7px;
418
}

Angular/src/app/app.component.ts

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,87 @@
1-
import { Component } from '@angular/core';
2-
import { ClickEvent } from 'devextreme/ui/button';
1+
import { Component, ViewChild } from '@angular/core';
2+
import type { DxDataGridTypes } from 'devextreme-angular/ui/data-grid';
3+
import type { DxFileUploaderTypes } from 'devextreme-angular/ui/file-uploader';
4+
import { DxFileUploaderComponent } from 'devextreme-angular/ui/file-uploader';
5+
import type { DxButtonTypes } from 'devextreme-angular/ui/button';
6+
import { Service, type Employee } from './app.service';
37

48
@Component({
59
selector: 'app-root',
610
templateUrl: './app.component.html',
711
styleUrls: ['./app.component.scss'],
12+
providers: [Service],
813
})
914
export class AppComponent {
10-
title = 'Angular';
15+
title = 'DataGrid - How to use FileUploader in an edit form';
1116

12-
counter = 0;
17+
employees: Employee[];
1318

14-
buttonText = 'Click count: 0';
19+
backendURL = 'http://localhost:5020/';
1520

16-
onClick(e: ClickEvent): void {
17-
this.counter++;
18-
this.buttonText = `Click count: ${this.counter}`;
21+
retryButtonVisible = false;
22+
23+
@ViewChild('uploadedImage') uploadedImageRef!: HTMLImageElement;
24+
25+
@ViewChild('fileUploader') fileUploaderRef!: DxFileUploaderComponent;
26+
27+
constructor(service: Service) {
28+
this.employees = service.getEmployees();
29+
}
30+
31+
onClick(_e: DxButtonTypes.ClickEvent): void {
32+
// The retry UI/API is not implemented. Use the private API as shown at T611719.
33+
const fileUploaderInstance = this.fileUploaderRef.instance;
34+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
35+
const files = (fileUploaderInstance as any)._files;
36+
if (files) {
37+
for (const file of files) {
38+
delete file.uploadStarted;
39+
}
40+
}
41+
fileUploaderInstance.upload();
42+
}
43+
44+
onValueChanged(e: DxFileUploaderTypes.ValueChangedEvent): void {
45+
if (!e.value || e.value.length === 0) return;
46+
47+
const reader: FileReader = new FileReader();
48+
reader.onload = (args): void => {
49+
if (typeof args.target?.result === 'string') {
50+
this.uploadedImageRef.src = args.target.result;
51+
}
52+
};
53+
reader.readAsDataURL(e.value[0]); // convert to base64 string
54+
}
55+
56+
onUploaded(e: DxFileUploaderTypes.UploadedEvent, cellInfo: DxDataGridTypes.ColumnEditCellTemplateData<Employee, number>): void {
57+
if (e.request?.responseText) {
58+
cellInfo.setValue(`images/employees/${e.request.responseText}`);
59+
this.retryButtonVisible = false;
60+
}
61+
}
62+
63+
onUploadError(e: DxFileUploaderTypes.UploadErrorEvent): void {
64+
const xhttp = e.request;
65+
if (xhttp) {
66+
if (xhttp.status === 400 && e.error?.responseText) {
67+
e.message = e.error.responseText;
68+
}
69+
if (xhttp.readyState === 4 && xhttp.status === 0) {
70+
e.message = 'Connection refused';
71+
}
72+
}
73+
this.retryButtonVisible = true;
74+
}
75+
76+
onEditCanceled(_e: DxDataGridTypes.EditCanceledEvent<Employee, number>): void {
77+
if (this.retryButtonVisible) {
78+
this.retryButtonVisible = false;
79+
}
80+
}
81+
82+
onSaved(_e: DxDataGridTypes.SavedEvent<Employee, number>): void {
83+
if (this.retryButtonVisible) {
84+
this.retryButtonVisible = false;
85+
}
1986
}
2087
}

Angular/src/app/app.module.ts

Lines changed: 4 additions & 0 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';
33
import { DxButtonModule } from 'devextreme-angular/ui/button';
4+
import { DxDataGridModule } from 'devextreme-angular/ui/data-grid';
5+
import { DxFileUploaderModule } from 'devextreme-angular/ui/file-uploader';
46
import { AppRoutingModule } from './app-routing.module';
57
import { AppComponent } from './app.component';
68

@@ -12,6 +14,8 @@ import { AppComponent } from './app.component';
1214
BrowserModule,
1315
AppRoutingModule,
1416
DxButtonModule,
17+
DxDataGridModule,
18+
DxFileUploaderModule,
1519
],
1620
providers: [],
1721
bootstrap: [AppComponent],

Angular/src/app/app.service.ts

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { Injectable } from '@angular/core';
2+
3+
export interface Employee {
4+
ID: number;
5+
FirstName: string;
6+
LastName: string;
7+
Prefix: string;
8+
Position: string;
9+
Picture: string;
10+
BirthDate: string;
11+
HireDate: string;
12+
Address: string;
13+
StateID: number;
14+
}
15+
16+
const employees: Employee[] = [{
17+
ID: 1,
18+
FirstName: 'John',
19+
LastName: 'Heart',
20+
Prefix: 'Mr.',
21+
Position: 'CEO',
22+
Picture: 'images/employees/01.png',
23+
BirthDate: '1964/03/16',
24+
HireDate: '1995/01/15',
25+
Address: '351 S Hill St.',
26+
StateID: 5,
27+
}, {
28+
ID: 2,
29+
FirstName: 'Olivia',
30+
LastName: 'Peyton',
31+
Prefix: 'Mrs.',
32+
Position: 'Sales Assistant',
33+
Picture: 'images/employees/09.png',
34+
BirthDate: '1981/06/03',
35+
HireDate: '2012/05/14',
36+
Address: '807 W Paseo Del Mar',
37+
StateID: 5,
38+
}, {
39+
ID: 3,
40+
FirstName: 'Robert',
41+
LastName: 'Reagan',
42+
Prefix: 'Mr.',
43+
Position: 'CMO',
44+
Picture: 'images/employees/03.png',
45+
BirthDate: '1974/09/07',
46+
HireDate: '2002/11/08',
47+
Address: '4 Westmoreland Pl.',
48+
StateID: 4,
49+
}, {
50+
ID: 4,
51+
FirstName: 'Greta',
52+
LastName: 'Sims',
53+
Prefix: 'Ms.',
54+
Position: 'HR Manager',
55+
Picture: 'images/employees/04.png',
56+
BirthDate: '1977/11/22',
57+
HireDate: '1998/04/23',
58+
Address: '1700 S Grandview Dr.',
59+
StateID: 11,
60+
}, {
61+
ID: 5,
62+
FirstName: 'Brett',
63+
LastName: 'Wade',
64+
Prefix: 'Mr.',
65+
Position: 'IT Manager',
66+
Picture: 'images/employees/05.png',
67+
BirthDate: '1968/12/01',
68+
HireDate: '2009/03/06',
69+
Address: '1120 Old Mill Rd.',
70+
StateID: 13,
71+
}, {
72+
ID: 6,
73+
FirstName: 'Sandra',
74+
LastName: 'Johnson',
75+
Prefix: 'Mrs.',
76+
Position: 'Controller',
77+
Picture: 'images/employees/06.png',
78+
BirthDate: '1974/11/15',
79+
HireDate: '2005/05/11',
80+
Address: '4600 N Virginia Rd.',
81+
StateID: 44,
82+
}, {
83+
ID: 7,
84+
FirstName: 'Kevin',
85+
LastName: 'Carter',
86+
Prefix: 'Mr.',
87+
Position: 'Shipping Manager',
88+
Picture: 'images/employees/07.png',
89+
BirthDate: '1978/01/09',
90+
HireDate: '2009/08/11',
91+
Address: '424 N Main St.',
92+
StateID: 5,
93+
}, {
94+
ID: 8,
95+
FirstName: 'Cynthia',
96+
LastName: 'Stanwick',
97+
Prefix: 'Ms.',
98+
Position: 'HR Assistant',
99+
Picture: 'images/employees/08.png',
100+
BirthDate: '1985/06/05',
101+
HireDate: '2008/03/24',
102+
Address: '2211 Bonita Dr.',
103+
StateID: 4,
104+
}, {
105+
ID: 9,
106+
FirstName: 'Kent',
107+
LastName: 'Samuelson',
108+
Prefix: 'Dr.',
109+
Position: 'Ombudsman',
110+
Picture: 'images/employees/02.png',
111+
BirthDate: '1972/09/11',
112+
HireDate: '2009/04/22',
113+
Address: '12100 Mora Dr',
114+
StateID: 26,
115+
}];
116+
117+
@Injectable()
118+
export class Service {
119+
getEmployees(): Employee[] {
120+
return employees;
121+
}
122+
}

Angular/src/app/orig_app.component.css

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

0 commit comments

Comments
 (0)