TypeError: input.setFocus is not a function when I run all Jasmine unit tests associated with the Angular component

TypeError: input.setFocus is not a function when I run all Jasmine unit tests associated with the Angular component

使用 @ViewChild 装饰器,我抓住 ion-input 元素以使用 setFocus() 方法设置焦点。这按预期工作。我不知道为什么,但我想不通为什么组件的所有 Jasmine 单元测试都失败了。我试过模拟 setFocus() 方法,但没有成功。谁能帮我解决这个问题并知道我做错了什么?

带有 @ViewChild 参考的模板 #input_username

  <ion-input id="inputusername"
                 #input_username
                 color="dark"
                 role="textbox"
                 aria-label=""
                 class="input"
                 placeholder=""
                 type="text"
                 formControlName="name"
                 ngDefaultControl>
   </ion-input>

设置焦点的组件方法setFocus()

private _setFocusDefaultInput(input: ElementRef): void {
    const ionInput = input['el']; // get the DOM element from Ionic
    ionInput.setFocus();
}

_setFocusDefaultInput()方法在ngAfterViewInit()方法内部调用

ngAfterViewInit(): void {
    setTimeout(() => { 
      this._setFocusDefaultInput(this.inputUsername); 
    }, 800);
  }

当我 运行 属于该组件的所有 Jasmine 单元测试时收到的错误消息:

Failed: input.setFocus is not a function
    error properties: Object({ longStack: 'TypeError: input.setFocus is not a function
        at LoginPage._setFocusDefaultInput (src/app/login/login/login.page.ts:123:11)
        at src/app/login/login/login.page.ts:54:12
        at ZoneDelegate.invokeTask (node_modules/zone.js/dist/zone.js:429:1)
        at AsyncTestZoneSpec.onInvokeTask (node_modules/zone.js/dist/zone-testing.js:1231:33)
        at ProxyZoneSpec.onInvokeTask (node_modules/zone.js/dist/zone-testing.js:328:43)
        at ZoneDelegate.invokeTask (node_modules/zone.js/dist/zone.js:428:1)
        at Object.onInvokeTask (node_modules/zone.js/dist/zone.js:307:88)
        at ZoneDelegate.invokeTask (nod ...
    TypeError: input.setFocus is not a function

我尝试使用以下代码模拟 setFocus() 方法,但没有成功

测试台配置

  {
    provide: ElementRef,
    useClass: ElementRefMock
  },
  {
    provide: IonInput,
    useClass: IonInputMock
  }

嘲笑类

class ElementRefMock {
   setFocus(){}
   el: {
   setFocus: () => {}
   };
 }

class IonInputMock {
  setFocus(){}
  el: {
  setFocus: () => {}
};

我认为每个单独的测试都比 800 毫秒的超时运行得更快,因此输入的视图被破坏了,但您仍然想在大约 800 毫秒后引用它。假设测试耗时 200 毫秒,焦点逻辑运行 600 毫秒,但在 200 毫秒内视图已被破坏,我们指的是不存在的东西。

试试这个调试:

private _setFocusDefaultInput(input: ElementRef): void {
    const ionInput = input['el']; // get the DOM element from Ionic
    // See what you get for the ionInput
    console.log(ionInput);
    // If ionInput is undefined, put a question mark between the . and setFocus
    // This way, it won't run if ionInput is undefined or null
    // You can of course put an if check as well.
    ionInput?.setFocus();
}

之前的答案解决了我的问题,但我找到了一个新的解决方案,它不再需要 setTimeout 方法来将焦点正确设置在 Ionic 输入字段上。我 运行 遇到的 Jasmine 单元测试问题也已通过此实现得到解决

  1. 我把ElementRef接口换成IonInput接口直接调用setFocus():

    
      @ViewChild('input_username') inputUsername: IonInput;
      </pre>
    
  2. 我实现了 ionViewDidEnter() Ionic 生命周期方法。在所有 Ionic 元素都已成功渲染到 DOM 之前,不会调用此方法。那是我想将焦点设置在 Ionic 输入字段上的时候:

    
      ionViewDidEnter(): void {
        this.inputUsername.setFocus();
      }
      </pre>