Issue
I am writing a hybrid app using Ionic with standalone modules and capacitor (note that I'm kinda new so not everything might be perfect). I have an issue with checkbox that doesn't occur when testing in browser, but happens on the real Android devices.
Background and code
I have a page component called "new-visit" which allows for both creating a new medical visit or updating an existing one. Depending on url parameters, I either create a form with default values or populate it with data from existing visit.
export class NewVisitPage implements OnInit {
editMode: boolean = false;
private existingVisit: VisitModel | undefined = undefined;
private customers: CustomerDetails[] = [];
public selectedCustomer: CustomerDetails | null = null;
public customerSearchField: FormControl;
public allowCustomerChange: boolean = true;
constructor(
private formBuilder: FormBuilder,
private visitService: VisitsService,
private route: ActivatedRoute,
private customersService: CustomersService,
private navCtrl: NavController,
private toastCtrl: ToastController,
private menuCtrl: MenuController) {
this.customerSearchField = new FormControl('');
}
visitDataForm: FormGroup = this.formBuilder.group({
customerId: ['', Validators.required],
medicines: '',
description: '',
date: [moment().format('YYYY-MM-DD'), Validators.required],
amount: 0,
hasDebt: false,
debtAmount: 0,
debtPaidAmount: 0
})
ngOnInit() {
addIcons(icons);
this.visitDataForm.reset();
this.route.queryParamMap.subscribe(params => {
const customerId = params.get('customerId');
if (customerId) {
this.selectedCustomer = this.customersService.getCustomerById(customerId);
this.customerId?.setValue(customerId);
this.allowCustomerChange = false;
}
})
this.route.paramMap.subscribe(params => {
let date = params.get('date');
if (date) {
this.visitDataForm.get('date')!.setValue(date)
}
let id = params.get('id');
if (id) {
this.editMode = true;
this.existingVisit = this.visitService.getById(id);
if (!this.existingVisit)
throw 'visit not found';
this.visitDataForm.get('customerId')?.setValue(this.existingVisit.customerDetails.id);
this.visitDataForm.get('medicines')?.setValue(this.existingVisit.medicines);
this.visitDataForm.get('description')?.setValue(this.existingVisit.description);
this.visitDataForm.get('date')?.setValue(moment(this.existingVisit.date).format('YYYY-MM-DD'));
this.visitDataForm.get('amount')?.setValue(this.existingVisit.amount);
this.visitDataForm.get('hasDebt')?.setValue(!!this.existingVisit.debt);
this.visitDataForm.get('debtAmount')?.setValue(this.existingVisit.debt?.amount || 0);
this.visitDataForm.get('debtPaidAmount')?.setValue(this.existingVisit.debt?.amountPaid || 0);
this.selectedCustomer = this.customersService.getCustomerById(this.existingVisit.customerDetails.id);
}
});
}
get hasDebt() {
return this.visitDataForm.get('hasDebt')!.value;
}
get customerId() {
return this.visitDataForm.get('customerId')!;
}
// ...
Then, on the UI side, within the form element, I have a checkbox that allows to select whether customer paid for whole visit or had some debt. Depending on the value of checkbox, I either present the textboxes for editing the debt amounts or not.
<ion-item lines="full">
<ion-checkbox labelPlacement="end" formControlName="hasDebt">
<div class="ion-text-wrap">Amount was not fully paid</div>
</ion-checkbox>
</ion-item>
<ion-item *ngIf="hasDebt">
<ion-label position="floating">Debt amount</ion-label>
<ion-input type="number" step="0.01" min="0" formControlName="debtAmount"></ion-input>
</ion-item>
<ion-item *ngIf="hasDebt && editMode">
<ion-label position="floating">Paid amount</ion-label>
<ion-input type="number" step="0.01" min="0" formControlName="debtPaidAmount"></ion-input>
</ion-item>
The issue
When I run the app with ng serve
in browser, everything works fine. When I mark the checkbox as checked, fields are visible, and otherwise they're not.
However on Android, it works only when creating a new visit. When I try to edit an existing one, it randomly stops working - I can change the checkbox value and I see the visual effect on the checkbox itself, but the "ngIf" part is not working until I minimize the application. It seems like the "ngIf" fires only during the moment of minimizing the app.
Solution
This looks like an Angular Change Detection issue, but there can be another underlying issue.
1.Try production mode on
when you run ng serve
, the application is most likely to be in debug mode, hence more forgiving to some runtime errors. you can try running in prod mode by ng serve --configuration=production
. Depending on your build, build mode is using production mode. You might find any potential errors that occur, and maybe replicate the error.
2.Manually detect changes on checkbox value change
See if manually triggering change detection fixes it.
// add to constructor
private changeDetector: ChangeDetectorRef
// add inside your ngOnInit function
this.visitDataForm.get('hasDebt').valueChanges.subscribe(()=> {
this.changeDetector.detectChanges();
})
3.ChangeDetectionStrategy
If your changedetection strategy is set to OnPush, the getters in your component might not work as you expect, setting it back to default should fix it.
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
...
4.Fixing underlying detection issue
this getter may not be working as expected. Change detection works best for primitive values, not with object references. Even though the value has changed, Angular might not be able to track it from the object's reference in this case.
get hasDebt() {
return this.visitDataForm.get('hasDebt')!.value;
}
Instead of this getter you can create your own primitive, for example
import {
startWith,
} from 'rxjs';
hasDebt$ = this.visitDataForm.get('hasDebt').valueChanges.pipe(startWith(this.visitDataForm.get('hasDebt').value))
<ion-item *ngIf="hasDebt">
becomes:
<ion-item *ngIf="hasDebt$ | async">
this way, you can keep being efficient with the change detections
Answered By - selcuk-sahin
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.