Nitrogen wf:wire() 不适用于动态内容

Nitrogen wf:wire() does not work for dynamic content

我有 2 个模块:

test2

测试

然后在事件函数中,它使用#wf:insert_after添加来自test2:get_content("Dynamic").

的内容

预期行为:

  1. 用户点击 myBtn,
  2. 出现警告消息"myBtn clicked"
  3. 出现新元素"Dynamic content" 这部分正在运行。

  4. 用户单击从 test2 生成的 "Static" 部分中的按钮:get_content/1 函数,

  5. 消息 "Btn_TEMP1" clicked 出现。 到目前为止一切都按预期工作。

现在: 6. 用户单击使用 test2:get_content/1 函数生成的 "Dynamic" 部分中的按钮,并使用 wf:insert_after() 调用添加到页面。

  1. 预期行为:消息 "btn_TEMP2" 已点击出现。

已实现的行为 - 什么都没发生。

wf:wire 无效。

Q: 如何确保 wf:wire() 对使用 wf:insert_XXX 函数添加的元素有效?

%% -*- mode: nitrogen -*-
-module (test).
-compile(export_all).
-include_lib("../deps/nitrogen_core/include/wf.hrl").

main() -> 
    #template { file="./priv/templates/bare.html",
       bindings=[{'PageTitle',<<"Test">>}]}.

title() -> 
    #template{bindings = L} = main(),
    proplists:get_value('PageTitle',L).

body() ->
    Body = [
       #panel{class="w3-panel w3-green",
              body = 
                    [#p{text = "some text"},
                     #button {
                        id=myBtn,
                        text="myBtn",
                        postback={btn_clicked,myBtn}
                    },
                    #panel{id = id_insert_here}
                    ]}
     ],
    wf:wire(myBtn, #event{ type= click, actions = [ #alert{text="MyBtn clicked"} ]}),
    Body,
    Content = test2:get_content("Pre-compiled"),
    Body2 = [Body,Content],
    Body2.

event({btn_clicked,myBtn} = Event) ->
    io:format("Event: ~p",[Event]),
    Content = test2:get_content("Dynamic"),
    wf:insert_after(id_insert_here,Content);

event(Event) ->
    io:format(" Unexpected Event: ~p ",[Event]),
    ok.
%% end of module test

模块测试 2:

-module(test2).
-compile(export_all).
-include_lib("../deps/nitrogen_core/include/wf.hrl").

get_content(Title) ->
    MyId = wf:temp_id(),
    BtnText = io_lib:format("Btn_~p",[MyId]),
    Body = [
    #panel{class="w3-panel w3-yellow",
           body = 
                [#p{text = Title},
                 #button {
                    id=MyId,
                    text=BtnText
                    }]
        }],
    Msg = io_lib:format("Btn: ~p clicked",[MyId]),
    wf:wire(MyId, #event{ type= click, actions = [ #alert{text= Msg} ]}),
    Body.

好问题,需要在氮文档中找到正确答案。

简短的回答是,在 test2:get_content 中,您应该将 wf:wire 替换为 wf:defer

它的推理有点长,但它是这样的:

在这两种情况下(静态和动态),当调用 test2:get_content 时,它会按以下顺序执行以下操作:

  1. 构建一些氮元素并将它们存储在变量中。
  2. 将动作连接到 (1) 中定义的元素之一
  3. Return 之前制作的元素。

静态请求中(或者更确切地说,用氮气的说法,first_request,因为在氮气中,"static request"指的是纯静态内容,如请求图像文件或其他),这完全没问题。返回静态内容并将其放入页面中,然后将任何有线操作插入模板底部的 [[[script]]] 部分。

但是,在动态 请求中,操作顺序变得很重要。

您定义的 test2:get_content 函数做同样的事情,但是函数被计算并且内容被返回到 wf:insert_after 函数,然后将元素插入到 DOM.

错误隐匿在上面的段落中,但我会融入整体上下文以使其更加明显。使用动态请求:

  1. wf:insert_after(或 wf:insert_beforewf:updatewf:replace 或其中任何一个)的调用需要 test2:get_content 进行评估第一的。所以评估 test2:get_content()
  2. 构建一些氮元素并将它们存储在变量中。
  3. 将动作连接到 (2) 中定义的元素之一
  4. Return 之前制作的元素。
  5. wf:insert_after 获取那些返回的元素并将它们连接到页面。

这里发生的事情是,因为 wf:insert_after 实际上是最后发生的,所以元素的 wf:wire 无处可去。也就是说,wf:wire 尝试附加的元素在 DOM 中不存在。

就像这样的伪代码:

$('#my_new_element').click(function(){do_something}));
$('#body').insert_bottom("<button id=my_new_element>click</button>");

使用 wf:defer 代替 wf:wire 确保接线发生 insert_XXX 函数求值之后。