COMBOLOOKUP

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