【问题标题】:Subscribing to an Observable based on conditions根据条件订阅 Observable
【发布时间】:2020-01-30 02:23:58
【问题描述】:

背景

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

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

我还可以根据 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. 单击“批准”按钮后,订阅即进行订阅,并且只要选择了新选项卡,页面就会滚动到错误处。我想取消订阅这个 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 Link。这只是一个示例页面,与此相比,我的页面有更多的字段和表单组。

重现问题

场景 1

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

【问题讨论】:

  • 您能在此处粘贴更多代码吗?我不确定这是否是实现您想要做的事情的正确方法。但是您可以使用BehaviorSubject 而不是Observable 并在一定时间后使用next。
  • @SuyashGupta,我已经用更多细节更新了我的问题
  • 你也可以分享你的html吗? The scroll happens before the tab loads 是什么意思?
  • 批准按钮上附加了哪些事件?它位于页面的什么位置?
  • 我将在 stackBlitz 中重新创建我的问题并在此处分享链接。

标签: angular observable subscription


【解决方案1】:

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

我没有研究您尝试的 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 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;
}

还有一点,在关注 invalid 元素之前,您必须稍作延迟,因为在 DOM 中为 Tab 2 创建 input 元素需要纳秒的时间,如果其中任何一个,它会专注于 Details 1 Tab 1 或 2 是 invalid。

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 链接了解完整流程,如果您遇到任何问题,请告诉我。

【讨论】:

  • 感谢您的回答。在这种使用延迟的方法中,我有一个小故障。这是多么愚蠢的证明?如果标签需要更多时间来加载怎么办?在我的场景中,选项卡本身是一个单独的复杂组件,它是延迟加载的。为了确保这一点,我必须订阅动画完成观察者。
  • 判断选项卡是否加载,有以下几种可能: 1. 动画完成; 2. SelectedTabChange;两者都是可观察的。
  • 感谢您关注输入字段而不是滚动的建议。我一定会使用它。
  • 你可以在这里验证相同的 - stackblitz.com/edit/angular-ugrjy7 因为我已将 animationDuration 设置为 800 ms 但延迟只是 1 ms 但仍然 focus() 工作正常所以没有使用delay 解决方案的危害。
  • 谢谢苏亚什。我将使用延迟方法和焦点(而不是 scrollIntoView)。但是我仍然不相信使用延迟;但我会使用,因为我没有更好的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 2018-08-28
相关资源
最近更新 更多