Как отправить реактивную форму с типом ввода в виде файла с данными для этого файла? - PullRequest
1 голос
/ 14 мая 2019

Мне нужно сохранить содержимое файлов CSV в моей Реактивной форме в качестве входного значения для Контроля формы. В настоящее время по умолчанию выбрано только имя файла, и мне нужно сохранить данные файла для этого элемента управления формы, а не только имя файла.

Я попробовал один из подходов, упомянутых здесь: Angular 7: Как я могу отправить файл / изображение вместе с моей реактивной формой?

Он говорит, что нужно исправить значение Form Control с данными файла. Когда я пытаюсь следовать этому подходу, я получаю следующую ошибку:

ОШИБКА DOMException: не удалось установить свойство 'value' на 'HTMLInputElement': этот элемент ввода принимает имя файла, которое может только программно установить на пустую строку. в EmulatedEncapsulationDomRenderer2.push ../ (http://localhost:4200/vendor.js:134583:18) at BaseAnimationRenderer.push ../ node_modules/@angular/platform-browser/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) at 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 () at FormControl.push ../ node_modules/@angular/forms/fesm5/forms.js.FormControl.setValue (http://localhost:4200/vendor.js:88761:28) at 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 голосов
/ 15 мая 2019

Эта реализация работала для меня именно так, как мне нужно было с помощью 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);
0 голосов
/ 14 мая 2019

Попробуйте:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...