@@ -14,15 +14,15 @@ published: True
1414
1515![ Swiss Army Knife] ( https://1.bp.blogspot.com/-gYoZl5j7qPc/YDqm3n30tJI/AAAAAAAAxsU/ngw1DveOti8V8CmcNI2hqP44zI1Vd6dIwCLcBGAsYHQ/w640-h548/swiss-army-knife.jpg )
1616
17- Roam 就好像一把优秀的瑞士军刀,竟然包含一个完整的 ClojureScript 开发环境。在过去两周里,我逐渐熟悉了 ` {``{roam/render}} ` 。这篇文章可以算作我自己的笔记,总结整理一下我的所见所学。
17+ Roam 就好像一把优秀的瑞士军刀,竟然包含一个完整的 ClojureScript 开发环境。在过去两周里,我逐渐熟悉了 ` {``{roam/render}`` } ` 。这篇文章可以算作我自己的笔记,总结整理一下我的所见所学。
1818
1919这篇文章是针对开发者和 Roam 黑客的。如果你还不属于这些阵营,你很可能会对这些内容感到吃力。
2020
21- 我并不是一个 Web 开发者,React、Clojure、Reagent 和 Datalog 对我来说完全是全新的 。我是一个典型的无知者无畏的终端用户,我知道足够的知识来构建一个 macro 的怪物 ,但却无法保持其健壮/可测试/可维护。在玩` roam/render ` 的过程中,有一次我几乎把我的整个 roam 数据库都毁掉了 —— 这与` roam/render ` 无关,一切都是因为我太笨了。请谨慎尝试我的结论和例子,欢迎在评论中分享更好的解决方案,或者联系我进行勘误。
21+ 我并不是一个 Web 开发者,React、Clojure、Reagent 和 Datalog 对我来说完全是崭新的事物 。我是一个典型的无知者无畏的终端用户,我知道如何构建一个基于宏的庞然大物 ,但却无法保持其健壮/可测试/可维护。在玩` roam/render ` 的过程中,有一次我几乎把我的整个 roam 数据库都毁掉了 —— 这与` roam/render ` 无关,一切都是因为我太笨了。请谨慎尝试我的结论和例子,欢迎在评论中分享更好的解决方案,或者联系我进行勘误。
2222
2323## 什么是 roam/render?
2424
25- ` {``{roam/render}} ` 是[ Roam] ( https://www.roamresearch.com/ ) 中用于构建自定义组件的原生特性。这些组件可以是简单的表格、计算器、滑块,也可以是复杂的交互式工具,比如数据透视表、图表、流程图等。
25+ ` {``{roam/render}`` } ` 是[ Roam] ( https://www.roamresearch.com/ ) 中用于构建自定义组件的原生特性。这些组件可以是简单的表格、计算器、滑块,也可以是复杂的交互式工具,比如数据透视表、图表、流程图等。
2626
2727` roam/render ` 也是一个使用 Reagent 在 ClojureScript 中实现的 React 组件。你可以使用 Datalog 在 Roam 的 Datomic Graph 中访问你的数据。
2828
@@ -47,15 +47,15 @@ Roam 就好像一把优秀的瑞士军刀,竟然包含一个完整的 ClojureS
4747
4848### Hello World!
4949
50- 你可以通过在 Block 中添加以下代码来嵌入 roam/render: ` {``{roam/render: ((block-ref))}} ` 其中 ` block-ref ` 指的是你的脚本代码的块引用。脚本代码必须至少有一个函数。如果这个 Block 有多个函数,那么在组件创建时只会调用最后一个函数。
50+ 你可以通过在 Block 中添加以下代码来嵌入 roam/render: ` {``{roam/render: ((block-ref))}`` } ` 其中 ` block-ref ` 指的是你的脚本代码的块引用。脚本代码必须至少有一个函数。如果这个 Block 有多个函数,那么在组件创建时只会调用最后一个函数。
5151
5252被引用的代码块必须包含一个设置为 Clojure 类型的代码块。代码块的创建是通过使用三个反引号 ` ``` ` 来完成的。如果你不知道键盘上的反引号键在哪里,你也可以使用` / ` 菜单,然后选择 Clojure Code Block。
5353
5454![ Hello World Roam Render Demo] ( https://1.bp.blogspot.com/-MYRuH2resf0/YDqxC6MnBbI/AAAAAAAAxss/aOXSimG-DIsC9wVDRuszOIIhIfRQJmwBwCLcBGAsYHQ/w640-h360/HelloWorldRoamRender.gif )
5555
5656### 一步一步来
5757
58- 1 . 在一个新的 Block 中输入:` {``{roam/render:...} ` 。
58+ 1 . 在一个新的 Block 中输入:` {``{roam/render:(( ...))} } ` 。
59592 . 当你开始在双括号之间打字时,Roam 会弹出 "search for blocks"。选择 "Create as block below"。这将在你当前 Block 下面创建一个 Block,并自动在括号之间放置一个块引用。
60603 . 导航到新的 Block,并添加两个额外的反引号``来创建一个代码块。
61614 . 将代码的语言设置为 Clojure。
@@ -77,7 +77,7 @@ Roam 就好像一把优秀的瑞士军刀,竟然包含一个完整的 ClojureS
7777
7878我花了好几天的时间才发现如何做到这一点。我想让你省点力气! 当一个组件被调用时,Roam 会把 block-id 作为第 0 个参数传递给主函数(注意:你代码块中的最后一个函数)。当然,Roam 还会传递当你调用组件时,所输入的其他任何参数。
7979
80- 执行下面的脚本,输入新的一行` {``{roam/render:((Vy8uEQJiL)) 10 "input 1" ["input" "vector" "with" 5 "elements"] {:key1 "this is a map" "key2" "value 2" :key3 15}。(1 2 3) #{"a""b""c"}} ` 将产生以下输出。(当然,请注意,当你在自己的图中尝试这个脚本时,Block 的 ID ` ((Vy8uEQJiL)) ` 会有所不同,所以 "eR7tRno7B "也会不同。)
80+ 执行下面的脚本,输入新的一行` {``{roam/render:((Vy8uEQJiL)) 10 "input 1" ["input" "vector" "with" 5 "elements"] {:key1 "this is a map" "key2" "value 2" :key3 15}。(1 2 3) #{"a""b""c"}`` } ` 将产生以下输出。(当然,请注意,当你在自己的图中尝试这个脚本时,Block 的 ID ` ((Vy8uEQJiL)) ` 会有所不同,所以 "eR7tRno7B "也会不同。)
8181
8282![ datatypes / arguments example] ( https://1.bp.blogspot.com/-pD3eddnuTxM/YD0yQX3I1WI/AAAAAAAAxu0/-b0LRqDcc8I0b_f4wNc9hm31Uk89QwdiACLcBGAsYHQ/s16000/Arguments%2Bexample.jpg )
8383
@@ -160,13 +160,13 @@ Reagent 是表单交互的关键。它们允许你根据用户输入的数据,
160160- 依然是 ` query-list ` 函数,请注意我们没有在函数的结尾有一个类似于 JavaScript 的 ` return ` 的语句。在 Clojure 中,函数中最后一次调用的结果总是被返回。由于` query-list ` 只包含一次调用,所以在执行 query 时,将直接返回` rd/q ` 的结果。
161161- 请注意,在` main ` 函数中,` x ` 被定义为一个初始值为 "TODO" 的 Reagent atom。将声明放在` (fn[] ` 后面的匿名函数之外,可以确保在创建组件时,只会设置一次 atom,而不是在每一次往 INPUT 框输入任何文本时,都将其重置为默认值。
162162
163- 以上是由[ Conor] ( https://twitter.com/Conaw?s=20 ) 的一个例子改编的。你可以在 Roam help 数据库 [ 这里] ( https://roamresearch.com/#/app/help/page/Tbl1U8OFT ) 找到 Conor 的版本。我的解决方案和 Conor 的解决方案之间的一个关键区别是,他使用的是` roam.datascript.reactive ` ,而不是仅仅使用` roam.datascript ` 。在这个具体的例子中,从我的理解来看它们是没有区别。如果我的理解是正确的,Datascript reactive 提供了一种创建查询的方法,当其结果集发生变化时,它可以自动识别。它们可以用于创建交互式组件,比如` {``{table}} ` 。
163+ 以上是由[ Conor] ( https://twitter.com/Conaw?s=20 ) 的一个例子改编的。你可以在 Roam help 数据库 [ 这里] ( https://roamresearch.com/#/app/help/page/Tbl1U8OFT ) 找到 Conor 的版本。我的解决方案和 Conor 的解决方案之间的一个关键区别是,他使用的是` roam.datascript.reactive ` ,而不是仅仅使用` roam.datascript ` 。在这个具体的例子中,从我的理解来看它们是没有区别。如果我的理解是正确的,Datascript reactive 提供了一种创建查询的方法,当其结果集发生变化时,它可以自动识别。它们可以用于创建交互式组件,比如` {``{table}`` } ` 。
164164
165165## 如何存储组件的属性值
166166
167167当你重新打开页面或关闭并重新打开 Roam 时,组件会被重新初始化。在大多数情况下,你会希望以某种方式存储组件的属性,这样当你下次打开页面时,你会发现组件处于你离开时的状态。
168168
169- 你可以选择将信息写入 Block 中,并决定写入哪一个 Block。你可以写到嵌套在组件下的 Block ,或者写到一个工具页面上的 Block 里面,例如` [[my component/data]] ` 或者更新执行组件的那个 Block。最后一种选择涉及更新` {``{roam/render: ((block-UID)) }} ` 与 input 参数,这与我们在前面的例子中打印 input 参数的方式有些类似。我将在一个非常简单的例子中演示如何做到这一点。
169+ 你可以选择将信息写入 Block 中,并决定写入哪一个 Block。你可以写到嵌套在组件下的 Block ,或者写到一个工具页面上的 Block 里面,例如` [[my component/data]] ` 或者更新执行组件的那个 Block。最后一种选择涉及更新` {``{roam/render: ((block-UID)) }`` } ` 与 input 参数,这与我们在前面的例子中打印 input 参数的方式有些类似。我将在一个非常简单的例子中演示如何做到这一点。
170170
171171顺便说一下,我用` datascript.core ` 做了一个实验,将自定义实体写入到 Roam 数据库。我也能够执行查询,但是没有找到一种方法让 Roam 保存更改。手动编辑自定义实体到 EDN 文件中是可行的([ 演示] ( https://twitter.com/zsviczian/status/1353772383186923520?s=20 ) ),所以使用` datascript ` 添加自定义实体应该也是可行的。
172172
@@ -202,7 +202,7 @@ Reagent 是表单交互的关键。它们允许你根据用户输入的数据,
202202
203203这个组件将保存 ` some-value ` 的值。在这个例子中,为了简单起见,它是硬编码的,但当然,你可以构建任何你想要的数据结构来代替 ` some-value ` 。请注意以下几点:
204204
205- - 我的 ` save ` 函数是在 main 函数中按钮的` :on-click ` 事件中调用的。在我的实验中,每次组件改变其值时自动调用 save 的效果并不好,因为每次覆盖` {``{roam/render: ((block-UID))}} ` 的时候,组件都会重新初始化,使得无法填写表单,或者通过交互的方式使用组件。
205+ - 我的 ` save ` 函数是在 main 函数中按钮的` :on-click ` 事件中调用的。在我的实验中,每次组件改变其值时自动调用 save 的效果并不好,因为每次覆盖` {``{roam/render: ((block-UID))}`` } ` 的时候,组件都会重新初始化,使得无法填写表单,或者通过交互的方式使用组件。
206206- 我在 ` save ` 函数中定义了三个变量。` code-ref ` 、` arg-str ` 和` render-string ` 。
207207- ` code-ref ` 将保存 ` block-string ` 的当前值,因为我在执行 datalog 查询的时候,会读取` :block/string ` 属性的当前值,并通过` block-uid ` 过滤。
208208- ` ->> ` 是一个函数,它通过一组 ` (str) ` 和` (re-find) ` 的形式来执行表达式。它的唯一目的是让代码更易读。
@@ -262,7 +262,7 @@ ClojureScript 提供了一种调用 JavaScript 函数的简单方法。当你想
262262 (.myDemoFunction js/window @x)])))
263263```
264264
265- 以及 ` {``{roam/js}} ` 的代码。
265+ 以及 ` {``{roam/js}`` } ` 的代码。
266266
267267``` clojure
268268window['myDemoFunction'] = function (x ) {
@@ -327,11 +327,11 @@ window['myDemoFunction'] = function (x) {
327327
328328### 调用组件
329329
330- ` {``{roam/render: ((block-UID))}} ` 引用组件的形式不是很友好。它需要点击几次才能得到 ` block-uid ` 并将其插入到渲染的 Block 中。我发现了两种选择。
330+ ` {``{roam/render: ((block-UID))}`` } ` 引用组件的形式不是很友好。它需要点击几次才能得到 ` block-uid ` 并将其插入到渲染的 Block 中。我发现了两种选择。
331331
332332你可以在 roam/templates 中创建一个简单的本地漫游模板,然后在那里用一个友好的名字添加你的组件。然后,当你需要它时,你可以使用` ;;template-name ` 将组件插入到你的文档中。
333333
334- 你也可以 Hack 一下 block 的 UIDs。我在[ 这里] ( https://twitter.com/zsviczian/status/1365606725924044808?s=20 ) 提供了一个解决方案。这将允许你用更友好的名字来创建组件,比如我的 query 查询组件。 ` {``{roam/render: ((str_query))}} ` 。
334+ 你也可以 Hack 一下 block 的 UIDs。我在[ 这里] ( https://twitter.com/zsviczian/status/1365606725924044808?s=20 ) 提供了一个解决方案。这将允许你用更友好的名字来创建组件,比如我的 query 查询组件。 ` {``{roam/render: ((str_query))}`` } ` 。
335335
336336### 前置声明
337337
@@ -343,7 +343,7 @@ window['myDemoFunction'] = function (x) {
343343
344344最简单的方法是使用` roamAlphaAPI ` 创建一个 Block,并在这个 Block 字符串中放置一个` page link ` 或` block-ref ` 。这将转换为一个正确的 Roam 链接。
345345
346- 另一种方法是创建一个包含 Roam 查询的 Block,并在查询参数中包含你想显示的链接:` {``{query: {or: page linkblock-ref}} ` 。
346+ 另一种方法是创建一个包含 Roam 查询的 Block,并在查询参数中包含你想显示的链接:` {``{query: {or: page linkblock-ref}`` } ` 。
347347
348348由于 Roam 在几天前发布了` roam.right-sidebar ` 命名空间,现在可以完全模仿 Roam 原生链接了。我还没有时间去试验这第三个选项,但它看起来是可行的。
349349
0 commit comments