RxJS - 使用成对确认和恢复输入字段

2024-02-29

所以我对可观察的东西有点陌生,我正在努力解决一个场景,我认为它可能是一个很好的候选问题。开始了...

场景是这样的:我有一个下拉字段;当它改变时,我想要

  1. 检查基于条件以前的值和新的值领域的
  2. 如果条件通过,则请求用户确认,并且...
  3. 如果用户未确认,则恢复该字段的值。

这是带注释的代码:

  ngOnInit(): void {
    
    // I am waiting for my view-model to load, then initializing my FormGroup using that view model data.
    // NOTE: My view model is for "Contact" (this code is for contact list)
    this.addSubcription(this.vm$.subscribe((vm) => this.initFormGroup(vm)));
    
    const field:string = 'customerEmployerId'; // the field's name
    // I create the observable that should listen to changes in the field, and return them in pairs
    const employerValueChanges$ = this.formInit$.pipe(
      switchMap(form=> form.get(field).valueChanges.pipe(
        startWith(form.get(field).value)
      )),
      pairwise()
    );

    // I combine the changes observable with my other observables to access data from each
    let employerCheckSub = combineLatest([
      employerValueChanges$, // the value-changes obs
      this.vm$, // the view-model data
      this.customers$ // a list of customers from a CustomerService
    ]).subscribe(
      ([
        [oldid,newid], // values from value-changes obs
        contact, // the contact info / data
        customers // the list of customers
      ])=> {
        
        // check the previously and newly selected employer values
        // request confirmation if contact was listed as the primary contact for the previously selected employer
         if(oldid > 0 && newid !== oldid){
           const employer = customers.find(c=> c.customerId === oldid && c.contactId === contact.contactId);
           if(employer === null) return;
           if(!confirm('Warning: changing this contact\'s employer will also remove them '+
           'as the primary contact for that customer. Are you should you want to continue?')){ 
             // user clicked cancel, so revert back to the previous value without emitting event
             this.contactEditForm.get(field).setValue(oldid, {emitEvent:false});
           }
         }
      });

    this.addSubcription(employerCheckSub);
  }

问题是,当我在不发出事件的情况下恢复值时,成对可观察量会在下一个值更改时发出不正确的“上一个”值。我希望有一两个 RxJS 操作符是我所缺少的,并且可以在这里完美地工作。有谁有解决这个问题的技巧可以分享吗?


更新工作代码:

首先,特别感谢安德烈的回答 https://stackoverflow.com/a/62770354/1751792。他使用的scan运营商绝对是最佳选择。我只需要一个小修复,那就是设置crt (or current在下面的代码中)值以及prev累加器中的值。瞧!这是我的最终工作版本:

/**
   * Requests confirmation when attempting to change a contact's employer if that contact is also
   * designated as the employer's primary contact.
   */
  private addEmployerChangeConfirmation() {
    // NOTE: In this scenario, "customers" are synonymous with "employers"; i.e., our customers are employers of these contacts.
    const field: string = 'customerEmployerId'; // the field's name
    const valueChanges$ = this.formInit$.pipe(
      switchMap((form) => form.get(field).valueChanges)
    );

    let employerCheckSub = combineLatest([
      // the value-changes obs
      valueChanges$,
      // the id needed from the view model
      this.vm$.pipe(
        filter((vm) => vm !== null),
        map((vm) => vm.contactId)
      ),
      // the customer/employer list
      this.customers$,
    ])
      .pipe(
        // once the user approves, I don't bother re-confirming if they change back in same session
        // NOTE: I use a "$$" naming convention to indicate internal subjects that lack a corresponding public-facing observable.
        takeUntil(this.employerChangeApproved$$),
        scan(
          (acc, [current, contactId, customers], i) => ({
            prevOfPrev: acc.prev,
            ///////////////////////////////////////////////////////////////////////////////////////////////////
            // NOTE: This was an interesting issue. Apparently the seed value is resolved immediately.
            // So, there is no way I found to seed a value from another obs.
            // Instead, I just check if this is the first run, and if so I use the resolved data for prev value.
            // I know the data is resolved because an upstream obs provides it.
            ///////////////////////////////////////////////////////////////////////////////////////////////////
            prev: i === 0 ? this.contactData.customerEmployerId : acc.current, // <-- setting seed manually on first emission
            current,
            contactId,
            customers,
          }),
          {
            prevOfPrev: null,
            prev: null,
            current: this.contactData?.customerEmployerId,
            contactId: this.contactData?.contactId,
            customers: [],
          }
        ),
        // only continue if condition passes
        filter((data) =>
          this.checkIfChangeWillRemoveAsPrimaryContact(
            data.prev,
            data.current,
            data.contactId,
            data.customers
          )
        ),
        // we only want to revert if user clicks cancel on confirmation box.
        // NOTE: If they approve change, this also triggers the "employerChangeApproved$$" subject.
        filter((data) => !this.confirmRemoveAsPrimaryContact())
      )
      // and now we actually subscribe to perform the action
      .subscribe((data) => {
        data.current = data.prev;
        data.prev = data.prevOfPrev;
        this.contactEditForm
          .get(field)
          .setValue(data.current, { emitEvent: false });
      }); 

    this.addSubcription(employerCheckSub);
  }

这是我的方法:

form.valuesChanges.pipe(
  scan(
    (acc, item) => ({
      // Needed in case we need to revert
      prevOfPrev: acc[prev],

      prev: acc[crt],
      crt: item,
    }), 
    { prevOfPrev: null, prev: null, crt: null }
  ),

  // 'check a condition based on the previous and new values of the field'
  filter(v => condition(v.prev, v.crt)),

  // 'request from the user a confirmation if the condition passes'
  switchMap(
    v => confirmationFromUser().pipe(
      // 'then revert the value of the field if the user did not confirm'
      tap(confirmed => !confirmed && (v[prev] = v[prevOfPrev])),
    )
  ),

  // Go further only if the user confirmed
  filter(v => !!v),
)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

RxJS - 使用成对确认和恢复输入字段 的相关文章

随机推荐

  • 根据 mongodb 中的正则表达式值从文档中查找匹配的项目

    我必须选择匹配的casts item m credits cast 使用正则表达式值从电影集合中获取 电影模态 var mongoose require mongoose var movieSchema new mongoose Schem
  • 如何通过电子邮件发送 HTML5 画布图像

    首先 我尝试使用 Pear 的 Mail mime 来完成此操作 但我无法从共享主机提供商和 cpanel 加载该类 接下来我按照指示这个帖子 https stackoverflow com questions 6150880 email
  • 调用 WebRequest GetSystemWebProxy()

    在 PowerShell 2 0 下 我知道您可以通过执行以下操作来设置您想要使用的代理 而无需知道确切的代理设置 proxy System Net WebRequest GetSystemWebproxy proxy Credential
  • 解析 JSON 以配置 Android 应用程序

    在我的 Android 应用程序中 我必须使用服务器中的 JSON 在应用程序中进行特定调整 我试图实现的目标是读取此 json 并将所有值存储到局部变量中以在应用程序内执行操作 来自服务器的 JSON sett glHdr sm scle
  • 如何获取 .wav 文件格式的 numpy 数组输出

    我是 Python 新手 我正在尝试训练我的音频语音识别模型 我想读取 wav 文件并将该 wav 文件的输出放入 Numpy 数组中 我怎样才能做到这一点 根据 Marco 的评论 您可以查看 Scipy 库 特别是scipy io fr
  • PHP:删除unicode文本中少于3个字符的单词

    我使用这些正则表达式来删除少于 3 个字符的单词 str preg replace b w 1 3 b str and rdu b b 1 2 b str preg replace rdu str 但在 unicode 文本中返回我 有没有
  • 使用 HttpListener 检测客户端断开连接

    我有一个使用 HttpListener 的应用程序 我需要知道客户端何时断开连接 现在我将所有代码都放在 try catch 块中 这非常丑陋 而且不是一个好的做法 我如何知道客户端是否已断开连接 thanks 简短的回答 你不能 如果客户
  • 程序可以读取自己的 elf 部分吗?

    我想使用 ld 的 build id 选项来将构建信息添加到我的二进制文件中 但是 我不确定如何在程序中提供此信息 假设我想编写一个程序 每次发生异常时都会写入回溯 以及一个解析此信息的脚本 该脚本读取程序的符号表并搜索回溯中打印的地址 我
  • 如何获取 Laravel 的平均时间

    我想要得到平均时间开始时间和结束时间之间以及Where CUR Time GroupBY 用户名我在 laravel 中编写了 sql 它显示了一些错误 我找不到那是什么 因为我是 laravel 的新手 请帮助修复此 sql 错误 我提交
  • Android Studio 的启动栏逐渐消失到主程序中

    我目前正在开发一个 Android 应用程序 刚刚开始 我就能够实现我的闪屏了 然而 我不喜欢它和主要活动之间的过渡 我希望初始屏幕淡出 主屏幕淡入 看起来它们混合在一起 因为我对两者都有相同的背景图像 做了一些研究 但未能找到正确的答案
  • 如何将数字转换为科学记数法?

    我想创建一个函数 它接受输入的值并将其转换为科学记数法 N x 10 a 我尝试了很多不同的事情 但我似乎无法做到正确 Example 我输入 200 转换器将其转换为 2 x 10 2 你可以这样做 a 200 a toExponenti
  • 解析错误:语法错误,意外的“mysql_query”(T_STRING)

    我是 php 新手 在更新表的某一列时遇到此错误 if isset POST approved change SELECT FROM workplan forchange mysql query change while infoChang
  • Grails 根据枚举列表属性中的枚举值选择域对象

    我在根据枚举列表中的值从域对象列表中选择项目时遇到问题 我的域对象如下所示 class Truck static hasMany makes Make 其中 Make 看起来像这样 enum Make KENWORTH MACK VOLVO
  • 非托管导出错误“RGiesecke.DllExport.MSBuild.DllExportAppDomainIsolatedTask”

    我在 Visual Studio 2017 编译 DLL 项目时遇到问题 我在包管理器控制台上执行了 Install Package UnmanagedExports 错误 无法从程序集 C Users Tom Desktop CSharp
  • NodeJS ping 端口

    我正在为我工 作的一家托管公司编写一个状态检查器 我们想知道如何使用 nodejs 检查端口的状态 如果可能的话 如果没有 你能建议任何其他想法 比如使用 PHP 和阅读 STDOUT 吗 是的 这可以很容易地使用net http node
  • Java EE 7 属性文件配置的最佳实践建议是什么?

    应用程序配置在现代 Java EE 应用程序中属于什么位置 人们有哪些最佳实践建议 通过应用程序配置 我的意思是诸如与其他设备上的服务的连接设置之类的设置 包括外部设备 例如 Twitter 和我们的内部 Cassandra 服务器 用于主
  • VS2015中如何设置工具提示颜色?

    我刚刚安装了 Visual Studio 2015 总体来说非常好 但是您可以在 工具 gt 选项 gt 字体和颜色 下配置的大量不同内容完全让我感到困惑 我需要找到允许我更改此工具提示颜色的设置 以便我可以实际阅读它 它在哪里 下载颜色主
  • php:: tmp 文件保留多长时间?

    我正在编写上传脚本 如果用户上传一个文件并且该文件已经存在 我想警告用户 这都是通过ajax 并让他们选择替换它或取消 而不是移动文件 我很好奇是否可以将文件保留在 tmp 中并在 ajax 响应中传回该文件的路径 如果用户说覆盖该 aja
  • Android 版 Chrome 无法正确显示 Google 网络字体

    我已经使用 CSS 重现了我遇到的问题font family以及 Android 版 Chrome Web 浏览器无法正确继承字体 而是使用后备字体 http jsbin com iyifah 1 edit http jsbin com i
  • RxJS - 使用成对确认和恢复输入字段

    所以我对可观察的东西有点陌生 我正在努力解决一个场景 我认为它可能是一个很好的候选问题 开始了 场景是这样的 我有一个下拉字段 当它改变时 我想要 检查基于条件以前的值和新的值领域的 如果条件通过 则请求用户确认 并且 如果用户未确认 则恢