Volání callbacku komponenty ze šablony
- Fires
- Člen | 97
Zdravím, chci poprosit o radu.
Problém: Presenter zobrazuje nějaká data, v rámci toho vytváři komponentu
ItemTable, ta zobrazí tabulku s daty. Potřebuji do komponenty nějak předat
url(“generator”) na které se má přesměrovat v případě
kliku/dvojkliku. Jedná se vetšinou o jinou action u stejného presenteru
která získá itemId jako parametr url, nastaví jej do druhé komponenty
ItemModal pro popup modalu a pomocí $this->forward(“default”) vykresí
zase default šablonu. Chtěl jsem nějak oddělit logiku komponenty a
presenteru a udělat to nějak modulární. Aby komponenta nemusela mít nic
napevno a presenter ji nastavil parametry, resp ji předal něco do čeho si
vloží id a vypadne ji URL které má nastavit.
Vytvoření komponenty v presenteru:
public function createComponentItemTable(){
$itemTable = $this->itemTableFactory->create();
$itemTable->onDoubleClickUrlRequire[] = function($itemId){
$link = $this->link('item',$itemId);
return $link;
};
return $itemTable;
}
V šabloně komponenty jsem pak volal
data-handle-doubleclick-url="{$control->onDoubleClickUrlRequire($item->id)}"
pokud si vypíšu link v presenteru je validní url, ale v komponentě vždy vráti null a netuším proč :) AI v tom má ještě větší chaos jak já.
Asi to není ani nejlepší způsob jak tohle řešit ale nic lepší mě nenapadlo. Pokud máte lepší řešení beru. Napadl mě ještě opačný postup do komponenty nastavit cestu/target k action “Presenter:action” a komponenta by si pak volala $link->($target, $itemId);.
Snad to dává alespoň trochu smysl. Předem díky všem za komenty.
- nightfish
- Člen | 516
@Fires IMHO je problém v
$itemTable->onDoubleClickUrlRequire[]
– prakticky to říká,
že přidáváš handler “Nette eventu”, zatímco chceš spíš volat
metodu.
Osobně bych to řešil předáním funkce, která má na starosti vygenerovat odkaz, z presenteru do komponenty, a následně jejím použitím jako Latte funkce.
Pro PHP 8.1+ by to mohlo vypadat +/- takto:
// Presenter
public function createComponentItemTable() {
return $this->itemTableFactory->create(static fn(int $itemId): string => $this->link('item', $itemId));
}
// ItemTableFactory
interface ItemTableFactory {
public function create(\Closure $linkCallback): ItemTable;
}
// komponenta ItemTable
class ItemTable extends AbstractControl {
public function __construct(
private \Closure $linkCallback,
) {
}
public function render(): void {
$this->getTemplate()->addFunction('link', ($this->linkCallback)(...));
}
}
ItemTable.latte
<div data-handle-doubleclick-url="{link($item->id)}">...</div>
- Fires
- Člen | 97
nightfish napsal(a):
@Fires IMHO je problém v
$itemTable->onDoubleClickUrlRequire[]
– prakticky to říká, že přidáváš handler „Nette eventu“, zatímco chceš spíš volat metodu.Osobně bych to řešil předáním funkce, která má na starosti vygenerovat odkaz, z presenteru do komponenty, a následně jejím použitím jako Latte funkce.
Pro PHP 8.1+ by to mohlo vypadat +/- takto:
// Presenter public function createComponentItemTable() { return $this->itemTableFactory->create(static fn(int $itemId): string => $this->link('item', $itemId)); } // ItemTableFactory interface ItemTableFactory { public function create(\Closure $linkCallback): ItemTable; } // komponenta ItemTable class ItemTable extends AbstractControl { public function __construct( private \Closure $linkCallback, ) { } public function render(): void { $this->getTemplate()->addFunction('link', ($this->linkCallback)(...)); } }
ItemTable.latte
<div data-handle-doubleclick-url="{link($item->id)}">...</div>
Díky moc..
- m.brecher
- Generous Backer | 863
@Fires
Asi řešíš něco podobného jako já. Já používám v tabulce v šabloně akce presenteru javascriptový odkaz na tagu <tr> tak, aby byl klikací celý řádek tabulky takto:
javascript TableLink.js – soubor vložím do šablony latte:
class TableLink
{
#ajaxAttribute = 'data-ajax'
#linkAttribute = 'data-link'
constructor()
{
document.addEventListener('click', (event) => this.#linkClick(event))
}
#linkClick(event)
{
let elem = event.target
while(elem && elem.nodeName !== 'BODY' && elem.nodeName !== 'A'){
if(elem.hasAttribute(this.#linkAttribute)){
if(!elem.hasAttribute(this.#ajaxAttribute)){
location.href = elem.dataset.link
}
break;
}
elem = elem.parentNode
}
}
}
new TableLink()
default.latte
{foreach $clients as $id => $client}
{var $href = href(':view', $id)}
<tr data-link="{$href}" class="body">
<td><a href="{$href}" class="text-link">{$clientModel->getClientTitle($client)}</a></td>
</tr>
{/foreach}
url je v proměnné $href, která se použije dvakrát – v tagu <tr> a v tagu <a>, $href se získá pomocí latte funkce href(), kterou definuji zde:
class Filters
{
public static function href(Presenter $presenter, string $destination, int|array|null $args = null): string
{
$args = match(true){
is_int($args) => ['id' => $args],
is_array($args) => $args,
default => [],
};
return $presenter->link($destination, $args);
}
}
Funkci filtru zaregistruji traitou:
trait FiltersTrait
{
public function injectFilter(): void
{
$this->onRender[] = function(){
// .....
$template->addFunction('href', fn(string $destination, int|array|null $args = null) => Filters::href($this, $destination, $args));
};
}
}
traitu vložím do BasePresenteru
abstract class BasePresenter extends UI\Presenter
{
use FiltersTrait;
}
Poznámka:
a) Pokud odkaz posílá ajaxový request, který indikuji atributem data-ajax (rozdíl oproti Naja, která vyžaduje označení linku class=„ajax“) tak javascript chování modifikuje.
b) Nemám FiltersTrait vyzkoušenou v UI\Control, pro použití jak v presenteru, tak v UI\Control asi bude potřeba v traitě injektnout LinkGenerator, který má také metodu link() do traity a použít místo $this (presenter), nějak takto:
trait FiltersTrait
{
public function injectFilter(Nette\Application\LinkGenerator $linkGenerator): void
{
$this->onRender[] = function() use($linkGenerator){ // edited
// .....
$template->addFunction('href', fn(string $destination, int|array|null $args = null) => Filters::href($linkGenerator, $destination, $args));
};
}
}
- upravit typehint v metodě href(LinkGenerator $linkGenerator, .....)
V podstatě jsem problém vyřešil v latte, filtru latte a javascriptu, komponenta samotná o ničem neví – což je ideální.
Editoval m.brecher (4. 9. 16:18)
- Fires
- Člen | 97
m.brecher napsal(a):
@Fires
Asi řešíš něco podobného jako já. Já používám v tabulce v šabloně akce presenteru javascriptový odkaz na tagu <tr> tak, aby byl klikací celý řádek tabulky takto:
javascript TableLink.js – soubor vložím do šablony latte:
class TableLink { #ajaxAttribute = 'data-ajax' #linkAttribute = 'data-link' constructor() { document.addEventListener('click', (event) => this.#linkClick(event)) } #linkClick(event) { let elem = event.target while(elem && elem.nodeName !== 'BODY' && elem.nodeName !== 'A'){ if(elem.hasAttribute(this.#linkAttribute)){ if(!elem.hasAttribute(this.#ajaxAttribute)){ location.href = elem.dataset.link } break; } elem = elem.parentNode } } } new TableLink()
default.latte
{foreach $clients as $id => $client} {var $href = href(':view', $id)} <tr data-link="{$href}" class="body"> <td><a href="{$href}" class="text-link">{$clientModel->getClientTitle($client)}</a></td> </tr> {/foreach}
url je v proměnné $href, která se použije dvakrát – v tagu <tr> a v tagu <a>, $href se získá pomocí latte funkce href(), kterou definuji zde:
class Filters { public static function href(Presenter $presenter, string $destination, int|array|null $args = null): string { $args = match(true){ is_int($args) => ['id' => $args], is_array($args) => $args, default => [], }; return $presenter->link($destination, $args); } }
Funkci filtru zaregistruji traitou:
trait FiltersTrait { public function injectFilter(): void { $this->onRender[] = function(){ // ..... $template->addFunction('href', fn(string $destination, int|array|null $args = null) => Filters::href($this, $destination, $args)); }; } }
traitu vložím do BasePresenteru
abstract class BasePresenter extends UI\Presenter { use FiltersTrait; }
Poznámka:
a) Pokud odkaz posílá ajaxový request, který indikuji atributem data-ajax (rozdíl oproti Naja, která vyžaduje označení linku class=„ajax“) tak javascript chování modifikuje.
b) Nemám FiltersTrait vyzkoušenou v UI\Control, pro použití jak v presenteru, tak v UI\Control asi bude potřeba v traitě injektnout LinkGenerator, který má také metodu link() do traity a použít místo $this (presenter), nějak takto:
trait FiltersTrait { public function injectFilter(Nette\Application\LinkGenerator $linkGenerator): void { $this->onRender[] = function() use($linkGenerator){ // edited // ..... $template->addFunction('href', fn(string $destination, int|array|null $args = null) => Filters::href($linkGenerator, $destination, $args)); }; } }
- upravit typehint v metodě href(LinkGenerator $linkGenerator, .....)
V podstatě jsem problém vyřešil v latte, filtru latte a javascriptu, komponenta samotná o ničem neví – což je ideální.
Díky, mám ještě hodně co studovat :)