How-To für das Erstellen eines Custom Form Elements für die tx_form-Extension unter Typo3 v8.
Falls man ein Custom Form Element erstellen möchte, haltet euch an das How-To. Für detaillierte Informationen und die Dokumentation zur ext:form besucht: https://docs.typo3.org/typo3cms/extensions/form/Index.html
Außerdem haben wir eine „Anleitung für Dummies und Normalsterbliche, die einfach einen Job zu erledigen haben“ erstellt – eine Kurzfassung mit farblicher Kennzeichnung und einem simplen Online-Generator.
Basics
Als Erstes erstellen wir eine Extension die alle Templates, Konfigurationsdateien, etc. enthält.
Im Beispiel werden wir diese your_custom_extension
nennen.
Beispiel der Ordnerstruktur der Extension:
├── Classes
├── Configuration
│ ├── TypoScript
│ │ ├── constants.txt
│ │ └── setup.txt
│ └── Yaml
│ ├── BaseSetup.yaml
│ └── FormEditorSetup.yaml
├── Documentation
├── Resources
│ ├── Private
│ │ ├── Frontend
│ │ │ └── Partials
│ │ │ └── YourCustomElement.html
│ │ └── Language
│ │ └── Backend.xlf
│ └── Public
│ ├── Icons
│ │ ├── Extension.svg
│ │ └── your_custom_element_icon.svg
│ └── JavaScript
│ └── Backend
│ └── FormEditor
│ └── YourCustomElementViewModel.js
├── Tests
├── composer.json
├── ext_emconf.php
├── ext_localconf.php
└── ext_typoscript_setup.txt
YAML
Da die Konfiguration der Extension in YAML geschrieben wird, wird das ganze wie folgt definiert:
# Configuration/Yaml/BaseSetup.yaml
TYPO3:
CMS:
Form:
prototypes:
standard:
formEditor:
formEditorPartials:
FormElement-YourCustomElement: 'Stage/SimpleTemplate'
formElementsDefinition:
Form:
renderingOptions:
templateRootPaths:
100: 'EXT:your_custom_extension/Resources/Private/Frontend/Templates/'
partialRootPaths:
100: 'EXT: your_custom_extension/Resources/Private/Frontend/Partials/'
layoutRootPaths:
100: 'EXT: your_custom_extension/Resources/Private/Frontend/Layouts/'
YourCustomElement:
__inheritances:
10: 'TYPO3.CMS.Form.mixins.formElementMixins.YourCustomElementMixin'
mixins:
formElementMixins:
YourCustomElementMixin:
__inheritances:
10: 'TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin'
Zu diesem Zeitpunkt geben wir unserem Element ein Template das in der Stage Area des Form-Editors benutzt wird FormElement-YourCustomElement
. Weitere Informationen gibt es hier: https://docs.typo3.org/typo3cms/extensions/form/ApiReference/Index.html#stage-simpletemplate
In den meisten Fällen reicht das Stage/SimpleTemplate
aus.
Bei renderingOptions
definieren wir unsere Pfade zu Templates, Layouts und / oder Partials, die zum Rendern im Frontend benötigt werden.
Registrieren wir nun unser neues Custom Element unterhalb formElementsDefinition
mit dem Namen des Elements. Dieser sollte einzigartig sein, da es von einer anderen Extension ersetzt bzw. überschrieben werden könnte.
In diesem Beispiel erstellen wir einen Custom Mixin für dieses Element, welches von TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin
erbt (mit der Verwendung des Stage/SimpleTemplate
reicht dieses Mixin aus). Natürlich können auch andere Mixins verwendet werden.
Nachdem wir unser Frontend definiert haben, kommen wir nun zur Konfiguration des Form Editors:
# Configuration/Yaml/FormEditorSetup.yaml
TYPO3:
CMS:
Form:
prototypes:
standard:
formEditor:
dynamicRequireJsModules:
additionalViewModelModules:
- 'TYPO3/CMS/YourCustomExtension/Backend/FormEditor/YourCustomElementViewModel'
formEditorPartials:
FormElement-YourCustomElement: 'Stage/SimpleTemplate'
formElementsDefinition:
YourCustomElement:
formEditor:
label: 'formEditor.elements.YourCustomElement.label'
group: custom
groupSorting: 1000
iconIdentifier: 't3-form-icon-your-custom-element'
mixins:
formElementMixins:
YourCustomElementMixin:
__inheritances:
10: 'TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin'
Die Konfiguration könnte im Gegensatz zur einfachen Konfiguration des Form Editors etwas anders ausssehen. Hier wurden schon einige Einstellungen vorgenommen.
Nehmen wir die wichtigen Zeilen etwas genauer unter die Lupe.
Registriere das Element für das „Neues Element erstellen“ mit diesem Code-Snippet:
formElementsDefinition:
YourCustomElement:
formEditor:
label: 'formEditor.elements.YourCustomElement.label'
group: custom
groupSorting: 1000
iconIdentifier: 't3-form-icon-your-custom-element'
- Der
key
der beimlabel
benutzt wird, ist der Key aus der Sprachdatei group
undgroupSorting
sollten selbsterklärend sein- Das Icon
iconIdentifier
muss mit Hilfe derIconFactory (EXT:/ext_localconf.php)
registriert werden und sollte eine SVG-Datei sein
Der schwerste Teil war, herauszufinden, wie man ein Viewmodel für die Stage Area definiert. Dafür gibt es hier ein Code-Snippet, damit in der Stage Area alles einwandfrei angezeigt werden kann (Label des Elements, etc.):
formEditor:
dynamicRequireJsModules:
additionalViewModelModules:
- 'TYPO3/CMS/YourCustomExtension/Backend/FormEditor/YourCustomElementViewModel'
Das verweist auf ein JavaScript Viewmodel innerhalb unserer Extension, welches sämtliche Informationen enthält und das Rendern in der Stage auslöst.
TypoScript
Das war bis hierher sehr viel YAML…..
Damit ext:form es auch erkennt, müssen wir es nun auch registrieren.
Ein Vorschlag wäre ext_typoscript_setup.txt
zu benutzen (ROOT-Level unserer Extension).
# ext_typoscript_setup.txt
module.tx_form {
settings {
yamlConfigurations {
40 = EXT:your_custom_extension/Configuration/Yaml/BaseSetup.yaml
50 = EXT:your_custom_extension/Configuration/Yaml/FormEditorSetup.yaml
}
}
view {
partialRootPaths {
40 = EXT:your_custom_extension/Resources/Private/Backend/Partials/
}
}
}
Das hilft uns zwar im Backend, aber das Frontend braucht auch ein paar Definitionen. Dafür benutzen wir Configuration/TypoScript/setup.txt
:
# Configuration/TypoScript/setup.txt
plugin.tx_form {
settings {
yamlConfigurations.40 = EXT:your_custom_extension/Configuration/Yaml/BaseSetup.yaml
}
view {
partialRootPaths.100 = EXT:your_custom_extension/Resources/Private/Frontend/Partials/
}
}
Das war es nun auch für TypoScript.
Sprachdatei
Um die Sprachdatei für das Backend zu überschreiben, müssen wir diese innerhalb von ext_localconf
überschreiben:
# ext_localconf.php
// register language file for backend
$GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride']['EXT:form/Resources/Private/Language/Database.xlf'][] = 'EXT:your_custom_extension/Resources/Private/Language/Backend.xlf'
Hier sollte das Label etc. stehen, wie schon weiter oben erwähnt.
Layout / Template / Partial
Man könnte nun Fluid-Layouts, Templats oder Partials für das Rendering im Frontend benutzen. Wie man diese erstellt, kann man in der Dokumentation nachschauen.
JavaScript
Dieses Code-Snippet ist erforderlich damit eurer Custom Form Element mit allen Features in der „Stage Area“ richtig angezeigt wird.
// Resources/Public/JavaScript/Backend/FormEditor/YourCustomElementViewModel.js
define([
'jquery',
'TYPO3/CMS/Form/Backend/FormEditor/Helper'
], function ($, Helper) {
'use strict';
return (function ($, Helper) {
/**
* @private
*
* @var object
*/
var _formEditorApp = null;
/**
* @private
*
* @return object
*/
function getFormEditorApp() {
return _formEditorApp;
};
/**
* @private
*
* @return object
*/
function getPublisherSubscriber() {
return getFormEditorApp().getPublisherSubscriber();
};
/**
* @private
*
* @return object
*/
function getUtility() {
return getFormEditorApp().getUtility();
};
/**
* @private
*
* @param object
* @return object
*/
function getHelper() {
return Helper;
};
/**
* @private
*
* @return object
*/
function getCurrentlySelectedFormElement() {
return getFormEditorApp().getCurrentlySelectedFormElement();
};
/**
* @private
*
* @param mixed test
* @param string message
* @param int messageCode
* @return void
*/
function assert(test, message, messageCode) {
return getFormEditorApp().assert(test, message, messageCode);
};
/**
* @private
*
* @return void
* @throws 1491643380
*/
function _helperSetup() {
assert('function' === $.type(Helper.bootstrap),
'The view model helper does not implement the method "bootstrap"',
1491643380
);
Helper.bootstrap(getFormEditorApp());
};
/**
* @private
*
* @return void
*/
function _subscribeEvents() {
/**
* @private
*
* @param string
* @param array
* args[0] = formElement
* args[1] = template
* @return void
*/
getPublisherSubscriber().subscribe('view/stage/abstract/render/template/perform', function (topic, args) {
if (args[0].get('type') === 'YourCustomElement') {
getFormEditorApp().getViewModel().getStage().renderSimpleTemplateWithValidators(args[0], args[1]);
}
});
};
/**
* @public
*
* @param object formEditorApp
* @return void
*/
function bootstrap(formEditorApp) {
_formEditorApp = formEditorApp;
_helperSetup();
_subscribeEvents();
};
/**
* Publish the public methods.
* Implements the "Revealing Module Pattern".
*/
return {
bootstrap: bootstrap
};
})($, Helper);
});
Eine Zeile muss hier noch angepasst werden:
if (args[0].get('type') === 'YourCustomElement') { // benutze hier den Namen des Elements
getFormEditorApp().getViewModel().getStage().renderSimpleTemplateWithValidators(args[0], args[1]);
}
Quelle: GitHub englisches Tutorial