NITRO: comboLookup
Категорії: дизайн та імплементація контрольних елементів NITRO, технічне завдання.
Покажемо на прикладі #comboLookup як створювати контрольні елементи у середовищі N2O NITRO. #comboLookup — це контрольний елемент для пошуку та відображення результатів в універсальних гетерогенних персистентних віддалених послідовностях, з порціонною видачею результатів та підтримкою персональних курсорів.
Дизайн
Скафолдінг контрольного елементу
Контрол стандартно постачається у 3—4 файлах:
— priv/css/comboLookup.css — CSS частина
— priv/js/comboLookup.js — JavaScript частина
— include/comboLookup.hrl — спільний код для Erlang та Elixir
— src/comboLookup.erl або lib/comboLookup.ex
Розробка компонент
Контрол структурується як: 1) поле вводу для пошуку по зовнішньому віддаленому сервісу; 2) контейнер для результатів пошуку; 3) елементи результатів пошуку (насправді спеціалізовані редактори вводу відповідних елементів списку результатів пошуку); 4) управляючі елементи: кнопка пошуку та кнопка вібірни наступної порції даних через серверний курсор пошуку.

Кожна компонента 1—4 повинна бути окремим DOM елементом, а елементи результатів пошуку (4) повинні мати свої Erlang або Elixir визначення, так як ці форми відображають безпосередньо дані пошукових фідів, і таким чином є обов'язковими.
Розробка протоколу
За основу пропонується взять наступний протокол управління контрольним елементом:
— #comboKeyup{value=[],dom=[],feed=20}, де value — це введені користувачем символи
для повнотекстового або довільного пошуку по фіду на сервері. Серверний код
цього повідомлення повинен виконати lookup по базі даних та пересунути курсор
на кількіть елементів у первій видачі.
— #comboNext{pos=[],count=[],feed=[]}, повторний запит на видачу результатів
попередньо виконаного пошуку, або просто видача даних з пересуненням курсору
на кількість елементів виданій у цьому запиту.
Як і кожне повідомлення протоколу NITRO, результати обробки протоколу контрольних елементів повинні бути IO повідомленнями {io,Code,Data}, де Code — це JavaScript з відрендереними на сервері HTML та JavaScript частинами форми, які вбудовують результати видачі у контейнер (2). Тому у тілі функції протоколу контрольного елементу ми формуємо actions (які будуть скинуті в сокет протколом NITRO в повідомленні IO) за допомогою :nitro.wire, або іншої функції з побічними ефектами які генерують JavaScript в контексті Erlang процесу.
Імплементація
Визначення бізнес-об'єкту
Для можливості користуватися одночасно Erlang та Elixir, контрольний елемент на базі HTML5 (ELEMENT_BASE) повинен визначатися в HRL файлі.
$ cat include/comboLookup.hrl
-record(comboInit, { name=[], feed=[]}).
-record(comboSearch, { prefix=[]}).
-record(comboNext, { pos=[], count=[]}).
-record(comboLookup, { ?ELEMENT_BASE(nx_comboLookup),
feed="/erp/people",
reader=[],
chunk=20 }).
Де "/erp/people" — адреса фіду на сервері (можна подивитися за допомогою :kvs.all)
Рендер, бінд та налаштування івентінгу
Оскільки контрольні елементи породжують HTML та JavaScript, рендер контрольного елементу по module полю — це саме те місце, де зазвичай підключаються шаблонізатори, як то: DTL, JSP, ASP, PHP. У нашому випадку ми рекурсивно використовуємо бібліотеку контрольних елментів NITRO, поки повний рендер починаючи з верхнього не буде завершено, або природній Erlang шаблонізатор з мовою Erlang (або Elixir).
$ cat src/nx_comboLookup.erl
-module(nx_comboLookup).
-include("comboLookup.hrl").
-include("erp.hrl").
-include_lib("nitro/include/nitro.hrl").
-include_lib("nitro/include/event.hrl").
-export([render_element/1,proc/1]).
render_obj(#'Person'{cn=CommonName,type=Type}=Person) ->
#panel{ body=[ #image { src = "default.png" },
#h1 { body = CommonName },
#p { body = Type } ] }.
render_element(#comboLookup{}=Record) ->
nitro:wire("console.log(\"Hello comboLookup!\");"),
"<input id=\"comboContainer\" type=\"comboLookup\"></input>".
Імплементація протоколу
Завдання написати найбільш компактну імплементацію з індивідуальними курсорами для кожного екземпляру контрольного елементу на стороні клієнта.
proto(#comboInit{feed=Feed,name=Name}) ->
kvs:save(kvs:reader(Feed)),
n2o:session(Name,Feed),
ok;
proto(#comboSearch{prefix=Name}) ->
Feed = n2o:session(Name),
lists:map(fun (X) -> nitro:insert_top(comboContainer, render_obj(X)) end,
lists:all(fun(#process{name=N}) -> N == Name end, kvs:all(Feed))),
ok;
proto(#comboNext{pos=Pos,count=Count}) ->
Reader = kvs:reader(Feed),
ok.
Підключення протоколу до модулю сторінки:
Позаяк протокол сторінки — це кінцева точка усіх клієнтських повідомлень NITRO протоколу, аби доставити повідомлення в модуль контрольного елементу потрібно підключити протокол comboLookup в модуль сторінки з відповідними форвардами.
$ cat src/container.erl
event(#comboInit{} =X) -> nx_comboLookup:proto(X);
event(#comboSearch{}=X) -> nx_comboLookup:proto(X);
event(#comboNext{} =X) -> nx_comboLookup:proto(X).
Розробка клієнтської частини
Мінімальне публічне API контрольного елементу.
$ cat priv/js/comboLookup.js
function direct(term) { ws.send(enc(tuple(atom('direct'),term))); }
function comboId() { return localStorage.getItem("comboLookup-1218297")||'';};
function comboInit() { direct(tuple(atom('comboInit'),string(comboId()))); }
function comboSearch() { direct(tuple(atom('comboSearch'),'Maxi')); }
function comboNext() { direct(tuple(atom('comboNext'),1,1)); }
Специфікація на стилі
$ cat priv/css/comboLookup.css
Для самостійного опрацювання.
Додаток 1
Перелік функцій на стороні JavaScript для реалізаціі мінімального клієнта N2O NITRO протоколу:
token() — поточний токен сесії
qi(name) — DOM елемент по існуючому id
qs(name) — пошук DOM елементу
qn(name) — створення нового DOM елементу
is(x, num, name)
co(name) — отримання cookies (застаріло, використувуємо localStorage)
querySource(name) — отримання преформатованої змінної поля value
validateSources(list) — провалідувати поле value для списку елементів
N2O_start() — створення веб-сокет каналу
$bert — реалізація бінарного берт протоколу на JavaScript
$io — IO протокол (евалуатор та помилки)
$file — FTP протокол (трансфер великих файлів до 100ГБ)
$ws — вебсокет транспорт
transports — транспорти
protos — протоколи
— NITRO — Загальна інформація про веб-фреймворк NITRO
— N2O — Опис контейнера протоколів та сервісів N2O
— N2O NITRO — Опис протоколу веб-фреймворку
— BERT.JS — Опис бінарного протоколу