Dynamická změna stromové struktury vykreslené do tabulky

Polki
Člen | 553
+
0
-

Zdravím,
asi jsem blázen, ale dostal jsem úkol a zkouším jej vyřešit v Nette. Zadání je jednoduché. Vytvořit web aplikaci, která bude vykreslovat strom (viz. obrázek na konci tohoto příspěvku). P: označuje id rodiče a ID: označuje identifikátor prvku. Podle zadání má být možné přidat prvek jako potomka libovolného z existujících prvků a nebo libovolný existující prvek odstranit, přičemž se potomci odstraňovaného prvku stanou potomky jeho rodiče. Dále mají na každém řádku shlukovány prvky podle společného rodiče.

Posem celkem v klidu a pohodě.

Problém nastává u druhé části.

Přidání a odstranění prvků mám řešit pomocí AJAXu. Jde o to, že daný strom může mít prý miliony prvků a tedy nechtějí po každém přidání či odebrání překreslit celý strom, ale pouze změněnou část. (Tj. Dynamicky přidat prvek, nebo celý podstrom smazaného prvku dynamicky přesunout o řádek výše a vrchním potomkům změnit rodiče a odstraňovaný prvek smazat.) Zbytek stránky musí zůstat nedotčen.

Využívám nette.ajax.js a revalidaci snippetů. Úpravu jednoho snippetu ok, přidání nového řádku na konec ok, přidání obalu pro prvky se stejným rodičem ok, přidání prvku do obalu rodiče ok.

Problém tedy je, když začnu věci z posledního odstavce napsaného před tímto kombinovat. Podle toho, který prvek je přidán musím buď přidat řádek, nebo editovat stávající, přidat obal prvků se stejným rodičem, nebo editovat stávající a nakonec přidat. Jinými slovy mohou nastat tyto případy:
Případ 1: Potřebuji přidání potomka pro node s ID:1 pouze do řádku 1 (řádky jsou indexované od 0), kontejneru pro potomky node s ID:1 přidat jeden záznam(nový node).
Případ 2: Potřebuji přidání potomka pro node s ID:8 potřebuji do řádku 2 přidat kontejner(div) pro potomky node s ID:8 a do něj nový node.
Případ 3: Potřebuji přidání potomka pro ID:6 je potřeba přidat řádek 3, do něj kontejner pro uchování potomků od node s ID:6 a do něj nový prvek.

Asi si dokážete představit, co teprve bude u mazání. Každopádně pokud vyřeším toto, snad by mi potom šlo i mazání lehčeji. Jde tedy o to, že někdy musím něco přidat a jindy to jen editovat. Jde něco takového v Nette udělat?

default.latte

<div id="contentTable">
	<table n:snippet='table'>
    	<thead>
        	<tr>
            	<th id="firstCol">
                	Depth
                </th>
                <th>
                    Tree Nodes
                </th>
            </tr>
		</thead>
		<tbody n:snippet='tbody'>
			<tr n:foreach='$tree as $rowId => $row' n:snippet='row$rowId'>
        		<td>
            		{$rowId}
            	</td>
            	<td class='data'>
            		<span n:foreach='$row as $parentId => $childs' n:if='count($childs) > 0' n:snippet='parent$parentId'>
                		<span n:foreach='$childs as $childId => $child' n:snippet='parent{$parentId}_child$childId' onClick='setElement({$child->id}, {$rowId})' class='clickable'>
                    		[P: {($child->parent == NULL) ? 'NONE' : $child->parent} ID: {$child->id}]
                    	</span>
                	</span>
				</td>
			</tr>
		</tbody>
	</table>
</div>

Událost nalinkovaná na formulář, pomocí kterého odesílám id prvku, kterému se má přiřadit potomek a řádek(hloubku), na kterém je rodič (potomek bude o 1 níže).

// volá se po úspěšném odeslání formuláře
public function addNodeFormSucceeded(SubmitButton $button) {
	$values = $button->getForm()->getValues();												// získá z formu data
	$row = $this->nodeManager->add($values->id);											// vloží do db prvek s rodičem $values->id
	$newNode = new Node($row[NodeManager::COLUMN_ID], $row[NodeManager::COLUMN_PARENT]);	// vytvoří node na základě dat z databáze
	$this->tree->addNode($newNode);															// přidá node do existujícího stromu (pro nás nepodstatné)

	if($this->isAjax()) {
		// vytvoří nové pole obsahující pouze nově přidaný prvek. U mazání to bude celý podstrom, který je nutno překreslit.
		$tmp = new ArrayHash();
		$tmp[$values->row + 1] = new ArrayHash();
		$tmp[$values->row + 1][$row[NodeManager::COLUMN_PARENT]] = new ArrayHash();
		$tmp[$values->row + 1][$row[NodeManager::COLUMN_PARENT]][$row[NodeManager::COLUMN_ID]] = $newNode;

		// Vloží pole do proměnné, ze které se poté vykresluje strom v latte
		$this->updateTree = $tmp;

		// tohle je třeba pozměnit podle vašeho doporučení
		$this->redrawControl('table');
		$this->redrawControl('row' . $values->row + 1);
		$this->redrawControl('parent' . $row[NodeManager::COLUMN_PARENT]);
		$this->redrawControl('parent' . $row[NodeManager::COLUMN_PARENT] . '_child' . $row[NodeManager::COLUMN_ID]);
	}
}

Obsah render metody

public function renderDefault() {
	$this->template->tree = $this->updateTree;
}

Obrázek vzhledu stromu zde zde:
https://ibb.co/cKGO9d

Demo aplikace:
https://apps.bata.cz/…er_demo.html

Editoval Polki (19. 6. 2018 11:58)