Skip to content

Commit a0bef5d

Browse files
authored
Merge pull request #212 from freesbi/feature/update_logic_of_upload_file
update upload and add remove to mad-upload-file component
2 parents a834e56 + fb0b53e commit a0bef5d

File tree

9 files changed

+187
-38
lines changed

9 files changed

+187
-38
lines changed

projects/material-addons/src/lib/file-upload/file-upload.component.html

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,29 @@
99
#fileInput
1010
(change)="uploadFile($event.target.files)"
1111
(click)="fileInput.value = null"
12-
[accept]="acceptForInput"
12+
[accept]="acceptedFileTypes"
1313
[id]="id"
1414
[multiple]="multiple"
1515
hidden
1616
type="file"
1717
/>
1818
<mat-card-content>
19-
<mat-icon>vertical_align_top</mat-icon>
20-
{{ text ? text : 'Upload' }}
19+
<ng-container *ngIf="hasSingleFile(); else uploadDefault">
20+
{{ fileList[0].name }}
21+
</ng-container>
22+
23+
<ng-template #uploadDefault>
24+
<mat-icon>vertical_align_top</mat-icon>
25+
{{ text ? text : 'Upload' }}
26+
</ng-template>
2127
</mat-card-content>
2228
</mat-card>
2329

2430
<ng-container *ngIf="showFileList">
2531
<mat-chip-listbox>
26-
<mat-chip *ngFor="let file of fileList" (click)="openFile(file)" class="download">{{ file.name }}</mat-chip>
32+
<mat-chip *ngFor="let file of fileList" (click)="openFile(file)" class="download" [removable]="removable" (removed)="remove(file)">
33+
{{ file.name }}
34+
<mat-icon matChipRemove *ngIf="removable">delete_forever</mat-icon>
35+
</mat-chip>
2736
</mat-chip-listbox>
2837
</ng-container>

projects/material-addons/src/lib/file-upload/file-upload.component.ts

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,48 +22,82 @@ export class FileUploadComponent implements OnInit {
2222
@Input() accept: string[];
2323
@Input() text: string;
2424
@Input() showFileList: boolean = false;
25+
@Input() removable: boolean = true;
2526
@Output() fileEmitter = new EventEmitter<FileList>();
2627
@Output() errorEmitter = new EventEmitter<UploadError>();
2728

2829
fileList: File[] = [];
29-
acceptForInput: string[] = [];
30-
private uploadError: boolean = false;
30+
acceptedFileTypes: string[] = [];
3131

3232
ngOnInit(): void {
33+
this.setAcceptedFileTypes();
34+
}
35+
36+
private setAcceptedFileTypes(): void {
3337
if (this.accept?.length) {
34-
this.accept.forEach((accepted) => this.acceptForInput.push(`.${accepted}`));
38+
this.acceptedFileTypes = this.accept.map((ext) => `.${ext.toLowerCase()}`);
3539
}
3640
}
3741

38-
uploadFile(fileList: FileList): void {
39-
if (!this.multiple && (fileList.length > 1 || this.fileList.length === 1)) {
40-
this.errorEmitter.emit('ONLY_SINGLE_FILE');
41-
this.uploadError = false;
42+
uploadFile(files: FileList): void {
43+
const fileArray = Array.from(files);
44+
45+
if (!this.validateFileList(fileArray)) {
4246
return;
4347
}
44-
if (this.accept && this.accept.length > 0) {
45-
for (let i = 0; i < fileList.length; i++) {
46-
this.getFileEnding(fileList.item(i).name);
47-
}
48+
49+
this.addFiles(fileArray);
50+
this.fileEmitter.emit(this.createFileListFromArray(this.fileList));
51+
}
52+
53+
private validateFileList(fileArray: File[]): boolean {
54+
if (!this.multiple && fileArray.length > 1) {
55+
this.emitError('ONLY_SINGLE_FILE');
56+
return false;
4857
}
49-
if (!this.uploadError) {
50-
for (let i = 0; i < fileList.length; i++) {
51-
this.fileList.push(fileList.item(i));
58+
59+
for (const file of fileArray) {
60+
if (!this.isAcceptedFileType(file.name)) {
61+
this.emitError('FILETYPE_NOT_SUPPORTED');
62+
return false;
5263
}
53-
this.fileEmitter.emit(fileList);
5464
}
55-
this.uploadError = false;
65+
66+
return true;
5667
}
5768

58-
getFileEnding(name: string): void {
59-
const ending = name.substring(name.lastIndexOf('.') + 1);
60-
if (this.accept.filter((a) => a.toLowerCase() === ending.toLowerCase()).length === 0) {
61-
this.errorEmitter.emit('FILETYPE_NOT_SUPPORTED');
62-
this.uploadError = true;
69+
private emitError(errorType: UploadError): void {
70+
this.errorEmitter.emit(errorType);
71+
}
72+
73+
private isAcceptedFileType(fileName: string): boolean {
74+
const fileExtension = fileName.split('.').pop()?.toLowerCase();
75+
return this.acceptedFileTypes.includes(`.${fileExtension}`);
76+
}
77+
78+
private addFiles(fileArray: File[]): void {
79+
if (!this.multiple) {
80+
this.fileList = [];
6381
}
82+
this.fileList.push(...fileArray);
6483
}
6584

66-
openFile(file: File) {
85+
openFile(file: File): void {
6786
window.open(window.URL.createObjectURL(file));
6887
}
88+
89+
remove(file: File): void {
90+
this.fileList = this.fileList.filter((f) => f !== file);
91+
this.fileEmitter.emit(this.createFileListFromArray(this.fileList));
92+
}
93+
94+
private createFileListFromArray(files: File[]): FileList {
95+
const dataTransfer = new DataTransfer();
96+
files.forEach((file) => dataTransfer.items.add(file));
97+
return dataTransfer.files;
98+
}
99+
100+
hasSingleFile(): boolean {
101+
return !this.multiple && this.fileList.length === 1;
102+
}
69103
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<div class="api-specification">
2+
<h2>Properties</h2>
3+
<table>
4+
<thead>
5+
<tr>
6+
<th>Property</th>
7+
<th>Type</th>
8+
<th>Default Value</th>
9+
<th>Description</th>
10+
</tr>
11+
</thead>
12+
<tbody>
13+
<tr>
14+
<td><code>id</code></td>
15+
<td><code>string</code></td>
16+
<td><code>''</code></td>
17+
<td>HTML id of the element.</td>
18+
</tr>
19+
<tr>
20+
<td><code>multiple</code></td>
21+
<td><code>boolean</code></td>
22+
<td><code></code></td>
23+
<td>if <code>false</code> only 1 document can be added, otherwise you can add as much documents as you want.</td>
24+
</tr>
25+
<tr>
26+
<td><code>accept</code></td>
27+
<td><code>string[]</code></td>
28+
<td><code>'[]'</code></td>
29+
<td>is a string array which contains all valid document types.</td>
30+
</tr>
31+
<tr>
32+
<td><code>text</code></td>
33+
<td><code>string</code></td>
34+
<td><code>'Upload'</code></td>
35+
<td>Text in the Upload Field.</td>
36+
</tr>
37+
<tr>
38+
<td><code>showFileList</code></td>
39+
<td><code>boolean</code></td>
40+
<td><code>false</code></td>
41+
<td>Shows the list of added files.</td>
42+
</tr>
43+
<tr>
44+
<td><code>removable</code></td>
45+
<td><code>boolean</code></td>
46+
<td><code>true</code></td>
47+
<td>Uploaded file can be deleted using icon in chip.</td>
48+
</tr>
49+
</tbody>
50+
</table>
51+
52+
<h2>Events</h2>
53+
<table>
54+
<thead>
55+
<tr>
56+
<th>Event</th>
57+
<th>Description</th>
58+
</tr>
59+
</thead>
60+
<tbody>
61+
<tr>
62+
<td><code>fileEmitter</code></td>
63+
<td>Emits when file is uploaded/removed. Contains file list</td>
64+
</tr>
65+
<tr>
66+
<td><code>errorEmitter</code></td>
67+
<td>(ErrorType) ONLY_SINGLE_FILE: if only 1 File is allowed and user tries to add more than 1 File in 1 dialog.</td>
68+
</tr>
69+
<tr>
70+
<td><code>errorEmitter</code></td>
71+
<td>(ErrorType) FILETYPE_NOT_SUPPORTED: if user tries to add unsupported file type</td>
72+
</tr>
73+
</tbody>
74+
</table>
75+
</div>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
.api-specification {
2+
font-family: Arial, sans-serif;
3+
}
4+
5+
.api-specification h2 {
6+
color: #333;
7+
}
8+
9+
.api-specification table {
10+
width: 100%;
11+
border-collapse: collapse;
12+
margin-bottom: 20px;
13+
}
14+
15+
.api-specification th,
16+
.api-specification td {
17+
border: 1px solid #ccc;
18+
padding: 8px;
19+
text-align: left;
20+
}
21+
22+
.api-specification th {
23+
background-color: #f2f2f2;
24+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { Component } from '@angular/core';
2+
3+
@Component({
4+
selector: 'app-upload-demo-api-spec',
5+
standalone: true,
6+
imports: [],
7+
templateUrl: './upload-demo-api-spec.component.html',
8+
styleUrl: './upload-demo-api-spec.component.scss',
9+
})
10+
export class UploadDemoApiSpecComponent {}

src/app/component-demos/upload-demo/upload-demo.component.html

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,9 @@
22
<br />
33
<h2>Behavior</h2>
44
<ul>
5-
<li>clickable with dialog to select files</li>
5+
<li>Clickable with dialog to select files</li>
66
<li>Drag And Drop Functionality</li>
7-
</ul>
8-
9-
<br />
10-
<h2>Inputs</h2>
11-
<ul>
12-
<li>accept: is a string array which contains all valid document types</li>
13-
<li>multiple: if false only 1 document can be added, otherwise you can add as much documents as you want</li>
14-
<li>text: Text in the Upload Field</li>
15-
<li>id: HTML id of the element</li>
16-
<li>showFileList: Shows the list of added files</li>
7+
<li>Replace file with a new one in single upload mode</li>
178
</ul>
189

1910
<br />
@@ -37,3 +28,5 @@ <h2>Output</h2>
3728
</p>
3829

3930
<example-viewer [example]="uploadComponent"></example-viewer>
31+
<h2>API Specification</h2>
32+
<app-upload-demo-api-spec></app-upload-demo-api-spec>

src/app/component-demos/upload-demo/upload-demo.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ import { Example } from '../../components/example-viewer/example.class';
33
import { UploadFileComponent } from '../../example-components/upload-file/upload-file.component';
44
import { ExampleViewerComponent } from '../../components/example-viewer/example-viewer.component';
55
import { TextCodeComponent } from '../../components/text-code/text-code.component';
6+
import { UploadDemoApiSpecComponent } from './upload-demo-api-spec/upload-demo-api-spec.component';
67

78
@Component({
89
selector: 'app-upload-demo',
910
templateUrl: './upload-demo.component.html',
1011
styleUrl: './upload-demo.component.scss',
1112
standalone: true,
12-
imports: [TextCodeComponent, ExampleViewerComponent],
13+
imports: [TextCodeComponent, ExampleViewerComponent, UploadDemoApiSpecComponent],
1314
})
1415
export class UploadDemoComponent {
1516
uploadComponent = new Example(UploadFileComponent, 'upload-file', 'upload');

src/app/example-components/upload-file/upload-file.component.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77

88
<mat-checkbox [(ngModel)]="multiple">{{ 'Allow Multiple Uploads' }}</mat-checkbox>
99
<mat-checkbox [(ngModel)]="showFileList">{{ 'Show File List' }}</mat-checkbox>
10+
<mat-checkbox [(ngModel)]="removable">{{ 'Removable File' }}</mat-checkbox>
1011

1112
<mad-file-upload
1213
[accept]="accept"
1314
[multiple]="multiple"
1415
[text]="'Upload File'"
1516
[showFileList]="showFileList"
17+
[removable]="removable"
1618
[id]="'upload'"
1719
(fileEmitter)="filesEmitted($event)"
1820
(errorEmitter)="errorReceived($event)"

src/app/example-components/upload-file/upload-file.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export class UploadFileComponent {
2020

2121
possibleFileEndings = ['pdf', 'doc', 'jpg', 'xls', 'xlsx', 'docx', 'doc', 'gif', 'png'];
2222
showFileList: boolean = false;
23+
removable: boolean = false;
2324

2425
filesEmitted(fileList: FileList) {
2526
for (let i = 0; i < fileList.length; i++) {

0 commit comments

Comments
 (0)