Přeskočit obsah
V
Pro vývojáře
Architektura, konvence, jádro systému a bezpečnost
UI vrstva / Picker

Picker#

Výběr vazby místo rozbalovacího seznamu: textové pole s našeptávačem a modální okno s plným výpisem. Kód: app/UI/Admin/System/Components/PickerComponent/.

Kdy ho použít a kdy ne#

Situace Čím to řešit
do ~50 položek, seznam se nemění addSelect()
stovky a víc, nebo je potřeba hledat Picker
výběr víc hodnot naráz addCheckboxList(), nebo Picker v Multiplieru

Rozbalovací seznam s deseti tisíci položkami se nenačte, nedá se v něm hledat a při každém otevření formuláře vytáhne celou tabulku.

Jak je poskládaný#

Část Role
PickerConfig hodnotový objekt — co se vybírá a odkud
picker traita (UserPickerTrait, CategoryPickerTrait, …) postaví PickerConfig vlastním managerem
PickerHostTrait hostitel v presenteru — vyrobí createComponentPicker()
Picker + PickerFactory samotná komponenta
picker.js našeptávač, modál a doplnění polí

Obsah modálu je DataGrid cílového presenteru v režimu výběru — žádná druhá implementace výpisu neexistuje.

Picker nezná manager

Data si bere přes callbacky v PickerConfig. Proto funguje v administraci i na frontu a proto se dá tentýž picker použít nad různými zdroji.

Postup: nahrazuji select pickerem#

  1. Napiš picker traitu do Presenters/Traits/Pickers/ — dvě metody: našeptávání a překlad id na popisek.
protected function userPicker(string $formName, string $targetField): PickerConfig
{
    return new PickerConfig(
        suggestCallback: fn(string $q): array => $this->suggestUsers($q),
        formName: $formName,
        targetField: $targetField,
        labelResolver: fn(int $id): ?string => $this->resolveUserLabel($id),
        targetPresenter: ':Admin:System:User',
    );
}
  1. Našeptávač vrací id + label, volitelně data pro doplnění dalších polí. Vždy s limit, jinak se našeptávač zadusí.
  2. labelResolver je povinný všude, kde se edituje — bez něj se při načtení editace nemá co zobrazit.
  3. Presenter use-ne PickerHostTrait a v pickers() vrátí pole konfigurací klíčované logickým názvem.
  4. Ve formuláři nahraď select skrytým polem téhož jména, jaké je v targetField.
  5. V šabloně vykresli {control picker-parentId} a popisek pole si napiš ručně — skryté pole žádný nemá.
  6. V cílovém presenteru zapni režim výběru v továrně gridu (viz Admin presenter).
  7. Prokliknij: našeptávání, výběr z modálu, uložení, znovuotevření editace.

pickers() musí být čisté a idempotentní

Volá se i při AJAX signálu našeptávače, kdy formulář ještě nemusí být postavený. Cokoli, co v té metodě sáhne na $form, spadne — a spadne to jen při psaní do našeptávače, ne při načtení stránky.

Popisek musí být naplněný při načtení editace

Když labelResolver chybí nebo vrátí null, pole vypadá prázdné, přestože vazba existuje — a protože prázdné pole je platná hodnota, uložení tu vazbu zruší. Uživatel přitom jen otevřel formulář a klikl na Uložit.

Na editaci se předvyplňuje přes setDefaults()

Ne přímým nastavením hodnoty prvku — viz Formuláře.

Doplnění dalších polí naráz#

fieldMap mapuje datový klíč z našeptávače na název dalšího pole formuláře:

fieldMap: ['email' => 'ownerEmail', 'company' => 'ownerCompany'],

Našeptávač k položce vrátí data: ['email' => '…'] a JavaScript po výběru doplní i tato pole. Z modálu tatáž data dodá setPickerData() na gridu.

Picker v Multiplieru#

Parametr Kdy je potřeba
targetContainer pole leží uvnitř multiplier kontejneru (addresses)
nestedContainer multiplier uvnitř multiplieru (skupiny → parametry)
displayField textový control pro popisek, sourozenec skrytého pole

Instance se pak jmenuje picker-<klíč>_<index> (u vnořeného picker-<klíč>_<i0>_<i1>). Oddělovač je podtržítko, ne pomlčka — pomlčka je v Nette separátor cesty komponent.

Bez displayField popisek nepřežije AJAX překreslení řádku

Bez něj picker vyrenderuje obyčejný neodesílaný input, který se při překreslení vrátí prázdný. Uživatel vidí, že se mu vybraná hodnota „ztratila“ — přitom skryté pole hodnotu pořád má.

Klíč konfigurace nesmí končit _<číslo>

Jméno instance se rozkládá regulárním výrazem na klíč a indexy řádků. Klíč address_2 by se rozpadl na address + index 2.

Sebe-reference (nadřazená kategorie, rodičovská stránka)#

Picker na tutéž entitu musí vyloučit sám sebe i svůj podstrom — na to jsou persistentní parametry pickerExclude a dvojice pickerExcludeSubtreeLft/Rgt (Admin presenter).

Cyklus ve stromu se projeví až při přepočtu

Kategorie nastavená jako vlastní rodič se uloží bez chyby. Rozbije až přepočet lft/rgt celého stromu — a to se stane někdy později, při úplně jiné operaci.

Překreslení závislých polí po výběru#

submitButton říká, které submit tlačítko má picker po úspěšném výběru „kliknout“, aby se dorenderovala pole závislá na vybrané hodnotě (třeba volby vybraného parametru).

To tlačítko musí mít prázdný validační rozsah

setValidationScope([]) + onClick → redrawControl. Jinak by překreslení blokovala validace polí, které uživatel ještě nevyplnil. Ukládat nebude — admin base ukládá jen přes save/update (Admin presenter).

Bez tlačítka jde na výběr navázat vlastní chování

picker.js vždycky vystřelí DOM událost picker:success; preventDefault() na ní potlačí automatické překreslení.

Zakládání nové položky z pickeru#

Picker umí založit novou položku bez opuštění formuláře.

Nově založená položka se musí vrátit i s popiskem

Jinak se do pole propíše jen id a uživatel vidí prázdno — přestože se záznam opravdu založil.

Skryté pole a formulářový kontext#

Picker uvnitř komponenty s vlastním kontextem umí vyrobit DRUHÉ skryté pole téhož jména

Odešle se to špatné a vazba se uloží prázdná. Řeší se to n:snippetArea kolem překreslované části, ne {formContext}.

Kam sáhnout#

Chci Kde
všechny volby konfigurace app/UI/Admin/System/Components/PickerComponent/PickerConfig.php
hostitele v presenteru app/UI/Admin/System/Presenters/Traits/Pickers/PickerHostTrait.php
hostitele v komponentě …/PickerComponent/ComponentPickerHostTrait.php
kanonický vzor traity app/UI/Admin/System/Presenters/Traits/Pickers/UserPickerTrait.php
režim výběru v gridu app/UI/Admin/System/Components/DataGridComponent/DataGrid.php

Navazující kapitoly: Práce s tabulkami · Admin presenter · Formuláře