INTRO
Модуль COMBOLOOKUP використовується для створення DropDown ComboBox активних елментів з підтримкою Autocompletion, які біндяться напряму до фідів KVS. Набираючи в пошуковому полі вводу текстову послідовність, кожен натиск клавіші генерує запит на сервер, який занурюється разом з пошуковою лямбдою в KVS стрім. Знайдені записи автоматично доставляються на клієнт та відображаюится в DropDown області.
SPEC
Ця функціональність реалізується за допомогою простого протоколу comboKeyup та comboSelect зі сторони клієнта. Зі сторони сервера використовується API Element:proto/1.
-record(comboKeyup, { value=[], dom=[], feed=[], delegate=[]}).
-record(comboSelect, { value=[], dom=[], feed=[], delegate=[] }).
-record(comboLookup, { ?ELEMENT_BASE(element_comboLookup),
value=[],
disabled=false,
feed=[],
reader=[],
chunk=20 }).
Ця функція повинна містити код який вміє рендерити рекорди в серверному фіді та відправляти їх на клієнт за допомогою NITRO API nitro:insert_top. Модуль, який містить цю функцію, можна вказувати на рівні протоколу контрольного елементу.
proto(#comboKeyup{delegate=Module}=Msg) -> Module:proto(Msg);
proto(#comboSelect{delegate=Module}=Msg) -> Module:proto(Msg).
Приклад використання
Покажемо на прикладі як використовувати контрольний елемент comboLookup бібліотеки NITRO разом з більш високорівневою бібліотекою FORM, яка призначеня для більш зручної маніпуляції формами як сукупністю полей зі своїми правилами валідації.
Для початку ми повинні використати цей контрольний елемент:
#comboLookup{ id="customLookup"
feed= <<"/acc/synrc">>,
delegate = 'Elixir.CRM.Forms.Person',
reader=[],
chunk=20 };
Модуль делегата повинен містити функцію proto/1, яка забезачує звертання до бази даних, рендер знайдених елементів, та відсилка їх на клієнт по WebSocket. Крім цього цей модуль містить в нашому прикладі одразу функцію рендеру знайденого елементу на стороні сервера в NITRO елмент. У нашому прикладі це один img та два p теги (Ім'я та Номер).
defmodule CRM.Forms.Person do
use N2O, with: [:n2o]
use FORM, with: [:form]
require NITRO
require ERP
require Logger
require Record
def doc(), do: "Персона: "
def id(), do: ERP."Person"()
def new(name, doc_obj, options) do
dom = :proplists.get_value(:dom, options)
commonName = :erlang.element(3, doc_obj)
id = :erlang.element(2, doc_obj)
NITRO.panel(
id: :erlang.element(2, doc_obj),
class: [:"dropdown-item"],
onclick:
:nitro.jse(
:nitro.to_list(
"comboSelect('" <>
:nitro.to_binary(dom) <>
"','" <> :nitro.to_binary(commonName) <> "')"
)
),
body: [
NITRO.image(
style: 'display: float;',
height: '30px',
src: 'https://n2o.dev/img/synrc.svg'
),
NITRO.p(
id: FORM.atom([:name, dom]),
body:
:lists.concat([
:nitro.to_list(:nitro.jse("Ім'я: ")),
:nitro.to_list(commonName)
])
),
NITRO.p(
id: FORM.atom([:id, dom]),
body: :lists.concat([:nitro.to_list(:nitro.jse("Номер: ")), id])
)
]
)
end
def proto(NITRO.comboKeyup(value: name0, dom: field0, feed: feed) = msg) do
IO.inspect(msg)
name = :nitro.to_list(name0)
field = :nitro.to_list(field0)
:nitro.clear(FORM.atom([:comboContainer, field]))
all = :kvs.all(feed)
filtered =
:lists.filter(
fn ERP."Employee"(person: per) ->
n = :unicode.characters_to_binary(ERP."Person"(per, :cn))
:string.rstr(:string.lowercase(:erlang.binary_to_list(n)),
:string.lowercase(name)) > 0
end,
all
)
case filtered do
[] -> :nitro.display(FORM.atom([:comboContainer, field]), :none)
_ -> :nitro.display(FORM.atom([:comboContainer, field]), :block)
end
:lists.map(
fn ERP."Employee"(person: per) ->
:nitro.insert_top(
FORM.atom([:comboContainer, field]),
:nitro.render(new([], per, dom: field))
)
end,
filtered
)
end
end
У випадку, коли адреси пошукових фідів змінюються протягом життєвого циклу контрольного елементу, необхідно оновлювати повний рендер контрольного елементу:
def branch_update(x) do
:nitro.update(
:cn_credentials_none,
NITRO.comboLookup(id: :cn_credentials_none, feed: x, delegate: CRM.Forms.Person)
)
end
