I have a problem with Angular's "Reactrive forms". The fact is that I had a "Template Driven" form but I decided to change it to perform validations through these forms. In one of them I upload an image, and in the template forms linking the model with "ngModel" I had no problem, since after filling in the form and sending it, it showed a dialog window notifying that the insertion had been made and redirected to a screen in which all the data of a database table is presented. Now every time I send it with the "Reactive Form" the form is reset, it does not navigate to the presentation screen and does not present the dialog, but it does make the insertions in the database. When I submit the form without uploading image it works as I expect, but if I add an image, nothing of what I want is presented. What is this behavior due to? I just don't get it. Here is some code:
HTML template:
<form [formGroup]="addEntityForm" (submit)="onSubmit()">
<div class="cardContainer">
<mat-form-field appearance="outline" color="accent">
<mat-label>Nombre de la entidad (Logintud máxima: 25)</mat-label>
<input matInput type="text" maxlength="25" name="nombre_entidad"
formControlName="entityName">
</mat-form-field>
<div *ngIf="addEntityForm.controls.entityName.invalid">
<p *ngIf="addEntityForm.controls.entityName.errors.required && addEntityForm.controls.entityName.touched"
class="alert-danger" [@fadeAnimation]="'in'">
Este campo es requerido.
</p>
</div>
<mat-form-field appearance="outline" color="accent">
<mat-label>Cif de la entidad (Logintud máxima: 20)</mat-label>
<input matInput maxlength="20" name="cif_entidad"
formControlName="entityCif">
</mat-form-field>
<div *ngIf="addEntityForm.controls.entityCif.invalid">
<p *ngIf="addEntityForm.controls.entityCif.errors.required && addEntityForm.controls.entityCif.touched"
class="alert-danger" [@fadeAnimation]="'in'">
Este campo es requerido.
</p>
<p *ngIf="addEntityForm.controls.entityCif.errors.invalidCif" class="alert-danger" [@fadeAnimation]="'in'">
Ya existe una entidad con el mismo Cif.
</p>
</div>
<mat-card>
<p>Logo de la entidad</p>
<button mat-raised-button color="primary" type="button" style="position: relative; margin-bottom: 15px;"
matTooltip="Solo archivos de imagen">Seleccionar archivo
<input type="file" #image accept="image/*" (change)="onSelectImage($event)"
style="opacity: 0; cursor: pointer; height: 100%; width: 100%; position: absolute; left: 0; top: 0;" formControlName="entityLogo">
</button>
<div *ngIf="imageToUpload != undefined" [@fadeAnimation]="'in'">
<p>Imagen seleccionada</p>
<mat-chip-list #chipList>
<mat-chip style="margin-bottom: 15px;" [removable]="removable">
{{imageToUpload.name}}
<mat-icon matChipRemove *ngIf="removable" (click)="deleteSelection(image)">cancel</mat-icon>
</mat-chip>
</mat-chip-list>
</div>
<div [@fadeAnimation]="'in'" *ngIf="imageUrl != undefined">
<p>Previsualización de imagen:</p>
<div id="entityImageDiv" style="border: 1px solid #ccc">
<img id="entityImage" src="{{imageUrl}}">
</div>
</div>
</mat-card>
</div>
<div class="buttonContainer">
<button mat-raised-button color="primary" [disabled]="addEntityForm.invalid">Aceptar</button>
</div>
</form>
Code in component:
onSubmit(){
this.entity.nombre_entidad = this.addEntityForm.get('entityName').value;
this.entity.cif_entidad = this.addEntityForm.get('entityCif').value;
let userData = JSON.parse(localStorage.getItem('identity'));
this.entity.usuario_cre = userData.USUARIO;
this._entityService.addEntity(this.entity, this.imageToUpload).subscribe(
response => {
this.openInsertedDialog(response['message']);
this._router.navigate(['/entidades']);
},
error => {
console.log(error);
}
);
}
Image component:
onSelectImage(event){
this.imageToUpload = <File>event.target.files[0];
let reader = new FileReader();
reader.onload = () => {
this.imageUrl = reader.result as string;
}
reader.readAsDataURL(this.imageToUpload);
}
Service:
addEntity(newEntity, file): Observable<Entity> {
let formData = new FormData();
if (file != undefined) {
formData.append('image', file, file.name);
}
let entityKeys = Object.keys(newEntity);
for (let k of entityKeys) {
formData.append(k, newEntity[k]);
}
return this._http.post<Entity>(this.serviceUrl + '/api/entidad/nuevaEntidad', formData);
}
It is as if the form was reset unintentionally. It skips the whole part of the navigation of the router and the presentation of the dialog. Reload and reload the form component. If it gives me too much trouble I'll go back to the template forms and work on a validation on my own. But it would be nice if someone could explain to me more or less the causes of this behavior, when in the template forms everything is done as expected.
Thanks in advance, if you need any additional information let me know. All the best.
EDIT:
I initialize the form with this code in the component's constructor:
this.addEntityForm = fb.group({
'entityName': [this.entity.nombre_entidad, [
Validators.maxLength(25),
Validators.required
]],
'entityCif': [this.entity.cif_entidad, [
Validators.maxLength(20),
Validators.required,
this.entityCifValidation.bind(this),
]],
'entityLogo': [this.entity.ruta_logo]
});
Once I send the data to the service within the response of the observable, I redirect to the screen where all the insertions that have been made in the database are presented:
response => {
this.openInsertedDialog(response['message']);
this._router.navigate(['/entidades']);
},
I managed to understand what was the cause of this behavior. After trying several solutions, and none of them worked, I realized that the application recompiled every time I uploaded a file. This was because the images you were uploading were stored in the web application's directory structure. When detecting changes in the structure, it will be recompiled. I hope this can help someone. The only thing I had to do to solve the problem was to change the path where the files are saved.
All the best.