零到OM-第4幕

到目前为止,这个应用程序还是相当无聊的。它只显示数据。但我们真的想用它!在本文中,我们将了解应用程序对用户输入的反应(没有双关语)。源代码位于github.

注:我强烈建议阅读上岗首先,如果你还没有这样做。


管理状态很棘手。每个框架都有自己的机制来检测和处理状态更改。这是OM的。

内部状态

在本节中,我们将了解如何管理组件内部的状态。

组件状态

在上一篇文章中,我们已经了解到组件可以有内部的,瞬态。在我们的例子中,待办事项组件使用了符号编辑文本指定当前文本字段的值。此代码段应刷新您的内存:

(defn todo item[todo owner](reify om/iinitstate(init state[]:编辑文本(:title todo))om/irenderstate(render state[u state]。)(dom/input js:classname“edit”:value(om/get state owner:edit text):onchange(change%todo owner)…()))))

现在,让我们来看一下换上

(defn change[e todo owner](OM/设置状态)所有者:编辑文本(->E.-目标.-值)))

处理程序更改编辑文本输入字段值的状态。>是一个宏,允许在不添加嵌套的情况下编写语句(即(.-选中(.-目标E)))但在一条“线”上。上面的代码片段显示了更新组件内部状态的两个选项之一:

  • OM/SET状态!:设置新值
  • OM/更新状态!:对当前值应用更新函数

两者都将触发OM重新呈现。

应用程序状态

我们之前已经了解到,整个应用程序状态都保存在一个原子中,这个根原子.但我们只将子集传递给组件(例如托多待办事项)如何使子集与根状态保持同步?

游标!根据OM文件:

光标将一个大的可变原子拆分为较小的子原子,这些子原子与根原子中的状态保持同步。

基本上,您可以将光标想象为指向根状态一部分的指针。组件可以对其光标应用更改,应用程序状态也将更改。当应用程序状态中的数据更改时(例如来自HTTP请求的数据),OM将重新呈现依赖更改值的所有组件。因此,绑定是双向的。

要从光标中读取值,需要取消对其的引用(例如@我的光标)-渲染阶段除外(即内部提供渲染状态)它可以像一个正则值一样被处理。要更改光标值,有两个选项:

  • 更新/更新!:设置新值
  • OM/交易!:对当前值应用更新函数

让我们在我们的应用程序中看看这是如何工作的。

这个待办事项组件包含一个复选框字段,用于切换项目的状态。它的换上此处处理事件:

(定义完成[TODO](OM/Transact!TODO:已完成(不是%))

这个办理!函数接收(1)组件的光标托多到todo项,(2)关键完成指定要更改数据的哪一部分,以及(3)一个简单地否定当前值的函数文本。

另一个例子主要是todoapp组件。它还包含一个复选框,但此复选框可切换所有项目的状态:

(defn切换所有[e状态](让[checked(->e.-target.-checked)](om/transact!状态:TODOS(fn[TODOS](vec(map(assoc%:completed checked)todos(vec))))))

此处,复选框的值绑定到选中的.这个办理!更新函数使用地图返回带有完整的密钥集选中的.别忘了:我们正在处理不可变数据,因此地图阿索克不要修改数据,而是返回新值。

注:VEC我们确保返回值始终是一个向量,因为OM的光标只与关联的数据结构(即ClojureScript映射和索引顺序数据结构)一起使用,如矢量)。

要了解更多关于光标的信息,我建议阅读OM的光标文档.

外部状态

通常,我们想要管理的状态不会在组件的直接接触范围内。那么,我们上面学到的两种机制还不够。

父组件

有时更改应用程序状态是不可行的,因为子组件通常只有一部分状态的光标。那么它的父母应该对此负责。但是他们如何与其他人沟通呢?

通常,在JavaScript中,这是由回调或事件冒泡处理的。但是在Clojurescript中,我们有更好的东西:队列式通道。这个克洛乔夫博客完美描述:

通道的一个关键特征是它们是阻塞的。以最原始的形式,没有缓冲的通道充当会合点,任何读者都会等待作家,反之亦然。

基本上,这使得我们可以在编写顺序代码时轻松地同步两个或多个异步操作。一切都回到托尼·霍尔的通信顺序过程(CSP)从20世纪70年代开始。它最近通过在GO编程语言.

图书馆提供频道异步电路,只有宏的力量才能实现。开始,我们需要在文件开头加载库:

(ns todomvc.app(:require macros[cljs.core.async.macros:refer[go]])(:require[cljs.core.async:refer[put!
       

这个todoapp组件使用威尔山Lifecycle函数,用于在应用程序启动后创建新的频道:

(defn todo app[:keys[todos]:as state owner](reify om/iwillmount(will mount[U](let[comm(chan))(om/set state!)所有者:comm comm)(go(while true(let[[类型值])(
        

(陈)返回新的无缓冲通道。然后用键将其添加到组件的状态COMM.最后,一块被打开:在里面我们发现一个无限循环,它从通道中读取一个值把它分解成一个有两个项的向量,[类型值].

重要的是要注意,从通道读取是一种阻塞操作,永远等待通过通道发送的数据。但由于JavaScript是单线程的,应用程序不会完全冻结吗?好,所以我们要把所有的东西都包起来 打电话:它是一个宏,允许包含全局事件循环中的事件循环。内部阻塞,而全局阻塞保持运行。

注:这个康姆通道现在被传递给每个需要它的函数或组件。为了避免混淆:上一篇文章中的所有代码片段都忽略了这一点。

回到我们的例子。从通道中读取的数据被传递到句柄事件

(defn handle event[type state val](case type:destroy(destroy todo state val):edit(edit todo state val):save(save todos state):cancel(cancel action state)nil))

函数使用案例根据的值选择要执行的表单类型.它处理所有事件待办事项组件可能要发送。让我们来看一下它的一个事件处理程序,当您单击TODO项上的红色“x”时,它会触发:

(defn destroy[todo comm](放!命令[:destroy@todo]))

它使用放!发送矢量[:销毁@todo]在海峡那边。注意到@它会预先取消对光标的引用-因此值会被发送,不是光标。然后通过我们在上面看到的无限循环阅读,传递给句柄事件最后作为参数破坏待办事项

(defn destroy todo[state:keys[id]](om/transact!状态:todos(fn[todos](vec(remove(=(:id%)id)todos)))

这里我们看到状态光标和todo的id用于更新应用程序状态。TODOS被所有TODOS的副本替换,不包括具有传入ID的任何项。记得,函数文本也可以写为(fn[todo](=(:id todo)id))。.

中的其他功能句柄事件基本上所有的工作方式都是一样的,我们现在还没看到什么。

这只是触及了渠道可能性的表面。要了解更多有关频道的信息,我建议阅读clojurescript core.async todos尝试一下这个源代码示例.

DOM节点

每个应用程序中都会有一段时间需要处理实际的DOM节点,例如阅读和改变它的价值。通过使用裁判关键字,一引用名可以分配给节点。后来,例如,在事件处理程序中,可以使用此名称访问节点。

在我们的应用程序中,我们需要在todoapp组件。这里有新增加的裁判关键:

(dom/input js:id“new todo”:ref“newfield”:onkeydown(输入new todo%state owner)……

裁判原地,事件处理程序进入新的待办事项可以访问节点:

(defn enter new todo[e state owner](when(==(.-which e)enter_key)(let[new field(om/get node owner“new field”)new field text(string/trim(.-value new field))](when not(string/blank?新字段文本)(让[新TODO:ID(GUID):标题新字段文本:已完成的FALSE](OM/Transact!状态:todos(conj%new todo))(设置!(.-value new field)“”)false)

当用户点击Enter键时,它会创建一个新的TODO:通过使用OM/GET节点带参数主人(基础反应成分)和纽菲尔德(引用名称)可以检索文本字段(本机浏览器DOM节点)。打电话-值在节点上读取其文本内容。如果它不是空的,则会创建一个新项目,并通过以下方式保存到应用程序状态:OM/交易!.最后,输入值被重置。

另一个例子是待办事项组件,现在添加了裁判关键:

(dom/输入js:ref“editfield”:类名“edit”……

双击TODO标签时,编辑调用事件处理程序:

(defn edit[e todo owner comm](让[todo@todo node(om/get node owner“editfield”)](放置!comm[:edit todo])(doto所有者(om/set状态!:需要焦点为真)(OM/设置状态!:编辑文本(:title todo))))

发生了很多事情!这个托多符号绑定到未引用的光标,并且结点到输入的dom节点。向量[编辑编辑]写入频道时放!(并由句柄事件我们之前看到的功能,将应用程序置于“编辑”模式。最后,组件状态将更新以反映对TODO的编辑。

注: 多多主人并以其作为第一个参数调用每个后续窗体的函数。

如果你想更多地了解参考资料,我就有你需要的:更多参考文献.


今天的表演到此结束!我们已经看到了在OM中如何管理状态。我们没有调查每一个示例应用程序的行,但您看到了最重要的部分。请随意浏览完整源代码去探索它的其他部分。

第5幕我们将学习如何正确发布应用程序。与此同时,以下是一些深入了解OM的链接:


评论由迪斯科