I have been trying to initialise my reactive form with using an observable that I subscribed to. In the form class template, I used the ngOnInit hook to obtain the object that I want, in this case, the product object.
First code is the class template, second code is the html template where I pass the product using a behavioural subject.
I then, will get the error "Property 'product' is used before its initialisation." in the first line of the form control name instance. I think this is because the product form is created before the ngOnInit hook, therefore it could not access the object. Any ideas? Have been stuck here for so long, will appreciate any help!
product-form-component.ts:
export class ProductFormComponent {
productReceived: Subscription;
editMode: boolean = false;
product: product;
constructor(private productService: ProductService) { }
ngOnInit() {
this.productReceived = this.productService.$emitProduct.subscribe((product: product) => {
if (product) {
this.product = product;
this.editMode = true;
}
});
}
productForm = new FormGroup({
name: new FormControl(`${this.product}`, [Validators.required, Validators.minLength(3)]), // THIS LINE
description: new FormControl(''),
specification: new FormControl(''),
price: new FormControl(''),
imagePath: new FormControl(''),
warranty: new FormControl(''),
});
ngOnDestroy() {
this.productReceived.unsubscribe();
}
}
product-item.component.html:
<div style="cursor: pointer; width: 450px;" class="card rounded p-3"
aria-current="true">
<div>
<div class="d-flex w-100 justify-content-between mb-1">
<div class="d-flex" style="gap: 0.75rem;">
<h5>{{ product.name | titlecase}}</h5>
<span style="color: darkred;">${{product.price}}</span>
</div>
<div class="dropdown">
<button class="btn btn-dark dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
Manage Product
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" >Add to Cart</a></li>
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#staticBackdrop" style="cursor: pointer;" (click)="formOpen(product)">Edit</a></li>
<li><a class="dropdown-item" style="cursor: pointer;">Delete</a></li>
</ul>
</div>
</div>
<div class="d-flex flex-row align-items-center" [routerLink]="product.name" (click)="emitProduct(product)">
<img [src]="product.imagePath[0]" style="max-height: 175px">
<ul class="product-desc">
<li>{{productDescList[0]}}</li>
<li>{{productDescList[1]}}</li>
</ul>
</div>
</div>
</div>
<app-modal-pop></app-modal-pop>
product-form.component.html:
<form [formGroup]="productForm">
<h5>{{editMode? 'Edit Product': 'Create Product'}}</h5>
<div>
<app-input-field label="Name" [control]="productForm.get('name')"></app-input-field>
</div>
<div>
<app-input-field label="Description" [control]="productForm.get('description')"></app-input-field>
</div>
<div>
<app-input-field label="Specification" [control]="productForm.get('specification')"></app-input-field>
</div>
<div>
<app-input-field label="Price" [control]="productForm.get('price')"></app-input-field>
</div>
<div>
<app-input-field label="Image Path" [control]="productForm.get('imagePath')"></app-input-field>
</div>
<div>
<app-input-field label="Warranty" [control]="productForm.get('warranty')"></app-input-field>
</div>
<button type="submit" class="btn btn-secondary">Create</button>
</form>
input-field.component.html:
<label class="label">{{label}}:</label>
<input type="text" [formControl]="control" id="name" [ngClass]="{'alert-danger': showErrors()}">
<ng-container *ngIf="control.errors && control.dirty && control.touched">
<div style="color: red;" *ngIf="control.errors.required">
Name is required!
</div>
<div style="color: red;" *ngIf="control.errors.minlength">
3 minimum characters!
</div>
</ng-container>
You are accessing this.product
before it's created.
this.productService.$emitProduct.subscribe
it's fired AFTER you use this.product
it in the form initialization.
Create the form in the constructor and to initialize it in the OnInit function
export class ProductFormComponent {
productReceived?: Subscription;
editMode: boolean = false;
product?: product;
productForm: FormGroup;
constructor(private productService: ProductService) {
this.productForm = new FormGroup({
name: new FormControl('', [Validators.required,Validators.minLength(3)]),
description: new FormControl(''),
specification: new FormControl(''),
price: new FormControl(''),
imagePath: new FormControl(''),
warranty: new FormControl(''),
});
}
ngOnInit() {
this.productReceived = this.productService.$emitProduct.subscribe((product: product) => {
if (product) {
this.product = product;
this.editMode = true;
this.productForm.patchValue({...this.product})
}
});
}
ngOnDestroy() {
this.productReceived.unsubscribe();
}
}