I have 2 modules:
test2
- Module create div element with a button id = wf:temp_id()
- wire #alert{} event to it.
- return these HTML elements so they could be used by other modules.
test
- Module create page content with a button myBtn and postback message {btn_clicked,myBtn}
- #alert{"myBtn clicked"} event to it,
- add empty div with id: id_insert_here - to be used as a placeholder for the dynamic element
- call test2:get_content("Static"), to add content from the module test2.
Then in event function, it use #wf:insert_after to add content from the test2:get_content("Dynamic").
Expected behavior:
- User click on myBtn,
- Alert message "myBtn clicked" appeared
New element "Dynamic content" appeared This part is working.
User click on button in "Static" section that was generated from the test2:get_content/1 function,
- Message "Btn_TEMP1" clicked appeared. So far everything is working as expected.
Now: 6. User click on button in "Dynamic" secion that was generated using test2:get_content/1 function, and added to the page using wf:insert_after() call.
- Expected behavior: Message "btn_TEMP2" clicked appeared.
Realized behavior - nothing happened.
wf:wire does not work.
Q: How to enure that wf:wire() works fo the elements that are added using wf:insert_XXX function?
%% -*- 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
Module test2:
-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.
Great Question, and one that needs a proper answer in the nitrogen docs.
The short answer is that in
test2:get_content, you should just replacewf:wirewithwf:defer.The reasoning for it is a little longer, but it goes like this:
In both situations (both static and dynamic), when
test2:get_contentis called it does the following in this order:In a static request (or rather, in nitrogen parlance, a
first_request, since in Nitrogen, a "static request" refers to purely static content like requesting image files or whatever), this is perfectly fine. Static content is returned, placed into the page, and then any wired actions are inserted into the[[[script]]]section at the bottom of the template.In a dynamic request, however, the order of operations becomes important.
The
test2:get_contentfunction you've defined does the same thing, but the function is evaluated and the content is getting returned to thewf:insert_afterfunction, which then inserts the elements into the DOM.The bug resides subtly in the above paragraph, but I'll fit in the overall context to make it more obvious. With the dynamic request:
wf:insert_after (orwf:insert_before, orwf:update, orwf:replaceor any of those) needs totest2:get_contentto be evaluated first. So evaluatetest2:get_content()wf:insert_aftertakes those returned elements and wires them to the page.What happens here is that because the
wf:insert_afteris effectively happening last, thewf:wireto the element goes nowhere. That is, the elementwf:wireis trying to be attached to doesn't exist in the DOM yet.It's like this pseudocode:
Using
wf:deferin place ofwf:wireensures the wiring happens after theinsert_XXXfunction evaluates.