项目场景:
填写的赠品大于等于10,保存时,需要弹框提示用户。用户确定则直接保存,用户点取消则不保存且光标定位到大于等于10的输入框中。
问题描述:
遇到的问题:
- 用viewChild拿不到孙组件,返回的是undefined。
- 点击保存时,会弹出模态框,点取消时,光标会定位到校验未通过的赠品输入框中,但是又迅速失焦。
- 赠品可以添加多个,所以赠品输入框也会有多个,取消时如何定位到校验未通过的输入框中。数据和视图找不到对应。
原因分析:
问题的分析:
- viewChild是父组件与子组件之间的通信方式,通过viewChild父组件可以使用子组件上的变量和方法,孙组件属于非父子的关系了,当然拿不到,返回undefined。
- 迅速失焦,点保存时会弹出模态框,当点击模态框中的取消时,确实focus了输入框,但是由于模态框自己有一个自动聚焦的事件,会自动聚焦到保存按钮上。所以输入框中的光标会迅速失焦。
解决方案:
具体解决方案:
- 用viewChild拿到子组件,而子组件用viewChild拿到孙组件,这样父组件就可以调用到孙组件中的方法。
- 模态框点取消时会自动聚焦到保存按钮上,所以要在取消时加上定时器,在定时器里面让光标定位到输入框。
- 有多个输入框时,多个输入框的值大于10,则让光标定位到第一个大于10的那个输入框。用document.querySelectorAll()找出所有的赠品输入框得到的是个nodeList,遍历nodeList找出第一个值>=10的元素,调用focus()方法。
- 注意:遍历这个nodeList时,要给里面的元素加上接口< HTMLInputElement >,否则拿值.value和调用方法.focus()是会报错的(报不存在该属性的错误)
<!-- 孙组件中的赠品输入框 -->
<input class="once-input" nz-input [(ngModel)]="gift.once" rule="^[1-9][0-9]*$">
<!-- 子组件 -->
<div>
<孙组件></孙组件>
</div>
<!-- 父组件 -->
<div>
<子组件></子组件>
</div>
// 孙组件中聚焦输入框的方法
cursorFocus() {
const onceInput = document.querySelectorAll('.once-input');
for (let i = 0; i < onceInput.length; i++) {
// <HTMLInputElement>需要这个接口否则拿不到value,也不能调用focus()
const item = (<HTMLInputElement>onceInput[i]);
if (+item.value >= 10) {
item.focus();
break;
}
}
}
// 子组件中引用孙组件
@ViewChild(PromotionDetailRuleGiftComponent, { static: false })
giftComponent:PromotionDetailRuleGiftComponent;
// 父组件中:
// 1.引用子组件
@ViewChild(PromotionDetailRuleComponent, { static: false })
ruleComponent: PromotionDetailRuleComponent;
// 2.调用孙组件的方法
const giftItem = spGiftList.find(v => +v.once >= 10);
if (giftItem) {
this.modalService.confirm({
nzTitle: this.getI18nValue('ts'),
nzContent: `${this.getI18nValue('sfmdzs')}${giftItem.once}${this.getI18nValue('gczp')}`,
nzOnCancel: () => {
setTimeout(() => { // 弹框取消后会自动聚焦仅保存or提交按钮,需要定时器来重新聚焦输入框
this.ruleComponent.giftComponent.cursorFocus(); // 调用孙组件中的光标定位的方法
}, 500);
},
nzOnOk: () => {
this.request(); // 保存时的请求
}
});
} else {
this.request(); // 保存时的请求
}