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
        