首页 > 解决方案 > 根据条件订阅 Observable

问题描述

背景

我使用的反应式表单扩展了两个选项卡(仅页面的前半部分有选项卡),然后是一个底部带有提交按钮的长页面。我在单击提交按钮时进行验证。

单击提交按钮时,页面需要在验证失败时滚动到错误表单字段。

我还可以根据 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. Once the Approve Button is clicked, the subscription subscribes, and the page scrolls to the errors whenever a new Tab is selected. 我想取消订阅这个 observable,然后在再次单击提交按钮时重新订阅。

  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

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

页面预览

这只是为了演示。 页面预览

编辑 2 下面

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

重现问题

方案 1

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

标签: angularobservablesubscription

解决方案


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

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

方法一

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

方法二

我遇到了这种非常酷的方式来做到这一点。

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 1首先被选中invalid,那么将另一个条件放在else/ else if部分 -

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

还有一件事,在关注元素之前,您必须稍微延迟一下,因为在 DOMinvalid中创建input元素Tab 2需要纳秒的时间,并且它关注Details 1的是Tab 1or中的一个2是否为invalid.

delay(ms: number) {
    return new Promise( resolve => setTimeout(resolve, ms) );
  }

focus()只是在之前和之后调用这个switching tabs check

await this.delay(1);

如果您不想设置delay,只需再次设置事件并将focus()时间设置为最小,这样您就不会实时看到。animationDoneanimationDurationfocusDetails 1

按照Methods完整流程的链接,如果您遇到任何问题,请告诉我。


推荐阅读