Skip to content

Commit

Permalink
Merge pull request #15508 from primefaces/issue-15435
Browse files Browse the repository at this point in the history
Fixes #15435 - FileUpload | Templating enhancements
  • Loading branch information
cetincakiroglu committed May 9, 2024
2 parents b12220f + 86545cb commit 37d7678
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 16 deletions.
74 changes: 73 additions & 1 deletion src/app/components/fileupload/fileupload.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,20 @@ export interface UploadEvent {
*/
originalEvent: HttpEvent<any>;
}
/**
* Remove uploaded file event.
* @group Events
*/
export interface RemoveUploadedFileEvent {
/**
* Removed file.
*/
file: any;
/**
* Uploaded files.
*/
files: any[];
}
/**
* Form data event.
* @group Events
Expand Down Expand Up @@ -128,6 +142,31 @@ export interface FileUploadTemplates {
* Custom template of file.
*/
file(): TemplateRef<any>;
/**
* Custom template of file.
*/
header(context: {
/**
* File list.
*/
$implicit: any;
/**
* Uploaded files list.
*/
uploadedFiles: any;
/**
* Callback to invoke on choose button click.
*/
chooseCallback: VoidFunction;
/**
* Callback to invoke on clear button click.
*/
clearCallback: VoidFunction;
/**
* Callback to invoke on upload.
*/
uploadCallback: VoidFunction;
}): TemplateRef<any>;
/**
* Custom template of content.
*/
Expand All @@ -136,7 +175,40 @@ export interface FileUploadTemplates {
* File list.
*/
$implicit: any;
}): TemplateRef<{ $implicit: any }>;
/**
* Uploaded files list.
*/
uploadedFiles: any;
/**
* Upload progress.
*/
progress: any;
/**
* Status messages about upload process.
*/
messages: any;
/**
* Callback to invoke on choose button click.
*/
chooseCallback: VoidFunction;
/**
* Callback to invoke on clear button click.
*/
removeFileCallback: VoidFunction;
/**
* Callback to invoke on clear button click.
*/
clearCallback: VoidFunction;
/**
* Callback to invoke on upload.
*/
uploadCallback: VoidFunction;
/**
* Callback to invoke on remove uploaded file, accepts index as a parameter.
* @param index Index of the file to remove.
*/
removeUploadedFileCallback: VoidFunction;
}): TemplateRef<any>;
/**
* Custom template of toolbar.
*/
Expand Down
48 changes: 42 additions & 6 deletions src/app/components/fileupload/fileupload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import {
ViewChild,
ViewEncapsulation,
booleanAttribute,
numberAttribute
numberAttribute,
signal
} from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { BlockableUI, Message, PrimeNGConfig, PrimeTemplate, SharedModule, TranslationKeys } from 'primeng/api';
Expand All @@ -37,7 +38,7 @@ import { ProgressBarModule } from 'primeng/progressbar';
import { RippleModule } from 'primeng/ripple';
import { VoidListener } from 'primeng/ts-helpers';
import { Subscription } from 'rxjs';
import { FileBeforeUploadEvent, FileProgressEvent, FileRemoveEvent, FileSelectEvent, FileSendEvent, FileUploadErrorEvent, FileUploadEvent, FileUploadHandlerEvent } from './fileupload.interface';
import { FileBeforeUploadEvent, FileProgressEvent, FileRemoveEvent, FileSelectEvent, FileSendEvent, FileUploadErrorEvent, FileUploadEvent, FileUploadHandlerEvent, RemoveUploadedFileEvent } from './fileupload.interface';
/**
* FileUpload is an advanced uploader with dragdrop support, multi file uploads, auto uploading, progress tracking and validations.
* @group Components
Expand All @@ -46,6 +47,18 @@ import { FileBeforeUploadEvent, FileProgressEvent, FileRemoveEvent, FileSelectEv
selector: 'p-fileUpload',
template: `
<div [ngClass]="'p-fileupload p-fileupload-advanced p-component'" [ngStyle]="style" [class]="styleClass" *ngIf="mode === 'advanced'" [attr.data-pc-name]="'fileupload'" [attr.data-pc-section]="'root'">
<input
[attr.aria-label]="browseFilesLabel"
#advancedfileinput
type="file"
(change)="onFileSelect($event)"
[multiple]="multiple"
[accept]="accept"
[disabled]="disabled || isChooseDisabled()"
[attr.title]="''"
[attr.data-pc-section]="'input'"
[style.display]="'none'"
/>
<div class="p-fileupload-buttonbar" [attr.data-pc-section]="'buttonbar'">
<ng-container *ngIf="!headerTemplate">
<span
Expand Down Expand Up @@ -100,7 +113,7 @@ import { FileBeforeUploadEvent, FileProgressEvent, FileRemoveEvent, FileSelectEv
</ng-container>
</p-button>
</ng-container>
<ng-container *ngTemplateOutlet="headerTemplate"></ng-container>
<ng-container *ngTemplateOutlet="headerTemplate; context: { $implicit: files, uploadedFiles: uploadedFiles, chooseCallback: choose.bind(this), clearCallback: clear.bind(this), uploadCallback: upload.bind(this) }"></ng-container>
<ng-container *ngTemplateOutlet="toolbarTemplate"></ng-container>
</div>
<div #content class="p-fileupload-content" (dragenter)="onDragEnter($event)" (dragleave)="onDragLeave($event)" (drop)="onDrop($event)" [attr.data-pc-section]="'content'">
Expand All @@ -126,7 +139,7 @@ import { FileBeforeUploadEvent, FileProgressEvent, FileRemoveEvent, FileSelectEv
<ng-template ngFor [ngForOf]="files" [ngForTemplate]="fileTemplate"></ng-template>
</div>
</div>
<ng-container *ngTemplateOutlet="contentTemplate; context: { $implicit: files }"></ng-container>
<ng-container *ngTemplateOutlet="contentTemplate; context: { $implicit: files, uploadedFiles: uploadedFiles, removeUploadedFileCallback: removeUploadedFile.bind(this), progress: progress, messages: msgs }"></ng-container>
<div *ngIf="emptyTemplate && !hasFiles() && !uploadedFileCount" class="p-fileupload-empty">
<ng-container *ngTemplateOutlet="emptyTemplate"></ng-container>
</div>
Expand Down Expand Up @@ -417,6 +430,12 @@ export class FileUpload implements AfterViewInit, AfterContentInit, OnInit, OnDe
* @group Emits
*/
@Output() onImageError: EventEmitter<Event> = new EventEmitter<Event>();
/**
* This event is triggered if an error occurs while loading an image file.
* @param {RemoveUploadedFileEvent} event - Remove event.
* @group Emits
*/
@Output() onRemoveUploadedFile: EventEmitter<RemoveUploadedFileEvent> = new EventEmitter<RemoveUploadedFileEvent>();

@ContentChildren(PrimeTemplate) templates: QueryList<PrimeTemplate> | undefined;

Expand Down Expand Up @@ -490,6 +509,8 @@ export class FileUpload implements AfterViewInit, AfterContentInit, OnInit, OnDe

dragOverListener: VoidListener;

public uploadedFiles = [];

constructor(
@Inject(DOCUMENT) private document: Document,
@Inject(PLATFORM_ID) private platformId: any,
Expand Down Expand Up @@ -741,7 +762,7 @@ export class FileUpload implements AfterViewInit, AfterContentInit, OnInit, OnDe
} else {
this.onError.emit({ files: this.files });
}

this.uploadedFiles.push(...this.files);
this.clear();
break;
case HttpEventType.UploadProgress: {
Expand Down Expand Up @@ -774,13 +795,28 @@ export class FileUpload implements AfterViewInit, AfterContentInit, OnInit, OnDe
this.clearInputElement();
this.cd.markForCheck();
}

/**
* Removes a single file.
* @param {Event} event - Browser event.
* @param {Number} index - Index of the file.
* @group Method
*/
remove(event: Event, index: number) {
this.clearInputElement();
this.onRemove.emit({ originalEvent: event, file: this.files[index] });
this.files.splice(index, 1);
this.checkFileLimit(this.files);
}
/**
* Removes uploaded file.
* @param {Number} index - Index of the file to be removed.
* @group Method
*/
removeUploadedFile(index) {
let removedFile = this.uploadedFiles.splice(index, 1)[0];
this.uploadedFiles = [...this.uploadedFiles];
this.onRemoveUploadedFile.emit({ file: removedFile, files: this.uploadedFiles });
}

isFileLimitExceeded() {
const isAutoMode = this.auto;
Expand Down
46 changes: 37 additions & 9 deletions src/app/showcase/doc/fileupload/templatedoc.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Component } from '@angular/core';
import { Component, inject } from '@angular/core';
import { Code } from '@domain/code';
import { PrimeNGConfig } from 'primeng/api';

@Component({
selector: 'file-upload-template-demo',
Expand All @@ -13,20 +14,20 @@ import { Code } from '@domain/code';
>
<div class="card">
<p-fileUpload name="myfile[]" url="https://www.primefaces.org/cdn/api/upload.php" [multiple]="true" accept="image/*" maxFileSize="1000000">
<ng-template let-file pTemplate="header">
<ng-template pTemplate="header" let-chooseCallback="chooseCallback" let-clearCallback="clearCallback" let-uploadCallback="uploadCallback" let-files="files">
<div class="flex flex-wrap justify-content-between align-items-center flex-1 gap-2">
<div class="flex gap-2">
<p-button (click)="chooseCallback()" icon="pi pi-images" [rounded]="true" [outlined]="true" />
<p-button (click)="uploadEvent(uploadCallback)" icon="pi pi-cloud-upload" [rounded]="true" [outlined]="true" severity="success" [disabled]="!files || files.length === 0" />
<p-button (click)="clearCallback()" icon="pi pi-times" [rounded]="true" [outlined]="true" severity="danger" [disabled]="!files || files.length === 0" />
<p-button (onClick)="choose($event, chooseCallback)" icon="pi pi-images" [rounded]="true" [outlined]="true" />
<p-button (onClick)="uploadEvent(uploadCallback)" icon="pi pi-cloud-upload" [rounded]="true" [outlined]="true" severity="success" [disabled]="!files || files.length === 0" />
<p-button (onClick)="clearCallback()" icon="pi pi-times" [rounded]="true" [outlined]="true" severity="danger" [disabled]="!files || files.length === 0" />
</div>
<p-progressBar [value]="totalSizePercent" [showValue]="false" styleClass="md:w-20rem h-1rem w-full md:ml-auto" [ngClass]="{ 'exceeded-progress-bar': totalSizePercent > 100 }">
<span class="white-space-nowrap">{{ totalSize }}B / 1Mb</span>
</p-progressBar>
</div>
</ng-template>
<ng-template pTemplate="content" let-files>
<div *ngIf="files.length > 0">
<ng-template pTemplate="content" let-files let-uploadedFiles="uploadedFiles" let-removeFileCallback="removeFileCallback" let-removeUploadedFileCallback="removeUploadedFileCallback">
<div *ngIf="uploadedFiles?.length > 0">
<h5>Pending</h5>
<div class="flex flex-wrap p-0 sm:p-5 gap-5">
<div *ngFor="let file of files; let i = index" class="card m-0 px-6 flex flex-column border-1 surface-border align-items-center gap-3">
Expand All @@ -36,7 +37,6 @@ import { Code } from '@domain/code';
<span class="font-semibold">{{ file.name }}</span>
<div>{{ formatSize(file.size) }}</div>
<p-badge value="Pending" severity="warning" />
<p-button icon="pi pi-times" (click)="onRemoveTemplatingFile(file, removeFileCallback, index)" [outlined]="true" [rounded]="true" severity="danger" />
</div>
</div>
</div>
Expand All @@ -50,7 +50,7 @@ import { Code } from '@domain/code';
<span class="font-semibold">{{ file.name }}</span>
<div>{{ formatSize(file.size) }}</div>
<p-badge value="Completed" class="mt-3" severity="success" />
<p-button icon="pi pi-times" (click)="removeUploadedFileCallback(index)" [outlined]="true" [rounded]="true" severity="danger" />
<p-button icon="pi pi-times" (onClick)="removeUploadedFileCallback(index)" [outlined]="true" [rounded]="true" severity="danger" />
</div>
</div>
</div>
Expand All @@ -67,6 +67,34 @@ import { Code } from '@domain/code';
`
})
export class TemplateDoc {
config = inject(PrimeNGConfig);

choose(event, callback) {
callback();
}

onRemoveTemplatingFile(event, file, removeFileCallback, index) {
removeFileCallback(event, index);
}

uploadEvent(callback) {
callback();
}

formatSize(bytes) {
const k = 1024;
const dm = 3;
const sizes = this.config.translation.fileSizeTypes;
if (bytes === 0) {
return `0 \${sizes[0]}`;
}

const i = Math.floor(Math.log(bytes) / Math.log(k));
const formattedSize = parseFloat((bytes / Math.pow(k, i)).toFixed(dm));

return `${formattedSize} ${sizes[i]}`;
}

code: Code = {
basic: `<p-fileUpload name="myfile[]" url="https://www.primefaces.org/cdn/api/upload.php" [multiple]="true" accept="image/*" maxFileSize="1000000">
<ng-template pTemplate="toolbar">
Expand Down

0 comments on commit 37d7678

Please sign in to comment.