如何将输入类型的反应形式提交为包含该文件数据的文件?

时间:2019-05-14 10:35:40

标签: angular angular7 angular-reactive-forms

我需要将CSV文件的内容保存在我的反应式表单中,作为表单控件的输入值。当前默认情况下仅选择文件名,我需要保存该Form Control的文件数据,而不仅仅是文件名。

我尝试了此处提到的一种方法:Angular 7 : How do I submit file/image along with my reactive form?

表示用文件数据修补Form Control值。当我尝试采用这种方法时,出现以下错误:

  

错误DOMException:无法在上设置'value'属性   'HTMLInputElement':此输入元素接受文件名,该文件名可能   只能以编程方式设置为空字符串。       在EmulatedEncapsulationDomRenderer2.push ../ node_modules/@angular/platform-b​​rowser/fesm5/platform-b​​rowser.js.DefaultDomRenderer2.setProperty   (http://localhost:4200/vendor.js:134583:18)       在BaseAnimationRenderer.push ../ node_modules/@angular/platform-b​​rowser/fesm5/animations.js.BaseAnimationRenderer.setProperty中   (http://localhost:4200/vendor.js:133181:27)       在DebugRenderer2.push ../ node_modules/@angular/core/fesm5/core.js.DebugRenderer2.setProperty   (http://localhost:4200/vendor.js:85257:23)       在DefaultValueAccessor.push ../ node_modules/@angular/forms/fesm5/forms.js.DefaultValueAccessor.writeValue(http://localhost:4200/vendor.js:86345:24)中       在http://localhost:4200/vendor.js:87606:27       在http://localhost:4200/vendor.js:88761:65       在Array.forEach()       在FormControl.push ../ node_modules/@angular/forms/fesm5/forms.js.FormControl.setValue   (http://localhost:4200/vendor.js:88761:28)       在FormControl.push ../ node_modules/@angular/forms/fesm5/forms.js.FormControl.patchValue   (http://localhost:4200/vendor.js:88776:14)       在http://localhost:4200/vendor.js:89118:38

 onFileChange(event, formCotrolKey: string) {

     if (event.target.files && event.target.files.length) {
      const [file] = event.target.files;
        this.formGroup.patchValue({
          [formCotrolKey]: file
        });
        // need to run CD since file load runs outside of zone
        this.changeDetectorRef.markForCheck();
      }
  }

2 个答案:

答案 0 :(得分:0)

尝试一下:

HTML

<input #fileInput type="file" name="document" [(ngModel)]="file" />

打字稿:

@ViewChild("fileInput") fileInput;

onFileChange() {
    let fi = this.fileInput.nativeElement;

    if (fi.files && fi.files[0]) {

      let fileToUpload = fi.files[0];
    }
}

我们将在变量 fileToUpload

中获取文件

答案 1 :(得分:0)

此实现完全符合我使用ControlValueAccessor所需的要求。为此,您只需要创建一个实现ControlValueAccessor接口的指令即可。

在下面使用以下代码:

import { ControlValueAccessor } from '@angular/forms';
import { Directive } from '@angular/core';
import { ElementRef } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { ChangeDetectorRef } from '@angular/core';

let noop = () => {
};

@Directive({
    selector: 'input[type=file][observeFiles]',
    host: {
        '(blur)': 'onTouchedCallback()',
        '(change)': 'handleChange( $event.target.files )'
    },
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: FileInputValueAccessorDirective,
            multi: true
        }
    ]
})
export class FileInputValueAccessorDirective implements ControlValueAccessor {

    private elementRef: ElementRef;
    private onChangeCallback: Function;
    private onTouchedCallback: Function;

    // I initialize the file-input value accessor service.
    constructor(elementRef: ElementRef,
        private changeDetectorRef: ChangeDetectorRef) {

        this.elementRef = elementRef;
        this.onChangeCallback = noop;
        this.onTouchedCallback = noop;

    }

       public handleChange(files: FileList): void {

           if (this.elementRef.nativeElement.multiple) {

              this.onChangeCallback(Array.from(files));

           } else {

            const reader = new FileReader();

            reader.readAsDataURL(files[0]);

            reader.onload = () => {        
            this.onChangeCallback(files.length ? reader.result.toString().split(',')[1] : null);
                this.changeDetectorRef.markForCheck();
            };                
        }
    }

    public registerOnChange(callback: Function): void {
        this.onChangeCallback = callback; 
    }

    public registerOnTouched(callback: Function): void {
        this.onTouchedCallback = callback;
    }

    // I set the disabled property of the file input element.
    public setDisabledState(isDisabled: boolean): void {
        this.elementRef.nativeElement.disabled = isDisabled;
    }

    public writeValue(value: any): void {

        if (value instanceof FileList) {

            this.elementRef.nativeElement.files = value;

        } else if (Array.isArray(value) && !value.length) {

            this.elementRef.nativeElement.files = null;

        } else if (value === null) {

            this.elementRef.nativeElement.files = null;

        } else {

            if (console && console.warn && console.log) {    
                console.log('Ignoring attempt to assign non-FileList to input[type=file].');
                console.log('Value:', value);
            }
        }
    }
}

现在将此指令包含在模块数组中的声明数组下:

// Your Directive location
import { FileInputValueAccessorDirective } from 'app/forms/accessors/file-input.accessor';

@NgModule({
  ...
  declarations: [
    ...
    FileInputValueAccessorDirective
  ]
})

最后在您的组件模板中使用:

<input observeFiles [(ngModel)]="fileContent" type="file"  />

确保组件中具有变量fileContent来保存数据。这就是全部。数据将以base 64格式保存在变量fileContent中。

如果不需要base 64编码,则可以在指令中替换以下行:

this.onChangeCallback(files.length ? reader.result.toString().split(',')[1] : null);reader.onload方法内,此行:

this.onChangeCallback(files.length ? atob( reader.result.toString().split(',')[1] ) : null);