Skip to content

Commit 45f6be4

Browse files
committed
Render the visibility toggle as a plain link
The toggle carried an onclick that called toggleVisibility, which fetched the url and swapped the icon by hand. Only the clicked icon though - rows inheriting the value, as variants inherit "published" from their base record, kept showing the old state until someone reloaded. Reproducing the inheritance rules in the browser was never realistic. The link now carries nothing but the scroll offset action. The server flips the value and the list is rendered anew, so inherited states are right without anyone tracking them client side. The handler redirects to the list afterwards, which the toggle action does not render itself - of no consequence while the answer was thrown away by an ajax call, but the browser shows it now, and it keeps the url idempotent. Two things surfaced by doing it: The inline script that wires the drag and drop sorting declared "const table" globally. It sits inside a loop, so it could already collide on a page with several groups, and Turbo re-executes the scripts of a swapped body - the toggle threw "Identifier 'table' has already been declared" on every click. Scoped in a function expression now. Three docblocks had piled up above buildToggleAttributes, two of them stranded from an earlier extraction: buildCommand and buildDeepLinkTarget stood without one while their text sat elsewhere. Handed back. Verified: the variant case that started this is fixed - toggling the base record updates all three inheriting rows without a reload, other groups stay untouched, three runs in a row. Toggle 6/6 with no javascript errors where there were two, drag and drop still persists, regression 19/19, display conditions 5/5, legends 10/10, tree 5/5, back url 3/3, edit all 4/4, picker 6/6. phpcq unchanged.
1 parent 36bfc88 commit 45f6be4

3 files changed

Lines changed: 49 additions & 41 deletions

File tree

src/Contao/View/Contao2BackendView/ActionHandler/ToggleHandler.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use ContaoCommunityAlliance\DcGeneral\Action;
2727
use ContaoCommunityAlliance\DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface;
2828
use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator;
29+
use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers;
2930
use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait;
3031
use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface;
3132
use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface;
@@ -105,7 +106,7 @@ public function handleEvent(ActionEvent $event)
105106
* @param ToggleCommandInterface $operation The operation.
106107
* @param ModelIdInterface|null $modelId The model id.
107108
*
108-
* @return void
109+
* @return never This redirects to the list once the new state is stored.
109110
*
110111
* @SuppressWarnings(PHPMD.ExitExpression)
111112
* @SuppressWarnings(PHPMD.Superglobals)
@@ -167,6 +168,12 @@ protected function process(
167168
/** @var MultiLanguageDataProviderInterface $dataProvider */
168169
$dataProvider->setCurrentLanguage((string) $language);
169170
}
171+
172+
// Back to the list, which the action itself does not render. That was of no consequence while
173+
// the toggle was an ajax call whose answer got thrown away, but it is a plain link now: the
174+
// browser shows what comes back. Redirecting also keeps the url idempotent - reloading the
175+
// list does not toggle a second time.
176+
ViewHelpers::redirectHome($environment);
170177
}
171178

172179
/**

src/Contao/View/Contao2BackendView/ButtonRenderer.php

Lines changed: 32 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,8 @@
2424

2525
namespace ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView;
2626

27-
use Contao\Controller;
2827
use Contao\Image;
2928
use Contao\StringUtil;
30-
use Contao\System;
3129
use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents;
3230
use ContaoCommunityAlliance\Contao\Bindings\Events\Backend\AddToUrlEvent;
3331
use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent;
@@ -313,47 +311,19 @@ private function hasPasteNewButton(): bool
313311
&& ($basicDefinition->isEditable() && $basicDefinition->isCreatable()));
314312
}
315313

316-
/**
317-
* Render a command button.
318-
*
319-
* @param CommandInterface $command The command to render the button for.
320-
* @param ModelInterface $model The model to which the command shall get applied.
321-
* @param ModelInterface|null $previous The previous model in the collection.
322-
* @param ModelInterface|null $next The next model in the collection.
323-
* @param bool $isCircularReference Determinator if there exists a circular reference between the
324-
* model and the model(s) contained in the clipboard.
325-
* @param string[] $childIds The ids of all child models.
326-
*
327-
* @return string
328-
*
329-
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
330-
*/
331-
/**
332-
* Build the target attribute of the "contao--deeplink" controller for a command.
333-
*
334-
* The controller opens a record on ctrl click or on a double tap. Contao derives its targets from a
335-
* legacy "click2edit" class otherwise and warns about the deprecated helper behind it.
336-
*
337-
* @param CommandInterface $command The command to render.
338-
*
339-
* @return string The attribute including a leading space, or an empty string.
340-
*/
341314
/**
342315
* Build the attributes that turn a command into a visibility toggle.
343316
*
344-
* @param array<array-key, mixed> $extra The extra information of the command.
345-
* @param string $icon The icon of the active state.
317+
* A plain link, the way Contao renders its own toggle operation. The server flips the stored
318+
* value and the list is rendered anew, so no script has to keep an icon in sync - and values
319+
* inherited by other rows, as variants inherit "published", are correct without anyone
320+
* reproducing the inheritance rules in the browser.
346321
*
347322
* @return string
348323
*/
349-
private function buildToggleAttributes(array $extra, string $icon): string
324+
private function buildToggleAttributes(): string
350325
{
351-
return sprintf(
352-
' data-action="contao--scroll-offset#store"'
353-
. ' onclick="return BackendGeneral.toggleVisibility(this, \'%s\', \'%s\');"',
354-
Controller::addStaticUrlTo(System::urlEncode($icon)),
355-
Controller::addStaticUrlTo(System::urlEncode((string) ($extra['icon_disabled'] ?? 'invisible.svg')))
356-
);
326+
return ' data-action="contao--scroll-offset#store"';
357327
}
358328

359329
/**
@@ -379,13 +349,38 @@ private function getToggleIcon(
379349
return (string) ($extra['icon_disabled'] ?? 'invisible.svg');
380350
}
381351

352+
/**
353+
* Build the target attribute of the "contao--deeplink" controller for a command.
354+
*
355+
* The controller opens a record on ctrl click or on a double tap. Contao derives its targets from a
356+
* legacy "click2edit" class otherwise and warns about the deprecated helper behind it.
357+
*
358+
* @param CommandInterface $command The command to render.
359+
*
360+
* @return string The attribute including a leading space, or an empty string.
361+
*/
382362
private function buildDeepLinkTarget(CommandInterface $command): string
383363
{
384364
$target = ['edit' => 'primary', 'children' => 'secondary'][$command->getName()] ?? null;
385365

386366
return null === $target ? '' : ' data-contao--deeplink-target="' . $target . '"';
387367
}
388368

369+
/**
370+
* Render a command button.
371+
*
372+
* @param CommandInterface $command The command to render the button for.
373+
* @param ModelInterface $model The model to which the command shall get applied.
374+
* @param ModelInterface|null $previous The previous model in the collection.
375+
* @param ModelInterface|null $next The next model in the collection.
376+
* @param bool $isCircularReference Determinator if there exists a circular reference between the
377+
* model and the model(s) contained in the clipboard.
378+
* @param string[] $childIds The ids of all child models.
379+
*
380+
* @return string
381+
*
382+
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
383+
*/
389384
private function buildCommand(
390385
CommandInterface $command,
391386
ModelInterface $model,
@@ -407,7 +402,7 @@ private function buildCommand(
407402
$attributes .= $this->buildDeepLinkTarget($command);
408403

409404
if ($command instanceof ToggleCommandInterface) {
410-
$attributes .= $this->buildToggleAttributes($extra, $icon);
405+
$attributes .= $this->buildToggleAttributes();
411406
$icon = $this->getToggleIcon($command, $model, $extra, $icon);
412407
}
413408

src/Resources/contao/templates/dcbe_general_common_list.html5

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,15 @@ assert($translator instanceof TranslatorInterface);
129129
</tbody>
130130
<?php if ($this->sortable) : ?>
131131
<script>
132-
const table = document.getElementById('table_<?= $this->tableName ?>');
133-
const SortableTableDnD = new GeneralTableDnD();
134-
SortableTableDnD.init(table);
132+
/*
133+
* Scoped on purpose. This block sits inside a loop, so it can appear more than
134+
* once per page, and Turbo re-executes the scripts of a swapped body - both make
135+
* a global "const table" throw "Identifier has already been declared".
136+
*/
137+
(function () {
138+
const table = document.getElementById('table_<?= $this->tableName ?>');
139+
new GeneralTableDnD().init(table);
140+
})();
135141
</script>
136142
<?php endif; ?>
137143
<?php endforeach; ?>

0 commit comments

Comments
 (0)