web-dev-qa-db-fra.com

Transmettre une image de FileReader pour créer Angular 6

J'essaie de créer une interface utilisateur dans laquelle se trouve un formulaire comportant deux champs de texte, un input type="file" et une div que vous pouvez supprimer des images à télécharger avec le reste du formulaire. 

Mon objectif/logique

utilisez la même div pour déposer une image ou cliquez dessus et ouvrez l'explorateur de dossiers comme le input type="file" se comporte. Activer les clics est logique dans les petits écrans où il est pratiquement impossible de "glisser-déposer". Et comme il existe déjà un input type="file" dans le formulaire, il n’ya aucune raison de prendre l’image de la variable div et de l’ajouter au formulaire, etc. J'essaie de prendre l’image déposée dans la variable div. input type="file" et soumettez le formulaire une fois. (Si l'utilisateur a cliqué sur div, alors le input type="file" a déjà une valeur, je suis donc libre de soumettre le formulaire à nouveau).

Voici le code

  <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 -->

Ainsi, lorsque vous cliquez sur imageDrop, appelez réellement la imageChange via (click)='imageInput.click()'

C'est le TypeScript dans le composant.

//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);
  }

Les erreurs

Lorsque j'essaie de faire glisser/déposer une image, j'obtiens ce 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 qui pointe vers cette ligne HTML <div id="imageDrop" (click)='imageInput.click()' (drop)="drop($event)" (dragover)="allowDrop($event)" #imageDrop> mais je suis sûr que cela est lié au 

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

partie de la fonction readfiles.

Des idées sur la façon de résoudre ce problème afin que le fichier puisse obtenir une valeur et être ensuite libre de soumettre le formulaire? 

Merci

5
codebot

Ok, la ligne qui vous a donné l'erreur était this.imageUpload.controls.imageInput.setValue(files[0]);

La raison en est que le navigateur vous empêchera de définir le fichier de cette manière par programme en raison de problèmes de sécurité.

Au lieu de cela, vous pouvez utiliser le e.dataTransfer.files directement:

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

Voici un fork de votre stackblitz

7
user184994

Ce que vous essayez de faire ne peut pas être réalisé de cette façon (cause de ce que tout le monde a dit, raison de sécurité, etc.). 

Pour réaliser ce que vous voulez faire, une solution Angular consiste à créer un composant personnalisé utilisant [(ngModel)] formControlName en implémentant le ControlValueAccessor.

En faisant cela, vous pourrez mettre ce que vous voulez comme valeur dans FormControl.

L'implémentation de ControlValueAccessor fait partie de plusieurs tutoriels: https://blog.angularindepth.com/never-again-be-confused-when-implementing-controlvalueaccessor-in-angular-forms-93b9eee9ee83

Ici vous pouvez trouver mon propre code, un sélecteur d’images travaillant dans un formulaire: 

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

exemple: https://ng-tools.asi.fr/views/showroom/asi-ngtools/components/asi-image-chooser

1
xrobert35