如果您想提高代码的整体质量,您可能希望使数据模型与底层视图完全解耦。
功能响应式框架或 UI 库(如 Rimmel.js)完全支持 Observables,除了鲜为人知的设计之外,还使您能够将模型定义为 Observable 流(例如:简单的数据输入、数据输出流)模式,即事件适配器。
事件适配器帮助您将任何源事件(例如:DOM 的 MouseEvent、PointerEvent、KeyboardEvent 等)映射到数据模型实际使用的格式,因此它们将从这种转换任务中解放出来,并最终与用户界面。
Rimmel 使将此类流连接到 DOM 变得简单:
import { rml } from 'rimmel'; const component = () => { const total = new Subject().pipe( map(x => doSomethingWith(x)), ); return rml`${stream}`; }
绑定很简单:Rimmel 将来自按钮的点击事件直接连接到您的可观察流中,每次单击按钮时,该流都会接收 PointerEvent 的实例。
到目前为止,一切都很好。如果您的流需要从多个源获取数据,并且根据每个源的行为有所不同,该怎么办?
让我们创建一个带有递增和递减按钮的简单计数器,每个按钮都会加一或减一。
import { scan } from 'rxjs'; import { rml } from 'rimmel'; const component = () => { const total = new BehaviorSubject(0).pipe( scan((old, new) => old new, 0), ); return rml`${total}`; }
这可行,但模板部分包含一些逻辑,这是一种反模式。理想情况下,我们应该努力拥有无逻辑的模板,以最大限度地提高整体可测试性。
因此,Rimmel 1.2 提供了一项新功能,即事件映射器,它可以帮助解决这一问题。它们帮助您将 DOM 事件映射到模型所需的内容,以便您可以将逻辑与模板完全分开。这是它的工作原理。
import { map, scan } from 'rxjs'; import { rml, reversePipe } from 'rimmel'; const Inc = reversePipe(map(() => 1)); const Dec = reversePipe(map(() => -1)); const component = () => { const total = new BehaviorSubject(0).pipe( scan((old, new) => old new, 0), ); return rml`${total}`; };
reversePipe 是这里的创新补充:一个管道创建工具,与 RxJS 中的 pipeline() 函数相反。后者将转换应用于流的输出,reversePipe() 将它们应用于输入。
这样您就可以确保您的主流Subject/BehaviorSubject/Observer/EventListener 始终以您想要的格式获取数据,并且您可以将适配器作为单独的关注点。
您可以在反向管道中使用任何 RxJS 运算符。您是否只想过滤掉某些事件,例如当用户按 Enter 键时,而不是任何其他键?只需使用过滤运算符:
import { Subject, filter, map } from 'rxjs'; import { rml, inputPipe } from 'rimmel'; const UpperOnEnter = inputPipe( filter((e: Event) => e.key == 'Enter'), map((e: Event) => e.target.value.toUpperCase()), ); const Component = () => { const state = new Subject(); return rml` Type some text and hit Enter
${state}`; };
关于单元测试,这是一个微小但有用的补充,将使测试更简单、更高效。
查看此 Stackblitz 上正在运行的事件映射器
免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。
Copyright© 2022 湘ICP备2022001581号-3