从<input type="file">上传文件

62

使用 Angular 2 Beta,我似乎无法让 <input type="file"> 正常工作。

使用诊断工具,我可以看到其他 type,如 text,的双向绑定。

<form>
    {{diagnostic}}
    <div class="form-group">
        <label for="fileupload">Upload</label>
        <input type="file" class="form-control" [(ngModel)]="model.fileupload">
    </div>
</form>
在我的 TypeScript 文件中,我有以下诊断行:
get diagnostic() { return JSON.stringify(this.model); }

可能是因为它不是JSON的问题吗?该值为null

我无法真正验证input的值。即使“选择文件...”旁边的文本更新了,由于某种原因我看不到DOM中的差异。


1
我是用这种方式进行文件上传的。看看它是否有帮助:https://dev59.com/AKDia4cB1Zd3GeqPILrW#43288706 - Midhun Darvin
7个回答

67

我认为这不被支持。如果您查看此DefaultValueAccessor指令(请参见https://github.com/angular/angular/blob/master/modules/angular2/src/common/forms/directives/default_value_accessor.ts#L23),您会发现用于更新绑定元素的值是$event.target.value

这在类型为file的输入框中不适用,因为文件对象可以通过$event.srcElement.files访问。

有关更多详细信息,您可以查看此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);
  }
}

我认为客户端无法为该值生成JSON值可能是有意设计的。 - PascalVKooten
事实上,ngModel 可以用于生成 JSON 之外的其他用途;-) 或许可以使用 files 属性来设置绑定的 ngModel 的值,这可能是一种可行的方法... 我的两分钱。 - Thierry Templier
onChange事件无法识别是否上传了相同的文件,且数据发生了变化。 - Kiran Kumar
这似乎不是有效的,event没有srcElement属性。 - phil294
请查看此答案,获取HTMLInputEvent类型的信息。 - hlovdal

40
@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() {
   }

}

31

访问附加文件有一种略微更好的方式。您可以使用模板引用变量来获取输入元素的实例。

以下示例基于第一个答案:

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

这里有一个示例应用程序来演示其功能。

模板引用变量可能会很有用,例如您可以通过@ViewChild直接在控制器中访问它们。


嗨@Belter,这是一个我刚刚制作的工作示例plnkr。你使用的Ng版本是哪个?具体有什么问题? - Frelseren
嗯,我尝试过使用Ng 2.4.0和4.3.0(似乎你正在使用的版本)。您还可以在文档中查看模板引用变量。您是否遇到任何错误? - Frelseren

7

另一种使用模板引用变量和ViewChild的方法,正如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);
  }
}

还可以查看 https://dev59.com/7VkS5IYBdhLWcg3wP0Vq#40165524


3

尝试使用这个小型库,适用于Angular 5.0.0。

使用ng2-file-upload 1.3.0的Quickstart示例:

用户点击自定义按钮,触发隐藏input type="file"的上传对话框,在选择单个文件后自动开始上传。

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 = "";
}

备用库,可以在Angular 4.2.4中使用,但需要一些解决方法来适应Angular 5.0.0。


1
如果您有一个包含多个文件和其他输入的复杂表单,这里有一个解决方案可以与 ngModel 兼容。
它由一个文件输入组件组成,该组件包装了一个简单的文件输入,并实现了 ControlValueAccessor 接口,使其可被 ngModel 使用。该组件将 FileList 对象暴露给 ngModel
此解决方案基于 this 文章。
该组件的使用方式如下:
<file-input name="file" inputId="file" [(ngModel)]="user.photo"></file-input>
<label for="file"> Select file </label>

这是组件代码:

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

这是组件模板:

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

0

尝试一下 (onclick)="this.value = null"

在你的HTML页面中添加onclick方法以删除先前的值,这样用户就可以再次选择相同的文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接