Topwire Features
Besides providing a seamles Hotwire Turbo integration, Topwire comes with additional concepts and an APIs, that gets you solve common tasks more quickly and elegantly. Have fun exploring the features.
Starting with Turbo Frames
Small effort, great result
Tick the Topwire checkbox of the content element, to wrap a TYPO3 form into a Turbo-Frame. With that, the form does not reload the whole page any more and your users stay on the page right where they left off.
This works with any third party plugin and without changing a single line of code, adding JavaScript or overriding Fluid templates.
Check out the message form example!
Magic checkbox
This is a standard form with the TYPO3 form extension. No templates have been adjusted, only a checkbox is checked in the form content element.
All TypoScript it takes:
Connecting the checkbox in the content element with Topwire Frame functionality, requires a single line of TypoScript.
The second line is optional. When no custom frame id is specified, an id is automatically generated from the uid of the element.
# Wrap content elements with a Turbo frame, when checkbox is checked in the content element
tt_content.stdWrap.turboFrameWrap.field = tx_turbo_wrap_in_frame
# Read the frame id from a text field in the content element
tt_content.stdWrap.turboFrameWrap.frameId.field = tx_turbo_frame_id
Adding Turbo Frames to your Fluid templates
Render any plugin in any Fluid template
Topwire's context view helpers can be used to render plugins where you need them, without any additional configuration. Use the view helpers, specify the plugin and optionally even an action that should be rendered or the Fluid template section that should be rendered.
<html
xmlns:topwire="http://typo3.org/ns/Topwire/ViewHelpers"
data-namespace-typo3-fluid="true">
<topwire:context.plugin
extensionName="MyExtension"
pluginName="MyPlugin"
action="list"
section="MySection"
>
<topwire:context.render />
</topwire:context.plugin>
</html>
Add a Turbo Frame to third party plugins
Third party plugins can be rendered and scoped within a frame without touching any code or template.
<html
xmlns:topwire="http://typo3.org/ns/Topwire/ViewHelpers"
data-namespace-typo3-fluid="true">
<topwire:context.plugin extensionName="FeLogin" pluginName="Login">
<topwire:turbo.frame id="login" wrapResponse="true">
<topwire:context.render />
</topwire:turbo.frame>
</topwire:context.plugin>
</html>
Any content element can be rendered in a frame
Content elements that are used anywhere in the TYPO3 installation, can be rendered in any Fluid template and be scoped in frame.
<html
xmlns:topwire="http://typo3.org/ns/Topwire/ViewHelpers"
data-namespace-typo3-fluid="true">
<topwire:context.contentElement uid="44">
<topwire:turbo.frame id="form-content" wrapResponse="true">
<topwire:context.render />
</topwire:turbo.frame>
</topwire:context.contentElement>
</html>
Every content that is rendered in a Turbo frame can be loaded asynchronously
Only the loading placeholder will be part of the fully rendered page and cached. The contents of the frame will be loaded asynchrounously by Hotwire Turbo. Very useful for content that must be always up to date and therefore can not be cached.
<html
xmlns:topwire="http://typo3.org/ns/Topwire/ViewHelpers"
data-namespace-typo3-fluid="true">
<topwire:context.plugin extensionName="FeLogin" pluginName="Login">
<topwire:turbo.frame id="login2" src="async" wrapResponse="true">
<div class="d-flex justify-content-center m-5">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
</topwire:turbo.frame>
</topwire:context.plugin>
</html>
Using Turbo frames in custom plugins
When developing your own plugins and Fluid templates, Turbo frames can be used to devide parts of the plugin into individual sections wrapped in frames.
<html
xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:topwire="http://typo3.org/ns/Topwire/ViewHelpers"
data-namespace-typo3-fluid="true">
<topwire:context.plugin>
<topwire:turbo.frame id="counter" morph="true">
<f:render partial="Counter/Plain" arguments="{_all}"/>
</topwire:turbo.frame>
</topwire:context.plugin>
</html>
Flexible and expressive Fluid view helpers
Rendering TypoScript snippets
You can use also use Topwire's context view helpers to render any defined TypoScript path and the rendered result will directly be embedded in the page. If needed, wrap the result with a Turbo frame view helper, declare it to be async and the result will be fetched on page load instead. That is very handy e.g. if the rendered content can not be cached with the rest of the page.
<html
xmlns:topwire="http://typo3.org/ns/Topwire/ViewHelpers"
data-namespace-typo3-fluid="true">
<topwire:context.typoScript typoScriptPath="lib.sideBar">
<!-- Directly embed the result of lib.sideBar -->
<topwire:context.render />
</topwire:context.typoScript>
<topwire:context.typoScript typoScriptPath="lib.sideBar">
<!-- Load the result of lib.sideBar after the full page is loaded -->
<!-- The HTML here acts as temporary content (loading spinner) -->
<f:turbo.frame src="async">
Loading ...
</f:turbo.frame>
</topwire:context.typoScript>
</html>
Link up your Extbase actions
Extbase standalone responses
Your Extbase controller code can simply return the desired response. In the Fluid template that is linking the action, you can easily declare that the linked action should not be embedded in a HTML page, done.
No more extra TypoScript page type delcaration, no additional middleware code, everything is handled transparently by Topwire.
<?php
declare(strict_types=1);
namespace Topwire\Examples\Controller;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class ExampleController extends ActionController
{
protected function jsonAction(): ResponseInterface
{
$data = [
'hello' => 'world',
];
return $this->jsonResponse(json_encode($data, JSON_THROW_ON_ERROR))
->withHeader(
'Content-Disposition',
'attachment; filename="hello.json"'
);
}
}
<html
xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:topwire="http://typo3.org/ns/Topwire/ViewHelpers"
data-namespace-typo3-fluid="true">
<topwire:context.plugin pluginName="Json">
<h3>Link to a download action in your plugin</h3>
<f:link.action
action="json"
class="btn btn-primary"
additionalParams="{topwire: {type: 'context'}}"
>
Download JSON
</f:link.action>
</topwire:context.plugin>
</html>
Link to a download action in your plugin
Download JSONLink anything from anywhere
TypoScript and PHP linking API
Sometimes it is necessary to link from Extbase actions from within PHP code, or from TypoScript.
Topwire got you covered! Declare what plugin action you want to link in additional arguments, and Topwire will transform it to an URI that only renders the contents of this action, without the surrounding HTML of the page.
lib.topwireLinkExample = TEXT
lib.topwireLinkExample.typolink.parameter.data = page:uid
lib.topwireLinkExample.typolink.topwire.type = plugin
lib.topwireLinkExample.typolink.topwire.extensionName = TopwireExamples
lib.topwireLinkExample.typolink.topwire.pluginName = Json
<?php
declare(strict_types=1);
namespace Topwire\Examples\Controller;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Core\Http\RedirectResponse;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class ExampleController extends ActionController
{
protected function exampleAction(): ResponseInterface
{
$uri = $this->uriBuilder
->reset()
->setArguments([
'topwire' => [
'type' => 'context',
]
])
->uriFor('json');
return new RedirectResponse($uri, 303);
}
}