如何将输入类型作为文件提交反应性表单,并带有该文件的数据



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

我尝试了这里提到的一种方法:Angular 7:如何提交文件/图像以及我的反应性形式?

它说用文件数据修补表单控制值。当我尝试遵循这种方法时,我会收到以下错误:

错误domexception:无法在 " htmlinputelement":此输入元素接受文件名,可以 仅通过编程方式设置为空字符串。 在emulateCapsulationdomRenderer2.push ../node_modules/@angular/platform-browser/fesm5/platform-browser.js.defaultdomrenderer2.setProperty (http://localhost:4200/vendor.js:134583:18( at baseanimationrenderer.push ../node_modules/@angular/platform-browser/fesm5/animation.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( at DefaultValueAccessor.push ../node_modules/@angular/forms/forms/forms.js.js.defaultvalueaeaccessor.writevalue(http://localhost:4200/vendor.js:86345:24( 在http://localhost:4200/vendor.js:87606:27 在http://localhost:4200/vendor.js:88761:65 在array.foreach(( at formcontrol.push ../node_modules/@angular/forms/fesm5/forms.js.js.s.formcontrol.setvalue (http://localhost:4200/vendor.js:88761:28( 在formcontrol.push ../node_modules/@angular/forms/fesm5/forms.js.s.s.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();
      }
  }

此实现对我的工作完全适用于我使用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来保存数据。一切都需要。数据将以64格式保存在变量fileContent中。

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

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

与此行的方法:

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

最新更新