0

I have a reactive form in Angular 8. On form submission, I need to post the values along with the uploaded file to an API. But I am not quite sure of how to post the file along with values.

<form [formGroup]="detailsForm" (ngSubmit)="onSubmit()">
    <div>
        <label for="desc">Description</label>
        <input type="text" id="desc" formControlName="desc"/>
    </div>
    <div>
        <label for="summary">Summary</label>
        <textarea id="summary" formControlName="summary"></textarea>
    </div>
    <div formGroupName="contact">
        <div>
            <label for="name">Name</label>
            <input type="text" id="name" required formControlName="name"/>
        </div>
        <div>
            <label for="email">Email</label>
            <input type="email" id="email" formControlName="email"/>
        </div>
    </div>
    <div>
        <label for="file">Upload File</label>
        <input type="file" id="file" formControlName="file">
    </div>
    <button type="submit">Submit</button>
</form>

In component

constructor(public httpService: HttpRequestService) { }

onSubmit() {
   this.httpService.postData(this.detailsForm.value).then(
      (result) => {
        this.jsonResponse = result;
      },
      (err) => {
        this.errorResponse = 'Sorry, there was an error processing your request!'; 
      }
   )
}

In service

postData(detailsData) {
    const headers = new HttpHeaders(
      { 'Content-Type': 'multipart/form-data' }
    );
    return new Promise((resolve, reject) =>{
      this.http.post('http://localhost:3000/postData', detailsData, { headers: headers })
      .subscribe(res => resolve(res), err => reject(err))
    });
}

In backend, just for testing purpose

const express = require("express");
const bodyParser = require('body-parser');
const cors = require('cors');

const app = express();
app.use(cors());
// Configuring body parser middleware
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

app.listen(3000, () => {
 console.log("Server running on port 3000");
});

app.post("/postData", (req,res) => {
    console.log(req.body);
});

All the values are logged, but for file I am only getting the path value. How do I get the contents of file(I need to upload and send Excel File).

2

2 Answers 2

1

Here's what I normally do when I want to send a file to the backend.

Html element

        <div class="form-group">
          <input style="color:transparent;" onchange="this.style.color = 'black';" type="file"
            (change)="onImageChange($event)" #bannerPhoto />
        </div>

component.ts

onImageChange(event) {
const reader = new FileReader();
if (event.target.files && event.target.files.length) {
  const [file] = event.target.files;
  reader.readAsDataURL(file);
  reader.onload = () => {
    this.addEventForm.patchValue({
      banner: reader.result
    });
    this.formData.append('banner', event.target.files[0], event.target.files[0].name);
  };
}}

Here's the type of formData and addEventForm variables:

  addEventForm: FormGroup;
  formData: FormData;

How I'm calling the API:

    this.eventService.add(this.formData)
        .subscribe(/*Your code goes here*/)
Sign up to request clarification or add additional context in comments.

4 Comments

I am new to Angular. We are using the FileReader here, to read the file and we patch value on the FormGroup. Why are we doing so? Isn't it enough to append to formData.
I am getting an error on, this.addEventForm.patchValue({ banner: reader.result }); The error is ERROR DOMException: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string. If I have only the file in my form it works fine. But when I add fields to the form I am getting the error.
Sorry for the late reply, as for appending it to the FormGroup I was appending it because I needed it for another thing. And as for the error, please check this answer: stackoverflow.com/a/41938495/9401556
@NHG if you used my answer and it helped you, can you please mark it as answer. If not then its your choice. Thank you.
0

I recommend you to use Multer at the backend side, and create form data request because you have a one file at the request, this condition makes the request a little bit more difficult.

You can check the npm package from: Multer NPM

The next code was extracted and adapted from the page linked before:

const cpUpload = upload.fields([
    { name: 'excel_file', maxCount: 1 }, 
    { name: 'description', maxCount: 1 },
    { name: 'summary', maxCount: 1 },
    { name: 'name', maxCount: 1 },
    { name: 'email', maxCount: 1 }
])
app.post('/postData', cpUpload, function (req, res, next) {
  // req.files is an object (String -> Array) where fieldname is the key, and the value is array of files
  //
  // e.g.
  //  req.files['excel_file'][0] -> File
  //  req.files['excel_file'] -> Array but contains only 1 file
  //
  // req.body will contain the text fields, if there were any
  const file = req.files['excel_file'][0];
  const description = req.body.description;
  ...
})

On the client side, you first need to catch the file selected by the user:

@HostListener('change', ['$event.target.files'])
emitFiles( event: FileList ) {
    const file = event && event.item(0);

    if (file) {
        this.file = file; // this is a variable in the component
    }
}

The next step is to create the applicative FormGroup -> FormData, you can do the task by:

submit() {
    const formData = new FormData();
    formData.append("excel_file", this.file); // be careful, the names appended as a key in the form data must be the same of the defined fields at the backend.
    formData.append("description", this.detailsForm.get("description").value;
    // ... more appends from formGroup
    
    // time to send the request!
    this.httpClient.post(".../postData", formData).subscribe({
        next: okResponseFromServer => {
            // do anything
        },
        error: err => {
            // handle the error
        }
    })
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.