Передайте изображение из FileReader для ввода формы в Angular 6

Я пытаюсь создать пользовательский интерфейс, в котором есть форма с парой текстовых полей, input type="file" и div, в которые вы можете добавлять изображения для загрузки вместе с остальной частью формы.

Моя цель / логика

используйте тот же div, чтобы перетащить изображение или щелкнуть по нему и открыть проводник папок, как ведет себя input type="file". Включение щелчка имеет смысл на маленьких экранах, где «перетаскивать» практически невозможно. И поскольку в форме уже есть input type="file", нет причин брать изображение из div и добавлять его в форму и т.д. и т.д. Я пытаюсь взять изображение, которое было сброшено в div, установить его как значение в input type="file" и отправьте форму один раз. (если пользователь щелкнул div, то input type="file" уже имеет значение, поэтому я снова могу отправить форму снова).

Вот код

  <div id="imageDrop" (click)='imageInput.click()' (drop)="drop($event)" (dragover)="allowDrop($event)" #imageDrop>
  </div> 
  <input type="file" formControlName="imageInput" required #imageInput id="imageInput" (change)='imageChange($event)' > <!-- use css to hide it -->

Итак, когда нажимается imageDrop, фактически вызывается imageChange через (click)='imageInput.click()'

Это машинописный текст в компоненте.

//imageUpload is the name of the reactive form
acceptedImageTypes = {'image/png': true,'image/jpeg': true,'image/gif': true};
@ViewChild('imageDrop') imageDrop; 

allowDrop(e) {
    e.preventDefault();
  }

  drop(e) {
    e.preventDefault();  
     //clear in case we selected something before via click
    this.imageUpload.controls.imageInput.reset();  
    this.imageDrop.innerHTML="";    
    this.checkfiles(e.dataTransfer.files);
  }

  imageChange(event){    
    this.imageDrop.innerHTML="";   
    this.checkfiles(event.target.files);    
  }//imageChange

  checkfiles(files){      
    if (this.acceptedImageTypes[files[0].type] !== true){
      this.imageDrop.nativeElement.innerHTML="Not an image";          
      return;   
    }
    else if (files.length>1){
      this.imageDrop.nativeElement.innerHTML="Only one image/time";           
      return;   
    }    
    else { this.readfiles(files); }
  }//checkfiles

  readfiles(files){
    const reader = new FileReader();
    let image = new Image();
    reader.onload =  (event) =>{
      this.imageDrop.nativeElement.innerHTML="";                
      let fileReader = event.target as FileReader;
      image.src = fileReader.result;
      image.width = 150; 
      this.imageDrop.nativeElement.appendChild(image);      
    };
    reader.readAsDataURL(files[0]);    

    if (this.imageUpload.controls.imageInput.value==null) {
        //if its null then means that we dragging an img, so the previous image from the input file is deleted
        //now we got to put this image to the input file in order to submit the form
      this.imageUpload.controls.imageInput.reset(files[0] );            
    }    
  }//readfiles

  imageUploadSubmitted(){
    //when form submit, for now just check image value to check if its the right one
    console.log('IMAGE VALUE SUBMIT = =  ',this.imageUpload.controls.imageInput.value);
  }

Ошибки

Когда я пытаюсь перетащить изображение, я получаю ERROR DOMException: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string, который указывает на эту строку HTML <div id="imageDrop" (click)='imageInput.click()' (drop)="drop($event)" (dragover)="allowDrop($event)" #imageDrop>, но я уверен, что это связано с

if (this.imageUpload.controls.imageInput.value==null) {
  this.imageUpload.controls.imageInput.reset(files[0] );            
} 

часть функции readfiles.

Есть идеи, как это исправить, чтобы входной файл мог получить значение, а затем можно было отправить форму?

Спасибо


person codebot    schedule 15.09.2018    source источник
comment
@ user184994 Извини, ты потерял меня. Установить что в пустую строку? imageInput? Но я хочу, чтобы изображение было ценностью.   -  person codebot    schedule 15.09.2018
comment
Извините, я неправильно прочитал код. Можете ли вы создать StackBlitz с вашим кодом, который воспроизводит проблему?   -  person user184994    schedule 15.09.2018
comment
Кстати я заменил this.imageUpload.controls.imageInput.reset(files[0] ); на this.imageUpload.controls.imageInput.setValue(files[0]); . setValue здесь имеет больше смысла. Я пытаюсь исправить это, и когда я создам stackblitz, я дам вам знать.   -  person codebot    schedule 15.09.2018
comment
@ user184994 Это stackblitz Я не Я понимаю, почему у него Template parse errors, но, по крайней мере, это весь код, и это дает мне проблему. Спасибо еще раз   -  person codebot    schedule 15.09.2018


Ответы (2)


Хорошо, строка с сообщением об ошибке была this.imageUpload.controls.imageInput.setValue(files[0]);

Причина в том, что браузер не позволит вам программно настроить файл таким образом из-за проблем с безопасностью.

Вместо этого вы можете использовать e.dataTransfer.files напрямую:

let input = this.imageUpload.controls.imageInput as any;
input.files = files;  

Вот форк вашего stackblitz

person user184994    schedule 15.09.2018
comment
Привет еще раз. Отличный улов, я бы не подумал об этом через миллион лет. Но есть проблема. Если я отбрасываю изображение и заполняю имя (2 обязательных ввода для формы), кнопка «Отправить» по-прежнему не активирована, как если бы форма недействительна, поэтому я предполагаю, что входной файл не имеет значения? Если я нажимаю и выбираю изображение, все работает нормально. Какие-либо предложения? Спасибо - person codebot; 17.09.2018
comment
Я пытаюсь подумать, может быть, есть другой способ сделать это, вместо того, чтобы переключать файл с перетаскивания на ввод файла. Может, когда при падении загорится imageInput? Но в этом нет смысла, потому что он снова откроет проводник папок. Есть ли другой способ по умолчанию установить значение входного файла? - person codebot; 17.09.2018
comment
Проверьте этот stackblitz. Использование input.value, похоже, работает в обоих случаях. Проблема в том, что входной файл имеет два разных типа значений (проверьте консоль). Думаю, я мог бы исправить это, если бы установил в качестве значения результат считанного изображения, поэтому я написал закомментированный код в функции readfiles. Но это не работает, выдает ошибку про event.target.result. - person codebot; 17.09.2018
comment
Извините, этот stackblitz - person codebot; 17.09.2018
comment
@codebot просто удалите что-нибудь из функции drop(e) и сохраните ее точно так же, как функцию imageChange, и вы получите точно такие же результаты на консоли - person un.spike; 23.09.2018
comment
Можно ли загрузить файл в папку? - person Pathik Vejani; 28.08.2019

То, что вы пытаетесь сделать, не может быть достигнуто таким образом (из-за того, что говорят все люди, из соображений безопасности и т. Д.).

Чтобы добиться того, что вы хотите сделать, угловое решение заключается в создании настраиваемого компонента, работающего с [(ngModel)] formControlName, путем реализации ControlValueAccessor.

Сделав это, вы сможете поместить все, что захотите, в качестве значения в FormControl.

Реализация ControlValueAccessor является частью многих руководств: https://blog.angularindepth.com/Never-again-be-confused-when-implementing-controlvalueaccessor-in-angular-forms-93b9eee9ee83

Здесь вы можете найти мой собственный код, средство выбора изображений, работающее в форме:

https://github.com/xrobert35/asi-ngtools/blob/master/src/components/asi-image-chooser/asi-image-chooser.component.ts

пример: https://ng-tools.asi.fr/views/showroom/asi-ngtools/components/asi-image-chooser

person xrobert35    schedule 25.09.2018