根据条件订阅 Observable

Subscribing to an Observable based on conditions

背景

我使用的 Reactive Forms 扩展了两个选项卡(页面的前半部分只有选项卡),然后是底部带有提交按钮的长页面。我在单击“提交”按钮时进行验证。

单击“提交”按钮后,如果验证失败,页面需要滚动到错误表单字段。

我还可以根据 FormGroups 中的错误加载选项卡。

问题

  1. 滚动发生在选项卡加载之前。
  2. 为了解决第 1 点,我订阅了 animationDone 事件,然后滚动到其中的点。
submit(){
   //code to select the desired tab
   this.selectedTab.animationDone.subscribe(res => {
   this.myElement.nativeElement.ownerDocument.getElementsByClassName('ng-invalid mat-form-field')[0].scrollIntoView({ behavior: 'smooth' });
          });
}

到目前为止一切正常!!!

  1. 单击批准按钮后,订阅就会订阅,并且只要选择新选项卡,页面就会滚动到错误。我想取消订阅这个可观察对象,然后在再次单击“提交”按钮时重新订阅。

  2. 我试过取消订阅,但无法再次订阅。这有时会破坏订阅功能并抛出错误

我想我遗漏了什么并寻求帮助。提前致谢!!

请求的更多代码

submit()
{
    if(this.bookingForm.valid){
    // do some actions ....
     }
    else {
    this.bookingForm.markAllAsTouched();
    //subscribing to the event before selecting the tab

    this.selectedTab.animationDone.subscribe(res => {

     this.myElement.nativeElement.ownerDocument.getElementsByClassName('ng-invalid mat-form-field')[0].scrollIntoView({ behavior: 'smooth' });

          });

    // code to select the Tab where the error occurs .....
     this.selectedTab.selectedIndex = this.errorIndex;

    //unsubscribe
    this.selectedTab.animationDone.unsubscribe()

    } // close of else block
}// close of submit Function

取消订阅(或类似功能)是必需的,因为只有在单击“提交”按钮时才应订阅。如果未取消订阅(或暂停),则每次更改选项卡时都会调用订阅函数,并且页面会根据错误不断上下滚动。

页面浏览量

这只是为了演示。

EDIT 2 below

这里是StackBlitz Link。这只是一个示例页面,与此相比,我的页面有更多的字段和表单组。

重现问题

场景一

  1. 填写字段 - 名称、选项卡 1、详细信息 1
  2. 保持选中 tab1 并单击“提交”。
  3. 这应该向上滚动并显示 Tab2。按预期工作!延迟是可以接受的。
  4. 现在手动切换到 Tab1。
  5. 您可以看到页面滚动到“详细信息 2”字段。
  6. 每当切换选项卡时都会发生此滚动。如何停止此操作?
  7. 我可以通过取消注释 app.component.ts 文件中的第 38 行来实现这一点。 - 取消订阅命令。
    • 在此,当我多次点击提交按钮时出现错误。
    • 当我第二次点击时,订阅不起作用。

抱歉回复晚了。我有点忙。

我没有研究您尝试的 animation 部分,而是专注于基础知识,因为我们只需要关注元素,如果它是 invalid

Method 1

submit() {
  if(this.testForm.valid) {
    //Code to Submit
  } else {
    ((document.getElementsByClassName('mat-input-element ng-invalid')[0]) as HTMLElement).focus();
  }
}

Method 2

I came across this very cool way to do this.

import { MatInput } from '@angular/material';
import { QueryList, ViewChildren } from '@angular/core';

@ViewChildren(MatInput) inputs: QueryList <MatInput>;

submit() {
  if(this.testForm.valid) {
    //Code to Submit
  } else {
    this.inputs.find(input => !input.ngControl.valid).focus();
  }
}

此外,如果您总是希望 Tab 1invalid 时先被选中,则将其他条件放在 else/ else if 部分 -

if(this.testForm.get('tab1').errors) {
  this.tabControl.selectedIndex = 0;
} else if(this.testForm.get('tab2').errors) {
  this.tabControl.selectedIndex = 1;
}

还有一件事,在关注 invalid 元素之前,您必须稍微延迟一下,因为在 DOM 中为 Tab 2 创建 input 元素需要纳秒的时间如果 Tab 12 中的任何一个是 invalid.

,它会关注 Details 1
delay(ms: number) {
    return new Promise( resolve => setTimeout(resolve, ms) );
  }

只需在 focus() 之前和 switching tabs check

之后调用
await this.delay(1);

如果您不想设置 delay,只需在 animationDone 事件上再次设置 focus() 并将 animationDuration 时间设置为最短,这样您就不会实时查看 Details 1 上的 focus

点击 Methods 链接获取完整流程,如果您遇到任何问题,请告诉我。