web-dev-qa-db-fra.com

Téléchargement de fichier à partir de <input type = "file">

En utilisant angular 2 beta, je n'arrive pas à faire fonctionner un <input type="file">.

En utilisant le diagnostic, je peux voir une liaison bidirectionnelle pour d’autres types comme text.

<form>
    {{diagnostic}}
    <div class="form-group">
        <label for="fileupload">Upload</label>
        <input type="file" class="form-control" [(ngModel)]="model.fileupload">
    </div>
</form>

Dans mon fichier TypeScript, j'ai la ligne de diagnostic suivante:

get diagnostic() { return JSON.stringify(this.model); }

Se pourrait-il que ce soit la question de ne pas être JSON? La valeur est null.

Je ne peux pas vraiment vérifier la valeur de input. Même si le texte à côté de "Choisir le fichier ..." est mis à jour, je ne vois aucune différence dans le DOM pour une raison quelconque.

57
PascalVKooten

Je pense que ce n'est pas pris en charge. Si vous consultez cette directive DefaultValueAccessor (voir https://github.com/angular/angular/blob/master/modules/angular2/src/common/forms/directives/default_value_accessor.ts#L23 ). Vous verrez que la valeur utilisée pour mettre à jour l'élément lié est $event.target.value.

Ceci ne s'applique pas dans le cas d'entrées de type file car l'objet fichier peut être atteint $event.srcElement.files à la place.

Pour plus de détails, vous pouvez jeter un oeil à cette plunkr: https://plnkr.co/edit/ozZqbxIorjQW15BrDFrg?p=info :

@Component({
  selector: 'my-app',
  template: `
    <div>
      <input type="file" (change)="onChange($event)"/>
    </div>
  `,
  providers: [ UploadService ]
})
export class AppComponent {
  onChange(event) {
    var files = event.srcElement.files;
    console.log(files);
  }
}
66
Thierry Templier
@Component({
  selector: 'my-app',
  template: `
    <div>
      <input name="file" type="file" (change)="onChange($event)"/>
    </div>
  `,
  providers: [ UploadService ]
})
export class AppComponent {
  file: File;
  onChange(event: EventTarget) {
        let eventObj: MSInputMethodContext = <MSInputMethodContext> event;
        let target: HTMLInputElement = <HTMLInputElement> eventObj.target;
        let files: FileList = target.files;
        this.file = files[0];
        console.log(this.file);
    }

   doAnythingWithFile() {
   }

}
38
Ashish Doneriya

Il existe un moyen légèrement meilleur d’accéder aux fichiers joints. Vous pouvez utiliser template reference variable pour obtenir une instance de l'élément input.

Voici un exemple basé sur la première réponse:

@Component({
  selector: 'my-app',
  template: `
    <div>
      <input type="file" #file (change)="onChange(file.files)"/>
    </div>
  `,
  providers: [ UploadService ]
})

export class AppComponent {
  onChange(files) {
    console.log(files);
  }
}

Voici un exemple d'application pour le démontrer en action.

Les variables de référence de modèle peuvent être utiles, par exemple. vous pouvez y accéder via @ViewChild directement dans le contrôleur.

26
Frelseren

Une autre façon d’utiliser la variable de référence du modèle et ViewChild, proposée par Frelseren:

import { ViewChild } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
    <div>
      <input type="file" #fileInput/>
    </div>
  `
})  
export class AppComponent {
  @ViewChild("fileInput") fileInputVariable: any;
  randomMethod() {
    const files = this.fileInputVariable.nativeElement.files;
    console.log(files);
  }
}

Voir aussi https://stackoverflow.com/a/40165524/4361955

5
jhujhul

Essayez cette petite bibliothèque, fonctionne avec Angular 5.0.0

Exemple de démarrage rapide avec ng2-file-upload 1.3.0:

L'utilisateur clique sur le bouton personnalisé, ce qui déclenche la boîte de dialogue de téléchargement depuis un type d'entrée masqué = "fichier". Le téléchargement démarre automatiquement après la sélection d'un fichier. 

app.module.ts:

import {FileUploadModule} from "ng2-file-upload";

your.component.html:

...
  <button mat-button onclick="document.getElementById('myFileInputField').click()" >
    Select and upload file
  </button>
  <input type="file" id="myFileInputField" ng2FileSelect [uploader]="uploader" style="display:none">
...

your.component.ts:

import {FileUploader} from 'ng2-file-upload';
...    
uploader: FileUploader;
...
constructor() {
   this.uploader = new FileUploader({url: "/your-api/some-endpoint"});
   this.uploader.onErrorItem = item => {
       console.error("Failed to upload");
       this.clearUploadField();
   };
   this.uploader.onCompleteItem = (item, response) => {
       console.info("Successfully uploaded");
       this.clearUploadField();

       // (Optional) Parsing of response
       let responseObject = JSON.parse(response) as MyCustomClass;

   };

   // Asks uploader to start upload file automatically after selecting file
  this.uploader.onAfterAddingFile = fileItem => this.uploader.uploadAll();
}

private clearUploadField(): void {
    (<HTMLInputElement>window.document.getElementById('myFileInputField'))
    .value = "";
}

Alternative lib, fonctionne dans Angular 4.2.4, mais nécessite certaines solutions de contournement pour adopter Angular 5.0.0. 

2
Yurii Bratchuk

Si vous avez un formulaire complexe avec plusieurs fichiers et autres entrées, voici une solution qui lit Nice avec ngModel.

Il consiste en un composant d'entrée de fichier qui encapsule une entrée de fichier simple et implémente l'interface ControlValueAccessor pour le rendre consommable par ngModel. Le composant expose l'objet FileList à ngModel.

Cette solution est basée sur this article.

Le composant est utilisé comme ceci:

<file-input name="file" inputId="file" [(ngModel)]="user.photo"></file-input>
<label for="file"> Select file </label>

Voici le code du composant:

import { Component, Input, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';

const noop = () => {
};

export const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => FileInputComponent),
    multi: true
};

@Component({
  selector: 'file-input',
  templateUrl: './file-input.component.html',
  providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR]
})
export class FileInputComponent {

  @Input()
  public name:string;

  @Input()
  public inputId:string;

  private innerValue:any;

  constructor() { }

  get value(): FileList {
    return this.innerValue;
  };

  private onTouchedCallback: () => void = noop;
  private onChangeCallback: (_: FileList) => void = noop;

  set value(v: FileList) {
    if (v !== this.innerValue) {
      this.innerValue = v;
      this.onChangeCallback(v);
    }
  }

  onBlur() {
    this.onTouchedCallback();
  }

  writeValue(value: FileList) {
    if (value !== this.innerValue) {
      this.innerValue = value;
    }
  }

  registerOnChange(fn: any) {
    this.onChangeCallback = fn;
  }

  registerOnTouched(fn: any) {
    this.onTouchedCallback = fn;
  }

  changeFile(event) {
    this.value = event.target.files;
  }
}

Et voici le modèle de composant:

<input type="file" name="{{ name }}" id="{{ inputId }}" multiple="multiple" (change)="changeFile($event)"/>
0
Jens

essayez juste (onclick)="this.value = null"

dans votre page html, ajoutez la méthode onclick pour supprimer la valeur précédente afin que l'utilisateur puisse sélectionner à nouveau le même fichier. 

0
yogesh chavan