I'm trying to fetch data like boolean isCompany and Strings companyName and nip from users table from backend and set it as default in fields in the form. All other data are patched correctly. There is only a problem with these.
The data are being feched correctly and they exist in database. The problem is I get this create-order.component.html:14 ERROR TypeError: Cannot read properties of undefined (reading 'companyForm')
error and these to fields remain empty in the form.
This is how I initialize form in form.service.ts
:
initCompanyForm(): FormGroup {
return new FormGroup({
companyName: new FormControl('', {}),
nip: new FormControl('', {
validators: [Validators.pattern(/^\d{10}$/)]
})
});
}
This is company-form.component.ts
:
export class CompanyFormComponent {
companyForm: FormGroup;
constructor(private formService: FormService) {
this.companyForm = this.formService.initCompanyForm();
}
get controls() {
return this.companyForm.controls as { [key: string]: FormControl };
}
getErrorMessage(control: FormControl): string {
return this.formService.getErrorMessage(control);
}
}
This Angular component create-order.component
manages a multi-step order form with child components for customer, address, delivery, company, and additional information. The issue arises because the CompanyFormComponent is not fully initialized when fetchUserProfile() tries to patch its form values, resulting in undefined errors. Even with ngAfterViewInit, the asynchronous nature of the profile fetch (profileService.getUserProfile()) causes a timing mismatch. I guess the problem is that the profile data arrives before the CompanyFormComponent is ready.
export class CreateOrderComponent implements OnInit, AfterViewInit {
errorMsg: null | string = null;
saveShipping = false;
isAuthenticated = false;
isCompany = false;
@ViewChild(CustomerFormComponent) customerFormComp!: CustomerFormComponent;
@ViewChild(AddressFormComponent) addressFormComp!: AddressFormComponent;
@ViewChild(DeliveryFormComponent) deliveryFormComp!: DeliveryFormComponent;
@ViewChild(CompanyFormComponent) companyFormComp!: CompanyFormComponent;
@ViewChild(InfoFormComponent) infoFormComp!: InfoFormComponent;
constructor(
private location: Location,
private router: Router,
private ordersService: OrdersService,
private profileService: ProfileService,
private authService: AuthService
) {}
ngOnInit(): void {
const locationState = this.location.getState() as {
summaryPrice: undefined | number;
navigationId: number;
};
if (!locationState.summaryPrice) {
this.router.navigate(['']);
}
this.checkIfUserIsLoggedIn();
}
ngAfterViewInit(): void {
if (this.isAuthenticated) {
this.fetchUserProfile();
}
}
checkIfUserIsLoggedIn(): void {
this.authService.isLoggedIn().subscribe({
next: (response) => {
this.isAuthenticated = response.message;
if (this.isAuthenticated) {
this.fetchUserProfile();
}
},
error: (err) => {
this.isAuthenticated = false;
console.error('Error checking login status:', err);
}
});
}
fetchUserProfile(): void {
this.profileService.getUserProfile().subscribe({
next: (profile) => {
console.log('User profile:', profile);
this.customerFormComp.customerForm.patchValue({
firstName: profile.firstName || '',
lastName: profile.lastName || '',
email: profile.email || ''
});
if (profile.phone) {
this.customerFormComp.controls.phone.setValue(profile.phone);
}
this.addressFormComp.addressForm.patchValue({
city: profile.city || '',
street: profile.street || '',
number: profile.number || '',
postCode: profile.postCode || ''
});
if (profile.company) {
this.isCompany = true;
if (this.companyFormComp?.companyForm) { // this if omits the error, but doesn't solve the problem
console.log('Patching company form with:', {
companyName: profile.companyName,
nip: profile.nip
});
this.companyFormComp.companyForm.enable();
this.companyFormComp.companyForm.patchValue({
companyName: profile.companyName || '',
nip: profile.nip || ''
});
}
} else {
this.isCompany = false;
if (this.companyFormComp?.companyForm) {
this.companyFormComp.companyForm.reset();
this.companyFormComp.companyForm.disable();
}
}
},
error: (err) => {
this.errorMsg = 'Nie udało się załadować danych użytkownika';
console.error('Error fetching user profile:', err);
}
});
}
onCompanyChange(): void {
console.log('Checkbox changed:', this.isCompany);
if (this.isCompany) {
console.log('Enabling company fields...');
this.companyFormComp.companyForm.enable();
} else {
console.log('Disabling and resetting company fields...');
this.companyFormComp.companyForm.reset();
this.companyFormComp.companyForm.disable();
}
}
order(): void {
if (
this.customerFormComp.customerForm.valid &&
this.addressFormComp.addressForm.valid &&
this.deliveryFormComp.deliveryForm.valid &&
(!this.isCompany || this.companyFormComp.companyForm.valid)
) {
const orderData = {
address: this.addressFormComp.addressForm.getRawValue(),
deliver: this.deliveryFormComp.deliveryForm.getRawValue(),
customerDetails: this.customerFormComp.customerForm.getRawValue(),
isCompany: this.isCompany,
companyName: this.isCompany
? this.companyFormComp.companyForm.get('companyName')?.value
: null,
nip: this.isCompany
? this.companyFormComp.companyForm.get('nip')?.value
: null,
info: this.infoFormComp.infoForm.get('info')?.value || null
};
console.log(
'Order data being sent to backend:',
JSON.stringify(orderData, null, 2)
);
this.ordersService.addOrder(orderData).subscribe({
next: () => {
if (this.saveShipping && this.isAuthenticated) {
this.saveShippingDetails(orderData.address);
}
},
error: (err) => {
this.errorMsg = err;
console.error('Order submission error:', err);
}
});
} else {
console.warn('Form is invalid');
}
}
All the form components are in create-order.component
.
My guess it the onCompanyChange
method fires before the view child is available. You can change the method to trigger the reset only if the companyFormComp
view child exists.
onCompanyChange(): void {
console.log('Checkbox changed:', this.isCompany);
if(this.companyFormComp?.companyForm) {
if (this.isCompany) {
console.log('Enabling company fields...');
this.companyFormComp.companyForm.enable();
} else {
console.log('Disabling and resetting company fields...');
this.companyFormComp.companyForm.reset();
this.companyFormComp.companyForm.disable();
}
}
}
Also make sure you are not taking a view child, of a component that is hidden by *ngIf
or @if
, if that is the scenario, you can try [hidden]
attribute, which does not remove the component from the DOM and view child will be able to find this component.