#javascript #angular
#javascript #угловой
Вопрос:
Я создаю приложение Angular 9. В этом приложении я получил компонент загрузки изображений.
Чтобы убедиться, что загружены правильные вещи, я хочу проверить размеры выбранных изображений. Я делаю это с помощью приведенного ниже кода (который работает).
let img = new Image();
img.src = window.URL.createObjectURL(fileList[index]);
img.onload = () => {
if (img.width < this.allowedWidth amp;amp; img.height < this.allowedHeight) {
this.uploadValid = true;
this.isDoneUploading = false;
} else {
this.uploadValid = false;
this.uploadErrorMessage = this.getTranslation('files.forms.labels.error_dimenstions');
}
}
Я могу получить доступ к переменным this.allowedWidth и this.allowedHeight, которые я установил в верхней части страницы компонента, но, как ни странно, я не могу установить переменные this.uploadValid и this.uploadErrorMessage .
Или, по крайней мере, я не могу установить их в «глобальном» контексте компонента только в операторе If / else .
Я тоже попробовал следующий код, но это не помогло.
let img = new Image();
let that = this;
img.src = window.URL.createObjectURL(fileList[index]);
img.onload = () => {
if (img.width < that.allowedWidth amp;amp; img.height < that.allowedHeight) {
that.uploadValid = true;
that.isDoneUploading = false;
} else {
that.uploadValid = false;
that.uploadErrorMessage = that.getTranslation('files.forms.labels.error_dimenstions');
}
}
Обновить
Это весь метод, в котором находится этот метод. Он вызывается при выборе файла.
handleDrop(fileList: FileList) {
this.files = [];
this.isDropzoneActive = false;
this.uploadingFileCount = fileList.length
for (let index = 0; index < fileList.length; index ) {
if (this.allowedTypes.includes(fileList[index].type)) {
if (fileList[index].size < this.allowedSize) {
let img = new Image();
img.src = window.URL.createObjectURL(fileList[index]);
img.onload = () => {
if (img.width < this.allowedWidth amp;amp; img.height < this.allowedHeight) {
this.uploadValid = true;
this.isDoneUploading = false;
} else {
this.uploadValid = false;
this.uploadErrorMessage = this.getTranslation('files.forms.labels.error_dimenstions');
}
}
} else {
this.uploadValid = false;
this.uploadErrorMessage = this.getTranslation('files.forms.labels.error_size');
}
} else {
this.uploadValid = false;
this.uploadErrorMessage = this.getTranslation('files.forms.labels.error_format');
}
const item = {
id: null,
file_name: fileList[index].name,
file_type: fileList[index].type,
file_size: fileList[index].size,
file_url: null
}
this.uploadItems.push(item);
const file = {
id: Math.random().toString(36).substr(2, 15),
file: fileList[index],
uploaded: false,
valid: this.uploadValid,
error: this.uploadErrorMessage
}
console.log(file);
this.uploadQueue.push(file);
}
// this.submitUploadForm();
}
Комментарии:
1. Пожалуйста, покажите, как это вложенный код и как он вызывается?
2. Пожалуйста, добавьте больше кода. Здесь сложно получить контекст кода, которым вы поделились, структуру компонентов и т. Д. Обычно импорт изображений выполняется в директивах, а не в компонентах, и включается в форму. Вы также должны использовать FormControl вместо своих собственных переменных.
3. Я обновил вопрос с помощью всего метода. Спасибо! @Jean-XavierRaynaud
4. Это долгий путь, но попробуйте сделать обработчик событий функцией со стрелкой:
handleDrop = (fileList: FileList) => {...}
5. К сожалению, это тоже не сработало. Очень странно, что я могу получить доступ к значениям this.allowedWidth, но я не могу установить значения (по крайней мере, не так, чтобы они изменялись глобально).
Ответ №1:
Используя директиву и FormControl для решения аналогичной проблемы, мой код выглядит следующим образом:
директива:
// angular imports
import { Directive, HostBinding, HostListener, Output, EventEmitter } from '@angular/core';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
// models imports
import { FileHandle } from '../models';
@Directive({
selector: '[appFilesDrop]'
})
export class FilesDropDirective {
@Output() files: EventEmitter<FileHandle[]> = new EventEmitter();
@HostBinding('style.background') private background = '#eee';
constructor(
private sanitizer: DomSanitizer
) { }
@HostListener('dragover', ['$event']) public onDragOver(evt: DragEvent) {
evt.preventDefault();
evt.stopPropagation();
this.background = '#999';
}
@HostListener('dragleave', ['$event']) public onDragLeave(evt: DragEvent) {
evt.preventDefault();
evt.stopPropagation();
this.background = '#eee';
}
@HostListener('drop', ['$event']) public onDrop(evt: DragEvent) {
evt.preventDefault();
evt.stopPropagation();
this.background = '#eee';
const files: FileHandle[] = [];
// tslint:disable-next-line: prefer-for-of
for (let i = 0; i < evt.dataTransfer.files.length; i ) {
const file = evt.dataTransfer.files[i];
const url = this.sanitizer.bypassSecurityTrustUrl(window.URL.createObjectURL(file));
files.push({ file, url });
}
if (files.length > 0) {
this.files.emit(files);
}
}
}
модели.ts:
export interface FileHandle {
url: SafeUrl;
file?: File;
}
html — код:
<!-- Image upload field -->
<ng-container *ngIf="formField.type === 'filesUpload'" class="example-full-width image-field-container">
<mat-form-field
appearance="outline"
floatLabel="always"
class=".example-full-width"
>
<mat-label>{{ objectName }} {{formField.label}}</mat-label>
<!-- This block is only displayed if there's no image -->
<!-- it's necessary because we need a formControlName in a mat-form-field -->
<input
*ngIf="formGroup.controls[formField.key].value === null"
class="image-input"
matInput
[formControlName]="formField.key"
type="text"
appFilesDrop
[placeholder]="formField.placeholder"
autocomplete="off"
(files)="filesDropped($event, formField.key)"
readonly
>
<!-- Display errors if any -->
<mat-error
*ngIf="formGroup.controls[formField.key].hasError('required') amp;amp; (formGroup.controls[formField.key].dirty || formGroup.controls[formField.key].touched)"
>
A {{formField.label.toLocaleLowerCase()}} is required
</mat-error>
<mat-error *ngIf="formGroup.controls[formField.key].hasError('tooLongArray')">
You must drop only one image, you dropped {{formGroup.controls[formField.key].errors.tooLongArray.length}} images.
</mat-error>
<mat-error *ngIf="formGroup.controls[formField.key].hasError('forbiddenMime')">
You must drop files of type {{formGroup.controls[formField.key].errors.forbiddenMime.forbidden.join(', ')}}.
All your files must be of type {{formGroup.controls[formField.key].errors.forbiddenMime.allowed.join(', ')}}
</mat-error>
<mat-error *ngIf="formGroup.controls[formField.key].hasError('tooLargeFile')">
The file(s) {{formGroup.controls[formField.key].errors.tooLargeFile.forbidden.join(', ')}} is/are too big.
The maximum allowed size is {{formGroup.controls[formField.key].errors.tooLargeFile.allowed/1024 | number}}kb
</mat-error>
<!-- If we have an image we need to display it. -->
<!-- We created a hidden mat-form-field and display the image -->
<ng-container *ngIf="formGroup.controls[formField.key].value !== null">
<div class="">
<input matInput [formControlName]="formField.key" style="visibility: hidden;" class="invisible">
<img [src]="formGroup.controls[formField.key].value[0].url" class="image-uploaded">
</div>
<button mat-mini-fab color="warn" (click)="removeFiles(formField.key)"><mat-icon>delete</mat-icon></button>
</ng-container>
</mat-form-field>
</ng-container>
Общая логика проверки немного сложна, потому что я использую сервис, который генерирует FormGroup динамически, но логику легко понять, даже если вы не можете скопировать / вставить ее:
// file fields
case('filesUpload'): {
const options = field.options as FileFieldOptions;
// build the validators
validators.push(mimeTypeValidator(options.allowedMimes));
if (!options.multiple) { validators.push(arrayLengthValidator(1)); }
if (options.maxSize) { validators.push(fileSizeValidator(options.maxSize)); }
// get the initial value
if (!options.multiple amp;amp; initialValue amp;amp; initialValue[field.key]) {
// we only have 1 object, so we will have on url from the api and we can provide the formControl as a 1 object control
initialFieldValue = [{
url: initialValue[field.key]
}] as Array<FileHandle>;
}
break;
Забыл добавить пример валидаторов:
For the file size validator:
// angular imports
import { ValidatorFn, AbstractControl, } from '@angular/forms';
// model imports
import { FileHandle } from '../models';
export function fileSizeValidator(maxFileSize: number): ValidatorFn {
return (control: AbstractControl): {[key: string]: any} | null => {
// validate when there's no value
if (control.value === null || control.value === undefined ) { return null; }
// validate when we only have a url (it comes from server)
if (control.value[0].file === undefined) { return null; }
const fileHandles = control.value as Array<FileHandle>;
const tooLargeFiles = fileHandles.filter(fileHandle => fileHandle.file.size > maxFileSize).map(fileHandle => fileHandle.file.name);
return tooLargeFiles.length > 0 ? {tooLargeFile: {forbidden: tooLargeFiles, allowed: maxFileSize}} : null;
};
}
и валидатор MimeType:
// angular imports
import { ValidatorFn, AbstractControl, } from '@angular/forms';
// model imports
import { FileHandle } from '../models';
export function mimeTypeValidator(allowedMimeTypes: Array<string>): ValidatorFn {
return (control: AbstractControl): {[key: string]: any} | null => {
// validate when there's no value
if (control.value === null || control.value === undefined ) { return null; }
// validate when we only have a url (it comes from server)
if (control.value[0].file === undefined) { return null; }
const fileHandles = control.value as Array<FileHandle>;
const forbiddenMimes = fileHandles.map(fileHandle => fileHandle.file.type).filter(mime => !allowedMimeTypes.includes(mime));
return forbiddenMimes.length > 0 ? {forbiddenMime: {forbidden: forbiddenMimes, allowed: allowedMimeTypes}} : null;
};
}