Apologies - Angular and typescript are still relatively Greek to me. Long story short, when I post my form to a C#/.NET 3.1 endpoint, all of the key value pairs from my form are smooshed together into one big key with no corresponding value.
I've put together a form in an Angular component, like
export class DialogFileUpload {
uploadForm = this.fb.group({
isFoo:[''],
hasHeaders:['', [Validators.required]],
distributor:[''],
file:['']
});
date = new FormControl(moment());
distributors: IDistributor[] = [];
get aliases() {
return this.uploadForm.get('aliases') as FormArray;
}
constructor(
public dialogRef: MatDialogRef<DialogFileUpload>,
private fb: FormBuilder,
private distributorService: DistributorService,
private uploadService: UploadService) {}
ngOnInit() {
this.distributorService.getDistributors()
.subscribe(distributors => {
this.distributors = distributors;
})
...
My corresponding HTML looks like
<form [formGroup]="uploadForm" (ngSubmit)="onSubmit()">
<mat-form-field>
<input matInput formControlName="effectiveMonth" [matDatepicker]="picker" [min]="sixMonthsAgo" [max]="today" placeholder="Effective Month">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker
startView="multi-year"
(yearSelected) = "chosenYearHandler($event)"
(monthSelected)= "closeDatePicker($event, picker)">
</mat-datepicker>
</mat-form-field>
<mat-form-field appearance="fill" class="distributor-select">
<mat-label>Distributor</mat-label>
<mat-select formControlName="distributor">
<mat-option value="-1">--</mat-option>
<mat-option *ngFor="let distributor of distributors" [value]="distributor.id">
{{distributor.name}}
</mat-option>
</mat-select>
</mat-form-field>
<input type="file" formControlName="file" (change)="appendFile($event.target.files)">
<mat-radio-group
formControlName="isFoo">
<mat-radio-button value="true">foo</mat-radio-button>
<mat-radio-button value="false">Non-foo</mat-radio-button>
</mat-radio-group>
<mat-radio-group *ngIf="uploadForm.value.isFoo == 'false'"
formControlName="hasHeaders">
<mat-radio-button value="true">Has Headers</mat-radio-button>
<mat-radio-button value="false">Headerless</mat-radio-button>
</mat-radio-group>
<div mat-dialog-actions>
<button mat-button (click)="onNoClick()">Cancel</button>
<button mat-raised-button color="primary" cdkFocusInitial>Ok</button>
</div>
</form>
<br>{{uploadForm.value | json}}
As an aside, the uploadForm.value appears to be formatted correctly Once the form is completed, there's this call:
onSubmit(): void {
console.log(this.uploadForm.value);
this.uploadService.postFileToServer(this.uploadForm.value)
.subscribe({
next: () =>{},
error: () => {},
complete: () =>{
this.dialogRef.close();
window.location.reload();
}
})
}
Which calls this:
postFileToServer(data: FormData) {
let url = this.baseUrl + this.fileUploadUrl;
return this.http.post(url, data, {headers : { "Content-Type" : "Application/x-www-form-urlencoded"}});
}
Should this be application/x-www-form-urlencoded? Multipart/form-data? I get errors in my backend if I use the latter. Anyway, the corresponding endpoint looks like this
[HttpPost]
[DisableRequestSizeLimit]
public IActionResult UploadEdiFile() {
var foo = Request.Form;
Console.WriteLine(foo["distributor"]);
bool isFoo = Request.Form["isFoo"] == "true" ? true : false;
And it turns up that 'isFoo' returns null. In looking at the request object, I see only one key and no value, it's like it's combined all my key values into one lump.
{[{"isFoo":"true","hasHeaders":"true","distributor":"5","file":"","effectiveMonth":"2021-06-21T22:54:18.038Z"}, {}]}
The uploadForm.value || json displayed in browser, however, looks like:
{ "isFoo": "true", "hasHeaders": "true", "distributor": "5", "file": "", "effectiveMonth": "2021-06-21T22:51:39.688Z" }
Where'd I mess up?
Per the comments on the question, I think this is good to go - though it feels more like a workaround than a happy path.
When adding a file to the form, the following is called:
appendFile(uploadFile: File) : void {
this.uploadForm.controls['file'].setValue(uploadFile, uploadFile ? uploadFile.name : '');
}
The new version of my onSubmit method, however, looks like this:
onSubmit(): void {
const formData = new FormData();
formData.append('file', this.uploadForm.get('file').value[0]);
formData.append('isFoo', this.uploadForm.get('isFoo').value);
formData.append('hasHeasders', this.uploadForm.get('hasHeaders').value);
formData.append('effectiveMonth', this.uploadForm.get('effectiveMonth').value);
formData.append('distributor', this.uploadForm.get('distributor').value);
this.uploadService.postFileToServer(formData)
.subscribe({
next: () =>{},
error: () => {},
complete: () =>{
this.dialogRef.close();
window.location.reload();
}
})
}
It now creates a new instance of FormData, and appends each element from the FormGroup, which is then posted. The post method no longer needs some sort of special content-type callout, and indeed the urlencodedform nor multipart/form-data did me any favors.
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments