我正在使用带有独立模块和电容器的 Ionic 编写一个混合应用程序(请注意,我是个新手,所以并不是一切都完美)。我遇到了一个复选框问题,在浏览器中测试时不会出现该问题,但在真实的 Android 设备上却会出现该问题。
背景和代码
我有一个名为“new-visit”的页面组件,它允许创建新的医疗访问或更新现有的医疗访问。根据 url 参数,我可以创建一个具有默认值的表单,也可以使用现有访问中的数据填充它。
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')!;
}
// ...
然后,在 UI 方面,在表单元素内,我有一个复选框,允许选择客户是否为整个访问付费或有一些债务。根据复选框的值,我要么显示用于编辑债务金额的文本框,要么不显示。
<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>
问题
当我在浏览器中使用
ng serve
运行应用程序时,一切正常。当我将复选框标记为选中时,字段可见,否则不可见。
但是在 Android 上,它仅在创建新访问时有效。当我尝试编辑现有的一个时,它会随机停止工作 - 我可以更改复选框值,并且看到复选框本身的视觉效果,但“ngIf”部分在我最小化应用程序之前不起作用。似乎“ngIf”仅在最小化应用程序时才会触发。
这看起来像是角度变化检测问题,但可能还有另一个潜在问题。
当您运行
ng serve
时,应用程序很可能处于调试模式,因此对某些运行时错误更加宽容。您可以尝试通过 ng serve --configuration=production
在生产模式下运行。根据您的构建,构建模式正在使用生产模式。您可能会发现发生的任何潜在错误,并且可能会复制该错误。
看看手动触发更改检测是否可以修复该问题。
// add to constructor
private changeDetector: ChangeDetectorRef
// add inside your ngOnInit function
this.visitDataForm.get('hasDebt').valueChanges.subscribe(()=> {
this.changeDetector.detectChanges();
})
如果您的更改检测策略设置为 OnPush,组件中的 getter 可能无法按您的预期工作,将其设置回默认值应该可以修复它。
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
...
此吸气剂可能无法按预期工作。更改检测最适合原始值,而不是对象引用。在这种情况下,即使值已更改,Angular 也可能无法从对象的引用中跟踪它。
get hasDebt() {
return this.visitDataForm.get('hasDebt')!.value;
}
例如,您可以创建自己的原语来代替此 getter
import {
startWith,
} from 'rxjs';
hasDebt$ = this.visitDataForm.get('hasDebt').valueChanges.pipe(startWith(this.visitDataForm.get('hasDebt').value))
<ion-item *ngIf="hasDebt">
变成:
<ion-item *ngIf="hasDebt$ | async">
这样,您就可以保持变更检测的效率