Азот wf:wire() не работает для динамического контента
У меня есть 2 модуля:
test2
- Модуль создания элемента div с кнопкой id = wf:temp_id()
- подключите к нему событие #alert{}.
- вернуть эти элементы HTML, чтобы они могли использоваться другими модулями.
тестовое задание
- Модуль создания содержимого страницы с помощью кнопки myBtn и постбэка {btn_clicked, myBtn}
- #alert {"myBtn clicked"} событие для него,
- добавить пустой div с id: id_insert_here - для использования в качестве заполнителя для динамического элемента
- вызовите test2:get_content("Static"), чтобы добавить контент из модуля test2.
Затем в функции события используется #wf:insert_after для добавления содержимого из test2:get_content("Dynamic").
Ожидаемое поведение:
- Пользователь нажимает на myBtn,
- Появилось предупреждающее сообщение "myBtn clicked"
Появился новый элемент "Динамический контент". Эта часть работает.
Пользователь нажимает кнопку в разделе "Статический", созданную функцией test2:get_content/1,
- Появилось сообщение "Btn_TEMP1" нажал. Пока все работает как положено.
Теперь: 6. Пользователь нажимает на кнопку в разделе "Динамический", которая была сгенерирована с помощью функции test2:get_content/1 и добавлена на страницу с помощью вызова wf:insert_after().
- Ожидаемое поведение: появилось сообщение "btn_TEMP2".
Реализованное поведение - ничего не произошло.
WF: провод не работает.
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
Модуль 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.
1 ответ
Отличный вопрос, и тот, который нуждается в правильном ответе в азотных документах.
Краткий ответ: test2:get_content
нужно просто заменить wf:wire
с wf:defer
,
Причина этого немного длиннее, но выглядит так:
В обеих ситуациях (как статических, так и динамических), когда test2:get_content
называется он делает следующее в следующем порядке:
- Соберите несколько азотных элементов и храните их в переменных.
- Подключите действие к одному из элементов, определенных в (1)
- Вернуть ранее сделанные элементы.
В статическом запросе (или, скорее, на языке азота, first_request
, поскольку в азоте "статический запрос" относится к чисто статическому контенту (например, запрашивает файлы изображений или что-то еще), это прекрасно. Статический контент возвращается, помещается на страницу, а затем любые проводные действия вставляются в [[[script]]]
раздел в нижней части шаблона.
Однако в динамическом запросе порядок операций становится важным.
test2:get_content
Функция, которую вы определили, делает то же самое, но функция оценивается, и содержимое возвращается wf:insert_after
функция, которая затем вставляет элементы в DOM.
Ошибка находится в указанном выше абзаце, но я вписываюсь в общий контекст, чтобы сделать его более очевидным. С динамическим запросом:
- Призыв к
wf:insert
_after (илиwf:insert_before
, или жеwf:update
, или жеwf:replace
или любой из них) необходимоtest2:get_content
быть оцененным первым. Так оценитеtest2:get_content()
- Соберите несколько азотных элементов и храните их в переменных.
- Подключите действие к одному из элементов, определенных в (2)
- Вернуть ранее сделанные элементы.
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
Функция оценивает.