Search code examples
angularmultipartform-dataform-dataformbuilder

Upload file in angular using FormBuilder & formdata


I'd like to know how to upload a file using angular and form builder, currently I have only found tutorials using formbuilder with a single file like this File Upload In Angular?

My problem is with the .ts file, how do you append the file to a field in formbuilder? I've seen it done with formData() but can't get them both to work together, any hints are very appreciated!

component.ts

 ngOnInit() {
   this.escolaridad_candidato =  this.fb.group({
      nivel_estudio: [, [Validators.required]],
      escuela: [, [Validators.required]],
      graduacion: [, [Validators.required]],
      certificado: [, [Validators.required]]  <--This is the file I need to append 
   });
 }

    onEstudiosChange(event) {
      const reader = new FileReader();
      if (event.target.files && event.target.files.length) {
        const [file] = event.target.files;
        reader.readAsDataURL(file);

        reader.onload = () => {
          this.exp_academica.patchValue({
            certificado: reader.result
        });
          // need to run CD since file load runs outside of zone
          this.cd.markForCheck();
        };
      }
    }

  // On Submit button
  this.myservice.post('myUrl', this.escolaridad_candidato.rawdata(), configOptions )

component.html

 <form [formGroup]="escolaridad_candidato" >
            <div class="col s6 center-align" >
              <p >Estudios</p>
              <div class="row">
                <div class="col s12">
                  <p >Degree</p>
                    <select class="browser-default" formControlName="nivel_estudio">
                      <option value="" disabled selected>----</option>
                      <option *ngFor="let nivel of listnivelEstudios" value="{{ nivel.id }}">
                        {{ nivel.nombre }}
                      </option>

                    </select>
                </div>
                <div class="col s12 center-align input-field">
                  <p >Escuela </p>
                  <input id="escuela" type="text" class="validate browser-default" formControlName="escuela"/>
                </div>
                <div class="col s12 input-field center-align">
                  <p >Año de graduación </p>
                  <input id="graduacion" type="text" class="validate browser-default" 
                   formControlName="graduacion" />
                </div>
              </div>
              <div class="row">
                <div class="col s12">
                  <p >Certificate </p>
                      <div class="file-field input-field">
                        <div class="btn" >
                          <span>Adjuntar archivo +</span>
                          <input type="file" formControlName="certificado" (change)="onEstudiosChange($event)">
                        </div>
                        <div class="file-path-wrapper">
                          <input class="file-path validate" type="text">
                        </div>
                      </div>
                </div>   
              </div>

            </div>
          </form>

Solution

  • It's weird there's so little documentation on how to do this.

    The answer was relatively simple:

    the html must have an input like this

    <input type="file" (change)="onFileChanged($event, i)">
    

    Using fb you can extract the values of your form with something like const myFormValue = this.myForm.value

    In the .ts file:

    public onFileChanged(event: any) {
        if (event.target.files && event.target.files.length) {
        const file = event.target.files[0];
        this.certEscolar = file;
        }
      }
    
      submit() {
        const myFormValue = this.myForm.value
    
        const myFormData = new FormData();
        for ( let i = 0; i < myFormValue.length; i++ ) {         
          for ( let key of myFormValue) {
            myFormData.append(key, myFormValue[key]);
          }
        }
    
        this.http.post<any>(SOMEURL, myFormData, Config.api.multiContentOptions)
        .subscribe(response => {
           console.log("response from server!", response)
      }
    

    The headers I used were:

    Config.ts

    const MultipartHeaders: HttpHeaders = new HttpHeaders({
      Authorization: 'Token ' + token,
      Accept: 'application/json'
    });
    
    
    
     let Config = {
      api: {
        baseURL: 'http://11.98.155.150/back/ws' //obviously fake
      options: { headers: jsonheaders },
      formatOptions: { headers : formDheaders },
      multiContentOptions: { headers : MultipartHeaders },
      }
    };
    

    It gets a lot trickier with formArrays and FormData, if anyone requests I can upload an example.