Chrome crashes on uploading files > 1.5 GB with resumablejs in Angular - javascript

I am using resumablejs library with Angular 7. When trying to upload files using resumable, it works till 1.5 GB without any issue, but when file size > 1.5 GB, it works in some cases and crashes in some cases. Not sure what is causing the issue.
It works fine without any issue on Firefox.
How can i make it work with Chrome.
Below is the component. Here we are calculating file size and then displaying it on screen.
export class ResumableAttachmentComponent
implements AfterViewInit, OnDestroy, OnChanges, OnInit {
#Output() fileSuccess = new EventEmitter<any>();
#Output() fileError = new EventEmitter<any>();
#Output() error = new EventEmitter<any>();
#Output() progress = new EventEmitter<any>();
#Output() pause = new EventEmitter<any>();
#Output() complete = new EventEmitter<any>();
#Output() uploadStart = new EventEmitter<any>();
#Output() uploadCancel = new EventEmitter<any>();
#Output() fileTypeError = new EventEmitter<any>();
#Output() removeBannerImage = new EventEmitter<any>();
#Output() onComponentInit = new EventEmitter<any>();
#Input() allowDirectorySelect = false;
#Input() maxFileSize = 1024 * 1024 * 1024 * 5; // 5GB
#Input() maxFiles = 1; // max number of file allowed
#Input() multiple = false;
#Input() pausable = false;
#Input() entityType: string;
#Input() entityIdentifierId: string;
#Input() categoryId: string;
#Input() chunkSize = 1024 * 1024 * 5; // 5 MB per chunk by default
#Input() checkBeforeUpload: () => boolean;
#Input() showCategory: boolean = true;
#Input() showUploadButton: boolean = true;
#Input() showDelete: boolean = false;
#Input() showUploadMessage: boolean = false;
#Input() fileType: string[] = []; // Allowing for both [extension, .extension, mime/type, mime/*]
#Input() theme: string = 'full';
#Input() permissionLevel: number;
#Input() permission: number;
#Input() bannerAttachment: any = null;
#Input() isUploadDisabled: any = false;
#Input() hasThumbnails: boolean = false;
Permissions = Permissions;
PermissionLevel = PermissionLevel;
fileSizeGreaterThanMaxAllowed = false;
numberOfFilesGreaterThanMaxAllowed = false;
// indicate that the upload is paused by user or not
paused = false;
progressBarElt: any;
querySet: boolean = false;
// text to show on the upload button depending on the fact the file upload is paused or not
uploadBtnText = localize('general_Upload');
filesToUpload: Resumable.ResumableFile[] = [];
disableUploadButton = true;
disablePauseButton = true;
disableCancelButton = true;
isChunking = false;
// #ts-ignore
resumable: Resumable;
supportResumable = false;
currentUser: IUserProfile;
baseUrl = titanApiUrl;
destroySubject = new Subject<any>();
showSelectButton: boolean;
#Input() gridSupport: {
gridData: SimpleGrid;
type: 'extend' | null;
onRowSelect: () => {};
};
resumableUploadedFiles: IResumableFileData;
backboneObservable$: Subscription;
showCommentsDialog: boolean;
tireComment: string;
constructor(
private userProfileService: UserProfileService,
private backbone: BackboneService,
private eltRef: ElementRef,
private renderer: Renderer2,
private notifyService: NotificationService,
private confirmationService: ConfirmationDialogService,
private router: Router,
private userPermissionHelper: UserPermissionHelper,
private attachmentService: AttachmentService
) {
this.currentUser = this.userProfileService.userProfile;
// cancel the file upload when user is navigating away from the page that contains the component
this.router.events
.filter(e => e instanceof NavigationStart)
.distinctUntilChanged()
.takeUntil(this.destroySubject.asObservable())
.subscribe((e: NavigationStart) => {
if (this.resumable && this.resumable.isUploading()) {
this.cancelUpload();
}
});
this.showSelectButton = true;
}
ngOnInit() {
this.checkPermission();
this.backboneObservable$ = this.backbone.message.subscribe(item => {
if (
item.type === BBMessageType.Resumable &&
item.subtype === BBMessageSubType.Resumable_Files_Attached
) {
this.resumableUploadedFiles = item.message;
}
});
this.duplicatesDialog.onSubmit = () => {
this.duplicatesDialog.show = false;
this.resumable.files.forEach(item => {
item.resumableObj.opts.query['overwrite'] =
this.duplicatesDialog.confirmedOverwrite.indexOf(
item.fileName
) >= 0;
});
};
this.onComponentInit.emit(this);
}
reset() {
// reset error flags
this.fileSizeGreaterThanMaxAllowed = false;
this.numberOfFilesGreaterThanMaxAllowed = false;
this.removeAllFiles();
this.isChunking = false;
this.paused = false;
this.uploadBtnText = localize('general_Upload');
//this.hideProgressBar = true
this.renderer.setStyle(this.progressBarElt, 'width', 0);
this.disableCancelButton = true;
this.disablePauseButton = true;
this.disableUploadButton = true;
}
ngOnDestroy() {
this.destroySubject.next();
this.backboneObservable$.unsubscribe();
}
ngAfterViewInit() {
if (!this.querySet) {
this.setResumableQuery();
}
}
hasUploadPermission: boolean = true;
ngOnChanges(changes: SimpleChanges) {
let self = this;
//If Test Template
if (
changes.hasOwnProperty('entityIdentifierId') &&
changes.entityIdentifierId.currentValue
) {
if (!self.querySet) {
self.setResumableQuery();
}
if (self.resumable) {
self.entityIdentifierId =
changes.entityIdentifierId.currentValue;
self.resumable.opts.query['entityId'] =
changes.entityIdentifierId.currentValue;
self.resumable.opts.query['entityIdentifierId'] =
changes.entityIdentifierId.currentValue;
}
}
if (changes.hasOwnProperty('entityType') && self.resumable) {
self.resumable.opts.query['entityType'] = this.entityType;
}
//Hijack query to add new/selected category id
if (changes.hasOwnProperty('categoryId') && self.resumable) {
self.resumable.opts.query['categoryId'] = this.categoryId;
}
if (changes.permission && changes.permissionLevel) {
self.hasUploadPermission = this.userPermissionHelper.checkPermission(
this.permission,
this.permissionLevel
);
}
if (changes.fileType && self.resumable) {
self.resumable.opts.fileType = this.fileType;
}
}
submitComment() {
this.resumable.opts.query['comment'] = this.tireComment;
this.resumable.upload();
this.tireComment = '';
this.showCommentsDialog = false;
}
setResumableQuery() {
let maxFileAllowed = this.multiple ? undefined : this.maxFiles; // undefined => allow to upload multiple file
//
// extra parameters to include in the multipart POST with data
let query = {};
if (this.entityType) {
query['entityType'] = this.entityType;
}
if (this.entityIdentifierId) {
query['entityIdentifierId'] = this.entityIdentifierId;
}
if (this.categoryId) {
query['categoryId'] = this.categoryId;
}
if (this.hasThumbnails) {
query['createThumbnail'] = true;
} else {
query['createThumbnail'] = false;
}
let fileUploadOptions: Resumable.ConfigurationHash = {
target: `${this.baseUrl}ResumableFile/Upload`,
simultaneousUploads: 1,
maxFiles: maxFileAllowed,
chunkSize: this.chunkSize,
forceChunkSize: true,
fileParameterName: 'file', // The name of the multipart POST parameter to use for the file chunk
query,
headers: {
userId: this.currentUser.id,
tenantId: this.currentUser.defaultTenantId
},
fileType: this.fileType,
fileTypeErrorCallback: this.onFileTypeError.bind(this)
};
// #ts-ignore
this.resumable = new Resumable(fileUploadOptions);
this.supportResumable = this.resumable.support; // check if the current browser suport resumable
// add event only if the browser support resumable upload
if (this.supportResumable) {
this.progressBarElt = this.eltRef.nativeElement.querySelector(
'.t-resumable-attachment-progress-bar-indicator'
);
let chooseFileBtn;
if (this.theme === 'icon') {
chooseFileBtn = this.eltRef.nativeElement.querySelector(
'.icon-theme-resumable'
);
} else {
chooseFileBtn = this.eltRef.nativeElement.querySelector(
'.choose-file'
);
}
if (chooseFileBtn) {
this.resumable.assignBrowse(
chooseFileBtn,
this.allowDirectorySelect
);
this.resumable.assignDrop(
this.eltRef.nativeElement.querySelector('.drop-area')
);
} else {
setTimeout(() => {
if (!this.querySet) {
this.setResumableQuery();
}
}, 400);
return;
}
this.setEvents();
}
this.querySet = true;
}
uploadTheFile() {
if (
this.checkBeforeUpload &&
typeof this.checkBeforeUpload === 'function'
) {
let checkResult = this.checkBeforeUpload();
// start the upload only if the check function return true
if (checkResult) {
// this.onUploadStart()
this.resumable.upload();
}
} else if (
this.checkBeforeUpload &&
typeof this.checkBeforeUpload !== 'function'
) {
throw new Error(
"The 'checkBeforeUpload' attribute must be a function."
);
} else {
// this.onUploadStart()
this.resumable.upload();
/* temporarily commenting the Comment popup while uploading file for Tire Model Request
if (this.entityType == 'TireModelSpecs') {
this.showCommentsDialog = true;
} else {
this.resumable.upload();
}*/
}
}
pauseFile() {
this.resumable.pause();
}
cancelUpload() {
this.resumable.cancel();
}
private setEvents() {
this.resumable.on('error', this.onError.bind(this));
this.resumable.on('fileError', this.onFileError.bind(this));
this.resumable.on('pause', this.onPause.bind(this));
this.resumable.on('complete', this.onComplete.bind(this));
this.resumable.on('fileSuccess', this.onFileSuccess.bind(this));
this.resumable.on('cancel', this.onUploadCancel.bind(this));
this.resumable.on('uploadStart', this.onUploadStart.bind(this));
//this.resumable.on('uploadStart', this.startUploadAfterCheckIsOk)
this.resumable.on('fileProgress', this.onProgress.bind(this));
this.resumable.on('fileAdded', this.onFileAdded.bind(this));
this.resumable.on('chunkingStart', this.onChunkingStart.bind(this));
this.resumable.on(
'chunkingComplete',
this.onChunkingComplete.bind(this)
);
}
#Output() onFileAdd = new EventEmitter();
private onFileAdded(file: ResumableFile, event) {
this.numberOfFilesGreaterThanMaxAllowed = false;
this.fileSizeGreaterThanMaxAllowed = false;
this.isDragOver = false;
//Getting Preview Image
let resumableFile: ResumableFile = this.resumable.files.find(item => {
// #ts-ignore
return file.file.uniqueIdentifier === item.uniqueIdentifier;
});
// #ts-ignore
resumableFile.fileReader = new FileReader();
// #ts-ignore
resumableFile.fileReader.readAsDataURL(file.file);
/**
* Show preview only if MIME type is of image type
*/
if (resumableFile.file.type.startsWith('image')) {
resumableFile.isPreviewSupported = true;
}
/**
* TODO: Check if total size was being looked at here..
*/
// #ts-ignore
if (this.resumable.getSize() > this.maxFileSize) {
this.fileSizeGreaterThanMaxAllowed = true;
this.filesToUpload = [...this.resumable.files];
} else if (
!this.multiple &&
this.resumable.files &&
this.resumable.files.length > this.maxFiles
) {
this.numberOfFilesGreaterThanMaxAllowed = true;
} else {
this.filesToUpload = [...this.resumable.files]; // get the updated list of files to upload
this.disableUploadButton = false;
this.renderer.setStyle(this.progressBarElt, 'width', 0);
}
this.disableCancelButton = false;
if (this.theme === 'icon') {
this.uploadTheFile();
} else {
this.duplicatesDialog.duplicatesArray = [];
this.checkForDuplicates();
}
this.setRealLastModifiedDate(resumableFile, file.file.lastModified);
this.onFileAdd.emit(this.filesToUpload);
}
setRealLastModifiedDate(file: ResumableFile, dateMS) {
file['resumableObj'].opts.query['documentLastModifiedByDate'] = moment(
dateMS
).format('YYYY/MM/DD HH:mm');
}
duplicatesDialog: {
duplicatesArray: any[];
confirmedOverwrite: any[];
show: boolean;
onSubmit: any;
} = {
duplicatesArray: [],
confirmedOverwrite: [],
show: false,
onSubmit: () => {}
};
checkForDuplicates() {
if (
this.resumableUploadedFiles &&
this.currentUser.tenantFeatureMap.overwriteFile &&
this.resumableUploadedFiles.categoryId === this.categoryId &&
this.resumableUploadedFiles.entityIdentifierId ===
this.entityIdentifierId
) {
this.filesToUpload.forEach(item => {
if (
this.resumableUploadedFiles.data.indexOf(item.fileName) >= 0
) {
this.duplicatesDialog.duplicatesArray.push(item.fileName);
} else {
item['resumableObj'].opts.query['overwrite'] = false;
}
});
}
if (this.duplicatesDialog.duplicatesArray.length > 0) {
this.duplicatesDialog.show = true;
}
}
private onPause() {
this.paused = true;
this.disableUploadButton = false;
this.uploadBtnText = localize('general_Resume');
this.pause.emit();
}
onComplete() {
this.complete.emit();
//Reset filesToUpload after completion
this.removeAllFiles();
}
OnRemoveBannerImg() {
this.confirmationService.setConfirm({
message: localize('general_ConformMessageForDelete'),
header: localize('general_Delete'),
accept: () => {
this.removeBannerImage.emit();
},
reject: () => {}
});
}
// `message` is the response body from the server.
private onFileSuccess(file: ResumableFile, message: any) {
if (this.showUploadMessage) {
this.notifyService.notify({
severity: 'success',
summary: localize('general_Success'),
detail: localize('general_MessageSuccessFileUploaded')
});
}
this.filesToUpload = [];
this.fileSuccess.emit({ file, message });
this.disableCancelButton = true;
this.disablePauseButton = true;
this.disableUploadButton = true;
}
// error on a specific file
private onFileError(file: ResumableFile, message: any) {
this.fileError.emit({ file, message });
}
// general error
private onError(message: any, file: ResumableFile) {
this.error.emit({ file, message });
}
onUploadStart() {
//this.hideProgressBar = false
this.uploadStart.emit(this.resumable.files.length);
this.paused = false;
this.disableUploadButton = true;
this.uploadBtnText = localize('general_Upload');
this.disableCancelButton = false;
this.disablePauseButton = false;
this.disableUploadButton = true;
}
private onProgress(file: ResumableFile) {
let fileClone: any = { ...file };
fileClone.uploaded = file.progress(false);
let totalProgress = this.resumable.progress();
let progressPercentage = Math.floor(totalProgress * 100) + '%';
this.renderer.setStyle(
this.progressBarElt,
'width',
progressPercentage
);
this.progress.emit({ file: fileClone, uploaded: totalProgress });
}
/**
* return individual file upload progress
*/
parseProgress(file: ResumableFile) {
return (
Math.round(Math.floor(file.progress(false) * 100) * 10) / 10 + '%'
);
}
private onUploadCancel() {
this.paused = false;
this.uploadBtnText = localize('general_Upload');
this.filesToUpload = this.resumable.files;
this.renderer.setStyle(this.progressBarElt, 'width', 0);
this.uploadCancel.emit();
this.disableCancelButton = true;
this.disablePauseButton = true;
this.disableUploadButton = true;
}
private removeAllFiles() {
let copy = [...this.resumable.files];
copy.forEach((f: any) => this.resumable.removeFile(f));
this.filesToUpload = this.resumable.files;
this.duplicatesDialog.confirmedOverwrite = [];
this.duplicatesDialog.duplicatesArray = [];
}
private onChunkingStart() {
this.isChunking = true;
}
private onChunkingComplete() {
this.isChunking = false;
}
private onFileTypeError(file, errorCount) {
this.fileTypeError.emit({ file, errorCount });
}
removeFile(file: any) {
this.resumable.removeFile(file);
// update the list when a file is removed
this.filesToUpload = this.resumable.files;
this.onFileAdd.emit(this.filesToUpload);
}
getFileSize(sizeInOctet: number) {
sizeInOctet = +sizeInOctet; // convert to number in case ng pass the value as string
let KB = 1024;
let MB = 1024 * 1024;
let GB = 1024 * 1024 * 1024;
if (sizeInOctet >= KB) {
if (sizeInOctet >= MB) {
if (sizeInOctet >= GB) {
return `${(sizeInOctet / GB).toFixed(2)} GB`;
}
return `${(sizeInOctet / MB).toFixed(2)} MB`;
}
return `${(sizeInOctet / KB).toFixed(2)} KB`;
}
return `${sizeInOctet} Bytes`;
}
// case multiple file
// Hide pause/resume when the upload has completed
// Show pause, hide resume on upload start
isDragOver: boolean = false;
onDrop($event) {
this.isDragOver = false;
}
allowDrop($event) {
this.isDragOver = true;
}
onLeave() {
this.isDragOver = false;
}
hasAccess: boolean = false;
hasReadAccess: boolean = false;
hasDeleteAccess: boolean = false;
hasCreateAccess: boolean = false;
checkPermission() {
if (
this.permission &&
this.permissionLevel &&
this.entityIdentifierId &&
this.entityType
) {
this.hasDeleteAccess =
this.userPermissionHelper.checkPermission(
this.permission,
PermissionLevel.Delete
) ||
this.userPermissionHelper.checkSpecialPermission(
this.permission,
PermissionLevel.Delete,
this.entityIdentifierId,
this.entityType
);
this.hasReadAccess =
this.userPermissionHelper.checkPermission(
this.permission,
PermissionLevel.Read
) ||
this.userPermissionHelper.checkSpecialPermission(
this.permission,
PermissionLevel.Read,
this.entityIdentifierId,
this.entityType
);
this.hasCreateAccess =
this.userPermissionHelper.checkPermission(
this.permission,
PermissionLevel.Create
) ||
this.userPermissionHelper.checkSpecialPermission(
this.permission,
PermissionLevel.Create,
this.entityIdentifierId,
this.entityType
);
this.hasAccess =
this.hasCreateAccess ||
this.hasDeleteAccess ||
this.hasReadAccess;
}
}
checkFileList() {
if (!this.resumable) {
return !this.isChunking;
} else {
return !this.isChunking && !this.resumable.isUploading();
}
}
downloadAttachment(id) {
this.attachmentService.download(id);
}
viewLog(id) {
this.attachmentService.getLogByDocumentId;
}
previewFileUrl: string = '';
previewDialog: boolean = false;
expandImage(previewFileUrl) {
this.previewFileUrl = previewFileUrl;
this.previewDialog = true;
}
}

Related

How to type the function responsible for the transition between the page

i have a problem. I'm beginner learn typescript and write code for SPA App
There is a Class for navigating between pages, the question is how to add types correctly so that it all works (everything works on pure js)
It should work depending on the mode of the class specified when calling the instance, the functionality was not checked on the typescript
interface IOption {
mode: string;
root: string;
}
class Router {
public routes: {
path: string;
cb: {
cb: string;
apply(param: {}, match: RegExpMatchArray): void;
};
}[] = [];
public mode!: string;
public root: string = '/';
public current: string = '';
constructor(options: IOption) {
// this.mode = window.history.pushState ? 'history' : 'hash';
// How correct is it to do so?
if (this.mode === 'history') {
window.history.pushState(null, '', this.root);
} else {
window.location.href = `${window.location.href.replace(/\?(.*)$/, '')}#`;
}
if (options.mode) this.mode = options.mode;
if (options.root) this.root = options.root;
this.listen();
}
add({path, cb}: {path: string; cb: never}) {
this.routes.push({
path,
cb,
});
return this;
}
remove(path: string) {
for (let i: number = 0; i < this.routes.length; i++) {
if (this.routes[i].path === path) {
this.routes.splice(i, 1);
return this;
}
}
return this;
}
flush() {
this.routes = [];
return this;
}
removeSlashes(path: string): string {
return path.replace(/\/$/, '').replace(/^\//, '');
}
getFragment() {
let fragment = '';
if (this.mode === 'history') {
fragment = this.removeSlashes(decodeURI(window.location.pathname + window.location.search));
fragment = fragment.replace(/\?(.*)$/, '');
fragment = this.root !== '/' ? fragment.replace(this.root, '') : fragment;
} else {
const match = window.location.href.match(/#(.*)$/);
fragment = match ? match[1] : '';
}
return this.removeSlashes(fragment);
}
navigate(path: string = '') {
if (this.mode === 'history') {
window.history.pushState(null, '', this.root + this.removeSlashes(path));
} else {
window.location.href = `${window.location.href.replace(/#(.*)$/, '')}#${path}`;
}
return this;
}
listen() {
clearInterval(this.interval as keyof typeof this.interval);
this.interval = setInterval(this.interval, 50);
// TS2322: Type 'Timer' is not assignable to type '() => void'.   Type 'Timer' provides no match for the signature '(): void'.
}
interval() {
if (this.current === this.getFragment()) return;
this.current = this.getFragment();
this.routes.some((route) => {
const match = this.current.match(route.path);
if (match) {
match.shift();
route.cb.apply({}, match);
return match;
}
return false;
});
}
}
i not use type any ( any === error )

angular listen for changes

NgOnChanges update table when changes is detected causes infinite service call , which I dont have any idea why. Maybe someone can help. Thanks.
I have parent component TableMultiSortComponent and I passed output EventEmitter which is the table data .
The event or the output from EventEmitter is passed to child component which I will use the pagesize , pageindex etc to query as you can see on the dataServiceEvent on the child component.
And then the output of the request is dealsListData which I pass back from child to parent as data to be populated to the table.
It should update table when there is changes that is why I put it on ngOnchanges which is the initTableMultiSort but right now it is updating the table infinitely , it should only update once if there is changes
#the table data that I am passing from parent component to child component , this.dataServiceEvent.emit(this.table); , this is what I emit cause I will be needing the pageindex, size etc.
#HTML CODE - Parent Component
<mat-card *ngIf="table !== undefined">
<mat-table mat-table [dataSource]="table.dataSource" matMultiSort (matSortChange)="table.onSortEvent()">
<ng-container *ngFor="let column of table.columns" [matColumnDef]="column.id">
<mat-header-cell class="table-multi-sort-header" *matHeaderCellDef [mat-multi-sort-header]="column.id">
<div>{{column.name}}</div>
<div class="sub-text">{{getColumnSubtitle(column.id)}}</div>
</mat-header-cell>
<mat-cell *matCellDef="let row">
<ng-container *ngIf="column.id !== 'action'; then col; else actionCol"></ng-container>
<ng-template #col>
<app-table-multi-sort-cell-default [cellData]="row" [id]="column.id" [subId]="getColumnSubId(column.id)"></app-table-multi-sort-cell-default>
</ng-template>
<ng-template #actionCol>
<app-table-multi-sort-cell-action [rowData]="row" [actions]="getActions(column.id)" (actionClickEvent)="clickTableAction($event,row)"></app-table-multi-sort-cell-action>
</ng-template>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="table.displayedColumns; sticky:true"></mat-header-row>
<mat-row *matRowDef="let item; columns: table.displayedColumns;"></mat-row>
</mat-table>
<mat-progress-bar *ngIf="isLoading" mode="indeterminate"></mat-progress-bar>
</mat-card>
#ts code- parent component
export class TableMultiSortComponent implements OnInit {
#Input() tableOptions:any;
#Input() tableData:any = [];
#Input() isClientSide:boolean = false;
#Input() isLoading: boolean = false;
#Output() tableActionsEvent = new EventEmitter<any>();
#Output() dataServiceEvent = new EventEmitter<any>() ;
#ViewChild(MatMultiSort, { static: false }) sort: MatMultiSort;
tableConfig: any = TABLE_MULTI_SORT_OPTIONS.DEFAULT;
table:TableData<any>;
displayedColumns: any;
constructor() { }
ngOnInit(): void {
this.initTableMultiSort();
}
initTableMultiSort(){
this.tableConfig = {
...this.tableConfig,
...this.tableOptions
}
this.table = new TableData<any>(this.tableConfig.columns,this.tableConfig.sortParams);
this.table.pageSize = this.tableConfig.pageSize;
this.table.pageIndex = this.tableConfig.pageIndex;
this.table.nextObservable.subscribe(() => { this.getData(); });
this.table.sortObservable.subscribe(() => { this.getData(); });
this.table.previousObservable.subscribe(() => { this.getData(); });
this.table.sizeObservable.subscribe(() => { this.getData(); });
setTimeout(()=>{
this.table.dataSource = new MatMultiSortTableDataSource(this.sort, this.isClientSide);
this.getData();
},0);
}
ngOnChanges(changes: SimpleChanges) {
if (changes.tableData && changes.tableData.currentValue){
console.log("changes" , changes)
this.initTableMultiSort()
}
}
getData(){
this.table.totalElements = 1;
this.table.pageIndex = 0;
this.table.pageSize = 10;
this.table.data = this.tableData;
if(this.dataServiceEvent) {
this.dataServiceEvent.emit(this.table);
}
}
#child component HTML code
<app-table-multi-sort (dataServiceEvent)="dataServiceEvent($event)" [tableOptions]="tableOptions" [tableData]="dealsListData" (tableActionsEvent)="tableActions($event)"></app-table-multi-sort>
#child component ts code
export class DealsTransactionComponent implements OnInit {
#Input() transactionId: any = 2;
#Input() transactionName: string = '-';
#ViewChild(TableMultiSortComponent, { static: true }) tableMultiSortComponent: TableMultiSortComponent;
private currentTableConfig: TableData<any>;
dealsListData: any;
tableOptions: any;
#Input() transaction: any;
isLoading: boolean;
private _destroyed$ = new Subject();
totalDeals : number;
accountId: any;
searchInput: string;
table: any;
constructor(
private dialog: MatDialog,
private dealService: DealService,
private notificationService: NotificationService,
private route: Router,
) {}
ngOnInit(): void {
const currentAccountDetails = localStorage.getItem('currAcct') as any;
if (currentAccountDetails) {
this.accountId = JSON.parse(currentAccountDetails).accountId;
}
this.tableOptions = {
columns:[
{id:'name',name:'Deal Name',subId:'type', subtitle:'Deal Type'},
{id:'annualRentProposed',name:'Annual Rent (Proposed)', subId: 'annualRentCurrent', subtitle:'Annual Rent (Proposed)'},
{id:'firmTermRemain',name:'Firm Term Remaining', subId: 'firmTermAdded', subtitle:'(Current)'},
{id:'maxTerm',name:'Max Available Term'},
{id:'cash',name:'Cash Contribution'},
{id:'action', name: 'Actions', actions:[
{icon:'file_copy', name:'Copy', class:'primary-color'},
{icon:'delete', name: 'Delete', class:'mat-error'},
{icon:'forward', name: 'For Approval', class:'primary-color'}
]}
]
}
}
dataServiceEvent(item) {
this.table = item;
if(this.table) {
this._pageEventMyList();
}
}
private _pageEventMyList() {
if (!this.shouldLoadData(this.currentTableConfig, this.table)) {
return;
}
this.currentTableConfig = this.table;
this.searchInput = '';
this.isLoading = true;
this.dealService
.getAllDeals(
this.accountId,
this.transaction.id,
this.table.pageIndex + 1,
this.table.pageSize,
this.searchInput,
this.table.sortParams,
this.table.sortDirs
)
.pipe(finalize(() => (this.isLoading = false)))
.subscribe({
error: (err) => this.notificationService.showError(err),
next: (res) => {
this.dealsListData = res.totalItemCount;
this.dealsListData = res.lastItemOnPage;
this.totalDeals = res.items.length;
this.dealsListData = res.items;
console.log("this.dealsListData" , this.dealsListData)
},
complete: noop,
});
}
private shouldLoadData(oldTableConfig: TableData<any>, tableConfig: TableData<any>): boolean {
if (!oldTableConfig) {
return true;
}
return oldTableConfig.pageIndex !== tableConfig.pageIndex
|| oldTableConfig.pageSize !== tableConfig.pageSize
|| oldTableConfig.sortParams !== tableConfig.sortParams
|| JSON.stringify(oldTableConfig.sortParams) !== JSON.stringify(tableConfig.sortParams)
|| JSON.stringify(oldTableConfig.sortDirs) !== JSON.stringify(tableConfig.sortDirs);
}
createDeal() {
this.route.navigateByUrl(`deals/detail/${this.transactionId}~create`, {
state: { data: this.transaction },
});
localStorage.removeItem('breadCrumbsPath');
const breadCrumbsPath = [
{
text: 'My transactions',
link: '/transactions',
},
{
text: this.transactionName,
link: `/transactions/overview/${this.transactionId}`,
},
];
localStorage.setItem('breadCrumbsPath', JSON.stringify(breadCrumbsPath));
}
copyDeal(id: number) {
const data = { id : id }
this.duplicateDeal(data)
}
deleteDeal(id: number) {
this.isLoading = true;
this.dealService.delete(id)
.pipe(finalize(() => { this.isLoading = false;}))
.subscribe({
next: (res) => {
this.dealsListData = this.dealsListData.filter((x) => x.id !== id);
this.notificationService.showSuccess('Deal been removed successfully.');
},
error: (err) => {
this.notificationService.showError('Something went wrong, Try again later.');
this.isLoading = false;
},
complete: () => {
this.isLoading = false;
},
});
}
duplicateDeal(data: any) {
this.isLoading = true;
this.dealService.duplicate(data)
.pipe( finalize(() => { this.isLoading = false;}))
.subscribe({
next: (res) => {
let copyData = {
...this.dealsListData.filter((x) => x.id === data.id),
};
copyData.name = copyData.name + '(copy)';
copyData.id = this.dealsListData[this.dealsListData.length - 1].id + 1;
this.notificationService.showSuccess('Deal was successfully duplicated.' );
},
error: (err) => {
this.notificationService.showError('Something went wrong, Try again later.');
this.isLoading = false;
},
complete: () => {
this.isLoading = false;
},
});
}
approveDeal(data: any) {
this.isLoading = true;
this.dealService
.approve(data)
.pipe(finalize(() => { this.isLoading = false; }))
.subscribe({
next: (res) => {
this.notificationService.showSuccess('Deal was approved');
},
error: (err) => {
this.notificationService.showError('Something went wrong, Try again later.');
this.isLoading = false;
},
complete: () => {
this.isLoading = false;
},
});
}
You are emitting this.table whenever you call getData in the TableMultiSortComponent which happens in ngOnInit for the first time.
This event emission (I suppose) triggers the actual data fetch in the parent component, in the tableActions method (could not see the implementation of this method in the code you included). This method is probably doing something similar to _pageEventMyList that gets data from the API and sets the dealsListData property on the TableMultiSortComponent ([tableData]="dealsListData").
This in turn, triggers your onChange in the TableMultiSortComponent and also passes your if check (the tableData is actually changed). From onChange, you call initTableMultiSort that reinitializes this.table and emits it with this.dataServiceEvent.emit(this.table);. This makes all go into a cycle.
I recommend to implement some extra checks to make sure you are not triggering data reload if the table configuration is the same.
In short, it looks like you use the table for providing you data about the pageIndex, pageSize, sortParams and sortDirs, so I suggest to keep information about the value of these properties when you try to load data again. Compare them, and if something is changed, then fetch data
private currentTableConfig: TableData<any>;
private _pageEventMyList() {
if (!shouldLoadData(currentTableConfig, this.table)) {
return;
}
this.currentTableConfig = this.table;
this.searchInput = '';
this.isLoading = true;
this.dealService
.getAllDeals(
this.accountId,
this.transaction.id,
this.table.pageIndex + 1,
this.table.pageSize,
this.searchInput,
this.table.sortParams,
this.table.sortDirs
)
.pipe(finalize(() => (this.isLoading = false)))
.subscribe({
error: (err) => this.notificationService.showError(err),
next: (res) => {
this.dealsListData = res.totalItemCount;
this.dealsListData = res.lastItemOnPage;
this.totalDeals = res.items.length;
this.dealsListData = res.items;
},
complete: noop,
});
}
and the new shouldLoadData method performs the checks:
private shouldLoadData(oldTableConfig: TableData<any>, tableConfig: TableData<any>): boolean {
if (!oldTableConfig) {
return true;
}
return oldTableConfig.pageIndex !== tableConfig.pageIndex
|| oldTableConfig.pageSize !== tableConfig.pageSize
|| JSON.stringify(oldTableConfig.sortParams) !== JSON.stringify(tableConfig.sortParams)
|| JSON.stringify(oldTableConfig.sortDirs) !== JSON.stringify(tableConfig.sortDirs);
}
Disclaimer for using JSON.stringify:
This works, but it is not optimal. If you have another third party library installed, like lodash, you can go for _.isEqual instead (or something similar, that will compare the array contents for you).

Clearcache is not defined

I created this script, however the cache cleaning warning appears in the debug console which is not defined. How can I solve it?
I uploaded the code here https://codepen.io/stiac/pen/ExPjgwe
class NotificationBanner {
constructor(el) {
this.storageKey = 'notifications'
this.el = el
this.id = this.el.dataset.id
this.el.querySelector(".closebutton").onclick = () => this.close()
this.showUnlessDismissed()
}
show() {
this.el.hidden = false
}
close() {
this.el.remove()
this.updateLocalStorage()
}
showUnlessDismissed() {
if(this.getLocalStorage().includes(this.id)) {
this.close()
}
else {
this.show()
}
}
updateLocalStorage() {
const dismissedNotifications = this.getLocalStorage()
if(!dismissedNotifications.includes(this.id)) {
dismissedNotifications.push(this.id)
localStorage.setItem(this.storageKey, JSON.stringify(dismissedNotifications))
}
}
getLocalStorage() {
return JSON.parse(localStorage.getItem(this.storageKey)) || []
}
}
class NotificationBanners {
constructor() {
const notifications = [...document.querySelectorAll(".notification-banner")];
notifications.forEach(function(notification) {
return new NotificationBanner(notification);
})
}
}
new NotificationBanners()
clearcache.onclick = e => localStorage.setItem('notifications', JSON.stringify([]))
It is a script to hide a message. I wish I could set a deadline to make it appear after a few days.

Variable is undefined in p5.js when used with angular?

I am learning WebGL and started developing an Sketch app using p5.js and angular. I have defined boolean variables in the component file so that based on that i want to trigger specific function like ellipse, rectangle, line etc in the draw function. These boolean variables are managed by buttons in another component.
I am getting error as core.js:6014 ERROR TypeError: Cannot read property 'isRectangleMode' of undefined
Component file:
import { Component, OnInit } from '#angular/core';
import * as p5 from 'p5';
import { Subscription } from 'rxjs';
import { HomeService } from '../home.service';
#Component({
selector: 'app-doodle-area',
templateUrl: './doodle-area.component.html',
styleUrls: ['./doodle-area.component.css']
})
export class DoodleAreaComponent implements OnInit {
private p5Init : any;
modeSubs : Subscription;
modeSelected : string = null;
isCircleMode : boolean = false;
isEllipseMode : boolean = false;
isRectangleMode : boolean = false;
isLineMode : boolean = false;
isPointMode : boolean = false;
isBrushMode : boolean = false;
isPenMode : boolean = false;
constructor(private homeService : HomeService) { }
ngOnInit() {
this.createCanvas();
this.homeService.modeSelected
.subscribe(modeSelected => {
this.modeSelected = modeSelected;
console.log(this.modeSelected);
if(this.modeSelected) {
this.modeReset();
if(this.modeSelected === "circle") {
this.isCircleMode = true;
} else if(this.modeSelected === 'ellipse') {
this.isEllipseMode = true;
} else if(this.modeSelected === 'rectangle') {
this.isRectangleMode = true;
} else if(this.modeSelected === 'line') {
this.isLineMode = true;
} else if(this.modeSelected === 'point') {
this.isPointMode = true;
} else if(this.modeSelected === 'brush') {
this.isBrushMode = true;
} else if(this.modeSelected === 'pen') {
this.isPenMode = true;
}
}
});
}
private modeReset() {
this.isCircleMode = false;
this.isEllipseMode = false;
this.isRectangleMode = false;
this.isLineMode = false;
this.isPointMode = false;
this.isBrushMode = false;
this.isPenMode = false;
}
private createCanvas() {
this.p5Init = new p5(this.doodleArea);
}
private doodleArea(p : any) {
p.setup = () => {
p.createCanvas(p.windowWidth - 440, p.windowHeight - 200).parent('doodle-area');
p.background(206,214,224);
p.createP("Hello");
}
p.draw = () => {
if(this.isRectangleMode) {
console.log("Rectangle");
}
p.stroke(0);
if(p.mouseIsPressed === true) {
p.line(p.mouseX, p.mouseY, p.pmouseX, p.pmouseY);
}
}
}
}
Screenshot of the console:
This is issue is because of the scope. Here inside the callback function doodleArea, the scope is not component scoped (this). Here this is undefined and we can not access isRectangleMode of undefined. The possible solution for this is:
In .ts modify the createCanvas() with code given below:
private createCanvas() {
const doodleArea = s => {
s.setup = () => {
let canvas = s.createCanvas(s.windowWidth - 440, s.windowHeight - 200);
canvas.parent("doodle-area");
s.draw = () => {
if (this.isRectangleMode) {
console.log("Rectangle");
}
s.stroke(0);
if (s.mouseIsPressed === true) {
s.line(s.mouseX, s.mouseY, s.pmouseX, s.pmouseY);
}
};
s.keyPressed = () => {
if (s.key === 'c') {
window.location.reload();
}};};
};
this.p5Init = new p5(doodleArea);
}
Below is the sample code of the same:
https://stackblitz.com/edit/angular-s-p5-angular?file=src%2Fapp%2Fapp.component.ts
https://angular-s-p5-angular.stackblitz.io
Hope this will help.

ReferenceError: Error #1056: Cannot create property com.facebook.graph.core:AbstractFacebook::oauth2 on com.facebook.graph.Facebook

First I would like to say I have been trying to get this to work for over a year. I have tried a majority of the tutorials offered online. I ended up literally copying a template I found online and I still can't get this to work. I have a bunch of files I'll post the main two. Can someone please tell my how to fix this error message? I don't mean to plagiarize, If I can just get this to work, I can work on my own based off a working example.
MainGameFile.as
package {
import com.adobe.serialization.json.JSON;
import com.facebook.graph.Facebook;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.MouseEvent;
import flash.net.URLRequest;
public class FlashMobileWeb extends Sprite {
protected static const APP_ID:String = "647743112027883"; //Your App Id
protected static const APP_URL:String = "https://localhost:3000/";
protected var profilePic:Loader;
public function FlashMobileWeb() {
var accessToken:String;
if (loaderInfo.parameters.accessToken != undefined) {
accessToken = String(loaderInfo.parameters.accessToken); //get
the token passed in index.php
}
Facebook.init(APP_ID, onInit, null, accessToken);
loginBtn.addEventListener(MouseEvent.CLICK, handleLoginClick, false, 0, true);
callBtn.addEventListener(MouseEvent.CLICK, handleCallClick, false, 0, true);
profilePic = new Loader();
profilePic.contentLoaderInfo.addEventListener(Event.INIT, handleProfilePicInit, false, 0, true);
profilePic.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, handleProfilePicIOError, false, 0, true);
profileHolder.addChild(profilePic);
}
protected function onInit(response:Object, fail:Object):void {
if (response) {
outputTxt.appendText("Logged In\n");
loginBtn.label = "Logout";
} else {
outputTxt.appendText("Click to Login\n");
loginBtn.label = "Login";
}
}
protected function handleLoginClick(event:MouseEvent):void {
if (loginBtn.label == "Login") {
var redirectUri:String = APP_URL; //Your App URL as specified in facebook.com/developers app settings
var permissions:Array = ["user_photos", "user_location"];
Facebook.mobileLogin(redirectUri, "touch", permissions);
} else {
outputTxt.appendText("LOGOUT\n");
Facebook.mobileLogout(APP_URL); //Redirect user back to your app url
}
}
protected function onLogout(response:Object):void {
loginBtn.label = "Login";
outputTxt.text = "";
}
protected function handleCallClick(event:MouseEvent):void {
Facebook.api("/me", onApiCall);
}
protected function onApiCall(response:Object, fail:Object):void {
if (response) {
outputTxt.appendText("RESPONSE:\n" + JSON.encode(response) + "\n");
var req:URLRequest = new URLRequest(Facebook.getImageUrl(response.id, "square"));
profilePic.load(req);
profileHolder.nameTxt.text = response.name + "\n";
if (response.location != null) { profileHolder.nameTxt.appendText(response.location.name); }
}
}
protected function handleProfilePicInit(event:Event):void {
profilePic.x = 1;
profilePic.y = profileHolder.height - profilePic.height >> 1;
}
protected function handleProfilePicIOError(event:IOErrorEvent):void {
outputTxt.appendText("Error Loading Profile Pic\n");
}
}
}
AbstractFacebook.as
package com.facebook.graph.core {
import com.facebook.graph.data.FacebookSession;
import com.facebook.graph.net.FacebookRequest;
import flash.net.URLRequestMethod;
import flash.utils.Dictionary;
public class AbstractFacebook {
protected var session:FacebookSession;
protected var openRequests:Dictionary;
public function AbstractFacebook():void {
openRequests = new Dictionary();
}
protected function api(method:String,
callback:Function = null,
params:* = null,
requestMethod:String = 'GET'
):void {
method = (method.indexOf('/') != 0) ? '/'+method : method;
if (session != null) {
if (params == null) { params = {}; }
params.access_token = session.accessToken;
}
var req:FacebookRequest = new FacebookRequest(
FacebookURLDefaults.GRAPH_URL,
requestMethod
);
openRequests[req] = callback;
req.call(method, params, handleRequestLoad);
}
protected function handleRequestLoad(target:FacebookRequest):void {
var resultCallback:Function = openRequests[target];
if (resultCallback === null) {
delete openRequests[target];
}
if (target.success) {
var data:Object = ('data' in target.data) ? target.data.data : target.data;
resultCallback(data, null);
} else {
resultCallback(null, target.data);
}
delete openRequests[target];
}
protected function callRestAPI(methodName:String,
callback:Function = null,
values:* = null,
requestMethod:String = 'GET'
):void {
if (values == null) { values = {}; }
values.format = 'json';
if (session != null) {
values.access_token = session.accessToken;
}
var req:FacebookRequest = new FacebookRequest(
FacebookURLDefaults.API_URL,
requestMethod
);
openRequests[req] = callback;
req.call('/method/' + methodName, values, handleRequestLoad);
}
protected function fqlQuery(query:String, callback:Function):void {
callRestAPI('fql.query', callback, {query:query});
}
protected function deleteObject(method:String, callback:Function = null):void {
var params:Object = {method:'delete'};
api(method, callback, params, URLRequestMethod.POST);
}
protected function getImageUrl(id:String, type:String = null):String {
return FacebookURLDefaults.GRAPH_URL
+ '/'
+ id
+ '/picture'
+ (type != null?'?type=' + type:'');
}
}
}

Categories