我正在ClojureScript / Reagent中开发一个简单的“类似wiki”的超文本视图。
我有一些短信。我现在想把匹配LinkPattern的东西变成可点击的链接。
在“传统”wiki编程中,我只使用regexes代替URL模式。
例如:转弯
this is SomeText that's a link
转到
this is <a href="/view/SomeText">SomeText</a> that's a link
但在这个版本中
( a)我不想使它们成为正常的html锚链接,我想在链接上附加某种点击事件处理程序(这将触发对服务器的Ajax调用,而不是重新加载整个页面)。
( b)我在Clojure的Reagent包装器中工作,负责React。因此,我假设我不想简单地将原始Javascript复制到html中。我希望以一种“原则性”的方式附加事件处理程序,这对ClojureScript和React / Reagent都是正确的。
那我该怎么做呢?
FYI,我正在将标记预处理成HTML,然后使用:危险设置insert将其插入到页面中。因此,无论我如何将单击处理程序添加到链接中,都需要在这种情况下生存下来。
或者我必须在呈现事件处理程序之后添加它们吗?
在这种情况下,我该怎么做?我知道我在JQuery中要做什么,使用"bind“将事件处理程序附加到特定类的跨范围。但是,我对在ClojureScript试剂中正确地思考这个问题感到困惑。在这个世界中,jQuery的"bind“相当于什么?
发布于 2019-12-22 23:48:13
我认为您可能在错误的路径上使用wrt :危险设置I。由于您使用的试剂(它是一个反应包装器),您很少希望以这种方式将HTML注入DOM。
基本方法是对所有HTML使用hiccup,并将应用程序构造为试剂组件的层次结构。除了你使用的试剂成分,你还会有一些“状态”存储在一个试剂原子(或原子)中。一旦连接了所有的位元,应用程序本质上是由对此状态的更改驱动的(您将试剂留给管理DOM)。
例如,(非常简化,从我的头顶!)
您可能有一个具有以下结构的状态原子
(def state (reagent/atom {:articl1 {:title "An Article"
:body "This is the body of the article"
:read false}
:article2 {:title "A second article"
:body "Yet another article"
:read false}})上面的状态原子可以通过AJAX调用来填充以获取文章列表。我可以定义三种试剂成分-
可以使用许多方法,但essent8ially的思想是,项目列表组件从状态原子获取文章列表,遍历列表,为每一篇文章调用项目组件。文章组件将为每一篇文章生成html --可能会调用其他组件来帮助它(例如,read切换组件,它可能会显示一些文本,其中包含一个单击处理程序,该处理程序将在状态原子中切换:read属性。例如
(defn read-toggle-component [path]
[:div {:on-click (fn []
(swap! state update-in path not))}
(if (get-in @state [path])
"Mark unread"
"Mark read"])以上内容将生成内容为"Mark“或"Mark”的HTML,具体取决于路径指向的状态原子中的值。例如,这可能是:第1条:从上面的示例读取。如果值为true,则将呈现"Mark“,如果值为false,则将呈现"Mark”。单击处理程序将在单击此值时切换此值。
上面的组件又可以由项目组件调用,这可能类似于
(defn article-component [article-id]
(let [article (get @state article-id)]
[:article
[:h2 (:title article)]
(when (not (:read article))
[:p (:body article)])
[read-toogle-component [article-id :read)]]))上面的组件将在标签中呈现一篇文章。文章将包含一个标题,可能包含文章主体(取决于:read的值)和一个切换按钮,以在true和false之间切换:read属性。
最后,您将拥有文章列表组件,这可能类似于
(defn article-list-component []
(let [articles (keys @state)]
(into
[:section]
(for [a articles]
[article-component a]))))上面的标签会产生一个标签,在这个标签里,每一篇文章都有标签。
然后,您将在您的clojureScript中得到一些东西,它将挂载文章列表组件。更好的是,您不需要直接处理HTML,甚至不需要直接与DOM交互。页面将在试剂原子被修改后立即更新(通过添加新文章或更改:read属性所带来的天气变化)。
以上是非常粗糙和简化给你的想法。这肯定是充满错误的,但我希望你会指出正确的方向。
你应该熟悉的事情-
最后两个是由重帧的作者编写的,它是一个非常强大但优雅的基于试剂的应用程序编写框架,非常值得一看。即使您认为它对于您的需求来说太大/太复杂,但是在重新框架中有一些非常好的想法,您可以适应您的需求。
如果您还没有这样做,请确保检查凸轮车主。除了提供一个很好的发展环境之外,它还提供了一种简单的“实验”和获得即时反馈的方法,这是学习clojure/clojurescript/试剂的最好方法。
发布于 2019-12-23 15:58:47
你的问题似乎有多个部分。我已经做了一段时间的个人维基了。(存储库这里,直到使用Mercurial后的几个月内,BitBucket将其删除。)
事件处理程序
我的大部分程序都是用Clojure编写的,只有一个Markdown编辑器和用ClojureScript编写的调整大小的控件。我惊讶地获悉,您可以使用带有JavaScript的字符串将事件处理程序添加到hiccup组件中来运行,至少在我尝试过的简单情况下是这样。
例如,下面是我(服务器端)页面呈现逻辑中的一个函数:
(defn sidebar-and-article
"Return a sidebar and article div with the given content."
[sidebar article]
[:div {:class "sidebar-and-article"}
sidebar
[:div {:class "vertical-page-divider"}]
[:div {:class "vertical-page-splitter"
:id "splitter"
; Don't forget to translate the hyphen to an underscore. The false
; return is required for correct behavior on Safari.
:onmousedown "cwiki_mde.dragger.onclick_handler(); return false;"}]
[:article {:class "page-content"}
article]])即使这是服务器的一部分,在Clojure中,也可以为:onmousedown事件指定一个处理程序。此代码在页上的列之间绘制垂直分隔符。事件处理程序允许用户拖动拆分器来调整列的大小,然后将这些列发送回服务器,以存储在用户首选项数据库中。
编辑
由于OP要求,添加事件处理程序的方法可以调用用ClojureScript编写的处理程序。例如,在上面的片段中,处理程序引用wiki_mde.dragger.onclick_handler,它只是命名空间中的一个函数:
(ns cwiki-mde.dragger
(:require [ajax.core :refer [ajax-request text-request-format
text-response-format]]))
.
.
.
(defn ^{:export true} onclick_handler []
(reset! sidebar-ele (.getElementById js/document "left-aside"))
(reset! starting-basis (- (.-offsetWidth @sidebar-ele) twice-padding-width))
(reset! dragging true)
(.addEventListener js/window "mousedown" start-tracking)
(.addEventListener js/window "mousemove" move)
(.addEventListener js/window "mouseup" stop-tracking))不过,OP的评论指出了一些令人困惑的地方。带有事件处理程序的字符串的内容实际上是JavaScript。碰巧的是,ClojureScript编译器已经将函数名转换为JavaScript等效名称。
另一个令人惊讶、也可能令人困惑的事情是,字符串的内容不仅仅是一个函数名,而是两个JavaScript语句。必须添加额外的return false;才能使一个浏览器工作。我感到惊讶的是,竟然有两种说法可以被使用。
链接
和您一样,我最初使用正则表达式来查找和识别wiki链接。然而,他们只是普通的链接与一些样式,以区别链接到不存在的网页.创建链接的方案有点像MediaWiki中使用的方案,它只构建服务器可识别的链接。
从那以后,我转向了一个更高级(更快)的Markdown转换器,柔性标记-java。我不得不为转换器编写一些自定义代码来处理wiki链接。不过,这使我的Java互操作技能达到了极限。也许你可以用这样的东西来构建你想要的链接的类型。
渲染
我认为没有必要使用:dangerouslySetInnerHTML。我处理它的方法是在呈现页面的同时将Markdown转换成HTML,并像其他人所解释的那样将HTML放入一个hiccup :div中。我在服务器上进行转换和页面构建,以响应对上述链接之一的单击。
例如,下面是我现在用于生成上面清单中的article和sidebar内容项的配置。这大部分只是fexmark-java的配置。
(def options (-> (MutableDataSet.)
(.set Parser/REFERENCES_KEEP KeepType/LAST)
(.set HtmlRenderer/INDENT_SIZE (Integer/valueOf 2))
(.set HtmlRenderer/PERCENT_ENCODE_URLS true)
(.set TablesExtension/COLUMN_SPANS false)
(.set TablesExtension/MIN_HEADER_ROWS (Integer/valueOf 1))
(.set TablesExtension/MAX_HEADER_ROWS (Integer/valueOf 1))
(.set TablesExtension/APPEND_MISSING_COLUMNS true)
(.set TablesExtension/DISCARD_EXTRA_COLUMNS true)
(.set TablesExtension/WITH_CAPTION false)
(.set TablesExtension/HEADER_SEPARATOR_COLUMN_MATCH true)
(.set WikiLinkExtension/LINK_FIRST_SYNTAX true)
(.set WikiLinkExtension/LINK_ESCAPE_CHARS "")
(.set Parser/EXTENSIONS (ArrayList.
[(FootnoteExtension/create)
(StrikethroughExtension/create)
; Order is important here.
; Our custom link resolver must
; preceed the default resolver.
(CWikiLinkResolverExtension/create)
(WikiLinkExtension/create)
(CWikiLinkAttributeExtension/create)
(TablesExtension/create)]))))
(def parser (.build ^Parser$Builder (Parser/builder options)))
(def renderer (.build ^HtmlRenderer$Builder (HtmlRenderer/builder options)))
(defn- convert-markdown-to-html
"Convert the markdown formatted input string to html
and return it."
[mkdn]
(->> mkdn
(.parse parser)
(.render renderer)))希望这会有帮助。
发布于 2019-12-23 09:44:43
您可以将标记转换为hiccup (或直接响应元素),而不是HTML。这将让React负责呈现方面,您可以将事件处理程序附加到需要它们的位置,而不必担心DOM。
所以,与其转向
this is SomeText that's a link转到
this is <a href="/view/SomeText">SomeText</a> that's a link你把它变成
[:div "this is " [:a {:href "/view/SomeText" :onClick (fn ...)} "SomeText"] "that's a link"]或者,如果不希望包含元素,则使用:<>而不是:div。
https://stackoverflow.com/questions/59437437
复制相似问题