Азот 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").

Ожидаемое поведение:

  1. Пользователь нажимает на myBtn,
  2. Появилось предупреждающее сообщение "myBtn clicked"
  3. Появился новый элемент "Динамический контент". Эта часть работает.

  4. Пользователь нажимает кнопку в разделе "Статический", созданную функцией test2:get_content/1,

  5. Появилось сообщение "Btn_TEMP1" нажал. Пока все работает как положено.

Теперь: 6. Пользователь нажимает на кнопку в разделе "Динамический", которая была сгенерирована с помощью функции test2:get_content/1 и добавлена ​​на страницу с помощью вызова wf:insert_after().

  1. Ожидаемое поведение: появилось сообщение "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. Соберите несколько азотных элементов и храните их в переменных.
  2. Подключите действие к одному из элементов, определенных в (1)
  3. Вернуть ранее сделанные элементы.

В статическом запросе (или, скорее, на языке азота, first_request, поскольку в азоте "статический запрос" относится к чисто статическому контенту (например, запрашивает файлы изображений или что-то еще), это прекрасно. Статический контент возвращается, помещается на страницу, а затем любые проводные действия вставляются в [[[script]]] раздел в нижней части шаблона.

Однако в динамическом запросе порядок операций становится важным.

test2:get_content Функция, которую вы определили, делает то же самое, но функция оценивается, и содержимое возвращается wf:insert_after функция, которая затем вставляет элементы в DOM.

Ошибка находится в указанном выше абзаце, но я вписываюсь в общий контекст, чтобы сделать его более очевидным. С динамическим запросом:

  1. Призыв к wf:insert_after (или wf:insert_before, или же wf:update, или же wf:replace или любой из них) необходимо test2:get_content быть оцененным первым. Так оцените test2:get_content()
  2. Соберите несколько азотных элементов и храните их в переменных.
  3. Подключите действие к одному из элементов, определенных в (2)
  4. Вернуть ранее сделанные элементы.
  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 Функция оценивает.

Другие вопросы по тегам