2

I'm using Angular 13 with PrimeNG. I have this file upload component

<form [formGroup]="form" >
...
    <p-fileUpload [customUpload]="true" (uploadHandler)="uploadFile($event)" [multiple]="false" formControlName="myFile"></p-fileUpload>

How do I bind this to my form's form control? I have this in my service file

  form: FormGroup;
  ...
   this.form = this.fb.group({
    ...
    myFile: [null],
  });


    save(){
    ...
      const formData = new FormData();
      const myObject = this.form.value;
    ...
      console.log("file:" + myObject.myFile);

but even when I upload a file, I repeatedly see "file: null" output and no file is bound to my form control. What's the proper way to bind my p-fileupload value to a form control?

Dave
  • 15,639
  • 133
  • 442
  • 830
  • I suspect you also have some errors related to value accessors in the console with this approach. I managed to have something working using a template driven approach. Check this stackblitz instance: https://stackblitz.com/edit/angular-ivy-ng4zpr?file=src%2Fapp%2Fapp.component.html,src%2Fapp%2Fapp.component.ts – CCBet Aug 09 '22 at 17:17
  • Thanks. Yeah I can get this path to work as well. But I'd really like to know how to wire my form object to just automatically inherit the value of what is uploaded into that p-fileupload component, without my having to intervene. – Dave Aug 09 '22 at 20:19

3 Answers3

1

The error is occurring because you have set the method save() to get the console.log. While you use customUpload and you want to get the file by clicking a button you can use @ViewChild method from angular. View the code and you will get to know how this works.

First give your fileUpload an id like #fileUpload and you don't need to set formControlName here. Instead you can set new FormControl in formBuilder group.

<form [formGroup]="form">
  <p-fileUpload
    #fileUpload
    [customUpload]="true"
    (uploadHandler)="uploadFile($event)"
  ></p-fileUpload>
</form>
<button (click)="save()">save</button> 

then in your .ts file you have to import @ViewChild method and view FileUpload. And in uploadFile event we can get the file and using patchValue method we can set the value of the form field.

export class AppComponent implements OnInit {
  @ViewChild('fileUpload') fileUpload: FileUpload;

  form: FormGroup;

  constructor(private fb: FormBuilder) {}
  ngOnInit() {
    this.form = this.fb.group({
      myFile: new FormControl(),
    });
  }
  uploadFile(event) {
    for (let file of event.files) {
      this.form.patchValue({ myFile: file });
      this.form.get('myFile').updateValueAndValidity();
    }
  }
  save(){
    this.fileUpload.upload();
    console.log(this.form.value.myFile)
  }
}

Demo : stackblitz

debugger
  • 1,442
  • 1
  • 9
  • 14
  • Thanks for this creative solution although I have to ask -- why do we need to patch the value into the form ourselves? Is it not possible to have the value automatically wired into the form like for a text field or a dropdown menu? – Dave Aug 19 '22 at 01:23
  • Yes of course you can. Is it a drop down menu you are selecting? – debugger Aug 19 '22 at 04:27
  • But why are you using `fileUpload` for a dropdown menu? – debugger Aug 19 '22 at 04:46
  • What I'm saying is when I add "formControl='name'" to a p-dropdown or input text, there are no additional steps required to bind to the form -- it just picks up the value based on what the user types in (or selects). I'm curious why we have to add an additional step here to bind the file uploaded to the form and why doesn't it happen automatically. – Dave Aug 19 '22 at 17:31
  • I am not sure but I think it's because `File` is a type of object so we can't read it's properties directly like the other inputs. – debugger Aug 19 '22 at 17:37
0

You can create a custom ControlValueAccessor directive in order to automatically bind the uploaded file to a form control. The template:

<p-fileUpload formControlName="abcFile"></p-fileUpload>

The directive:

import { Directive, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { FileUpload } from 'primeng/fileupload';

@Directive({
  selector: 'p-fileUpload[formControlName], p-fileUpload[formControl]',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => FileUploadControlValueAccessorDirective),
      multi: true,
    },
  ],
})
export class FileUploadControlValueAccessorDirective
  implements ControlValueAccessor
{
  constructor(private fileUpload: FileUpload) {}

  writeValue(value: any): void {
    // update the model and changes logic goes here
  }

  registerOnChange(fn: any): void {
    // notify the outside world about changes (when the user interacts with the input)
    this.fileUpload.onUpload.subscribe(fn);
  }

  registerOnTouched(fn: any): void {
    // here goes the touch logic
    this.fileUpload.onClear.subscribe(fn);
  }

  setDisabledState(isDisabled: boolean) {
    this.fileUpload.disabled = isDisabled;
  }
}

Stackblitz here

Meqwz
  • 1,319
  • 8
  • 14
-1

Try This

<form [formGroup]="form" >
...
    <p-fileUpload [customUpload]="true" name="file-upload" (uploadHandler)="uploadFile($event)" [multiple]="false" formControlName="myFile"></p-fileUpload>

and your form group should be

 form: FormGroup = new FormGroup({});
  ...
   this.form = this.fb.group({
    ...
    myFile: new FormControl(''),
  });


    save(){
    ...
      const myObject = this.form.value;
    ...
      console.log("file:", myObject.myFile)  // do not use + operator, because it converts object to string as [Object Object] and it causes the confusion.

Hope this helps :)