When the form is initially accessed I get the following error:
ERROR TypeError: this.form._updateTreeValidity is not a function
_updateDomValue
ngOnChanges
For each character I enter into a field, I get this error:
ERROR TypeError: this.form.get is not a function
_setUpFormContainer
addFormGroup
ngOnInit
My form:
<form [formGroup]="checkoutForm" (ngSubmit)="placeOrder()">
<section>
<div class="row">
<div class="col col-12 col-lg-8">
<div formGroupName="deliveryAddress">
<div class="row mb-4">
<div class="col">
<label class="form-label">First Name</label>
<input id="firstName" class="form-control fw-bold" type="text"
required formControlName="firstName"
(change)="saveToDataStore()">
</div>
<div class="col">
<label class="form-label">Last Name</label>
<input id="lastName" class="form-control fw-bold" type="text"
required formControlName="lastName"
(change)="saveToDataStore()">
</div>
<div class="row mb-4">
<div class="col">
<label class="form-label">Phone Number</label>
<input id="phone" class="form-control fw-bold" type="phone"
required formControlName="phone"
(change)="saveToDataStore()">
</div>
</div>
<div class="row mb-4">
<div class="col">
<label class="form-label">Delivery Address</label>
<input id="address1" class="form-control fw-bold" type="text"
required formControlName="address1"
(change)="saveToDataStore()">
</div>
</div>
<div class="row mb-4">
<div class="col">
<label class="form-label">Delivery Address Line 2 (Optional)</label>
<input id="address2" class="form-control fw-bold" type="text" formControlName="address2"
(change)="saveToDataStore()">
</div>
</div>
<div class="row mb-4">
<div class="col">
<div class="form-check">
<input id="useAsBillingAddress" class="form-check-input" type="checkbox"
formControlName="useAsBillingAddress" (change)="saveToDataStore()">
<label class="form-check-label" for="formCheck-1">Use as Billing
Address</label>
</div>
</div>
</div>
</div>
</div>
<div formGroupName="applianceDelivery">
<div class="row">
<div class="col d-flex flex-column col-12 col-md-4">
<img src="assets/img/deliverytruck.png"
style="width: 64px;">
<div>
<small class="me-2">{{cart?.length}}</small>
<small>Item(s)</small>
</div>
</div>
<div class="col" style="font-size: 14px;">
<h6>Select Delivery Date</h6>
<input id="deliveryDate" class="form-control fw-bold mb-2" type="date"
required formControlName="deliveryDate"
(change)="saveToDataStore()">
<small class="fw-normal" style="font-size: 14.3px;">We'll call or text (617)
567-7112 with your projected 4-hour delivery window one business day
before your scheduled delivery date. You may also receive an optional
customer service satisfaction survey.</small>
<p class="mt-4">Delivery Instructions:</p>
<textarea id="specialInstructions" class="form-control"
formControlName="specialInstructions"
(change)="saveToDataStore()"></textarea>
</div>
</div>
</div>
<div formGroupName="paymentMethod">
<div class="row">
<div class="row mb-4">
<div class="col">
<div class="form-check">
<input class="form-check-input" type="radio" id="paymentType1" value="PayPal"
formControlName="paymentType" (change)="saveToDataStore()">
<label class="form-check-label" for="paymentType1" style="font-style: italic;">PayPal</label>
</div>
<div cl ass="form-check">
<input class="form-check-input" type="radio" id="paymentType2"
value="Credit Card" formControlName="paymentType" (change)="saveToDataStore()">
<label class="form-check-label" for="paymentType2">Credit Card</label>
</div>
</div>
</div>
<div class="row mb-4">
<div class="col">
<div class="input-group">
<span class="input-group-text ps-0">
<i class="fa fa-credit-card-alt" style="font-size: 25px;"></i>
</span>
<input id="cardNumber" class="form-control fw-bold" type="text"
required formControlName="cardNumber"
(change)="saveToDataStore()" placeholder="Enter credit card number">
<button class="btn btn-primary d-none" type="button">Button</button>
</div>
</div>
</div>
<div class="row mb-4">
<div class="col col-12 col-lg-4">
<small>Expiration Month</small>
<select id="expMonth" class="form-select fw-bold" formControlName="expMonth" (change)="saveToDataStore()">
<optgroup label="Month">
<option *ngFor="let expMonth of expirationMonths"> {{expMonth}}
</option>
</optgroup>
</select>
</div>
<div class="col col-12 col-lg-4">
<small>Expiration Year</small>
<select id="expYear" class="form-select fw-bold" formControlName="expYear" (change)="saveToDataStore()">
<optgroup label="Year">
<option *ngFor="let expYear of expirationYears">
{{expYear}}
</option>
</optgroup>
</select>
</div>
<div class="col col-12 col-lg-4">
<small>CVV (on back)</small>
<div class="input-group">
<span class="input-group-text ps-0">
<i class="fa fa-credit-card-alt" style="font-size: 25px;"></i>
</span>
<input id="CVV" class="form-control fw-bold" type="text" maxlength="3" formControlName="CVV"
(change)="saveToDataStore()" placeholder="Enter CVV">
<button class="btn btn-primary d-none" type="button">Button</button>
</div>
</div>
<div class="col col-12 mt-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="saveAsDefaultCard"
formControlName="saveAsDefaultCard"
(change)="saveToDataStore()">
<label class="form-check-label" for="saveAsDefaultCard" style="font-size: 13px;">Save as my default credit
card</label>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col col-12 col-md-4">
<app-order-summary></app-order-summary>
<section class="mt-5">
<button class="btn btn-primary w-100 primary-button" type="submit"
(click)="placeOrder()"
style="font-size: 20px;font-weight: bold;">Place Order</button>
</section>
</div>
</div>
</section>
</form>
The component:
import { Component } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { Store } from '@ngrx/store';
import { ToastrService } from 'ngx-toastr';
import { OrderSummaryComponent } from '../order/order-summary/order-summary.component';
import { CommonModule } from '@angular/common';
import { storeCheckoutData } from '../checkout/checkout.actions';
import { CheckoutService } from '../checkout/checkout.service';
@Component({
selector: 'app-checkout2',
standalone: true,
imports: [
ReactiveFormsModule,
CommonModule,
OrderSummaryComponent
],
templateUrl: './checkout2.component.html',
styleUrl: './checkout2.component.scss'
})
export class Checkout2Component {
checkoutForm: FormGroup = new FormGroup({});
public cart;
public checkoutData;
public expirationMonths;
public expirationYears;
constructor(
private store:Store<{cartReducers, checkoutReducers}>,
private checkoutService:CheckoutService,
private toastr: ToastrService,
){}
ngOnInit() {
this.initCheckoutForm();
this.subscribeToRedux();
}
private subscribeToRedux = () => {
const cartReducers$ = this.store.select((state) => {
return state.cartReducers;
});
cartReducers$.subscribe((cartReducers:any) => {
this.cart = cartReducers.cart;
console.log('CheckoutComponent.cart', this.cart)
});
const checkoutReducers$ = this.store.select((state) => {
return state.checkoutReducers;
});
checkoutReducers$.subscribe((checkoutReducers:any) => {
if(Object.keys(checkoutReducers?.checkoutData).length > 0) {
this.checkoutData = JSON.parse(JSON.stringify(checkoutReducers.checkoutData));
this.checkoutForm = {...this.checkoutData}
// console.log('this.checkoutData 2', this.checkoutData);
console.log('ChecoutComponent.checkoutForm 2', this.checkoutForm);
}
this.expirationMonths = checkoutReducers.expirationMonths;
this.expirationYears = checkoutReducers.expirationYears;
console.log('CheckoutComponent.checkoutData', this.checkoutData)
console.log('CheckoutComponent.expirationMonths', this.expirationMonths)
console.log('CheckoutComponent.expirationYears', this.expirationYears)
});
}
private initCheckoutForm = () => {
this.checkoutForm = new FormGroup({
deliveryAddress: new FormGroup({
firstName: new FormControl(''),
lastName: new FormControl(''),
phone: new FormControl(''),
address1: new FormControl(''),
address2: new FormControl(''),
useAsBillingAddress: new FormControl(''),
}),
applianceDelivery: new FormGroup({
deliveryDate: new FormControl(''),
specialInstructions: new FormControl(''),
}),
paymentMethod: new FormGroup({
paymentType: new FormControl(''),
cardNumber: new FormControl(''),
expMonth: new FormControl(''),
expYear: new FormControl(''),
CVV: new FormControl(''),
defaultCreditCard: new FormControl(''),
})
});
console.log('this.checkoutForm', this.checkoutForm.value);
}
public saveToDataStore = () => {
this.store.dispatch(storeCheckoutData({checkoutData: this.checkoutData}))
}
public placeOrder = () => {
const hdepot = localStorage.getItem('hdepot');
let token = null;
if(hdepot) {
token = JSON.parse(hdepot).user.token;
}
console.log('token', token)
if(!token) {
this.toastr.warning('Please sign in to place an order.', '');
return;
}
const items = [];
for(let item of this.cart) {
items.push (
{
product: item.product._id,
quantity: item.quantity,
color: item.color,
size: item.size
}
)
}
this.checkoutData = {...this.checkoutForm}
this.checkoutData.items = items;
console.log('CheckoutComponent.checkoutData 2', this.checkoutData)
this.checkoutService.placeOrder(this.checkoutData)
}
}
1
You must have strict mode turned off, given all the untyped variables. You’re issue is that you reassign checkoutForm to a value that is another type other than FormGroup. That is why the errors are complaining about missing functions.
This issue I spotted occurs in the following snippet:
checkoutReducers$.subscribe((checkoutReducers:any) => {
if(Object.keys(checkoutReducers?.checkoutData).length > 0) {
this.checkoutData = JSON.parse(JSON.stringify(checkoutReducers.checkoutData));
// 👇 checkoutData IS NOT a ForGroup! 👇
this.checkoutForm = {...this.checkoutData}
// console.log('this.checkoutData 2', this.checkoutData);
console.log('ChecoutComponent.checkoutForm 2', this.checkoutForm);
}
/* ... */
}
You probably meant to do something like this.checkoutForm.setValue(this.checkoutData);
Your code is probably not in a state where you can quickly turn on more robust type checking. At the very least consider the following:
- Make checkoutForm readonly so that it isn’t accidently reassigned again.
- Set the value of checkoutForm when it is initialized. There’s nothing gained by waiting for the OnInit lifecycle event and you never intentionally reassign the value anywhere else.