Eigenes Custom Form Element für tx_form-Extension – Typo3 v8

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'
  • Derkey der beim label benutzt wird, ist der Key aus der Sprachdatei
  • group und groupSorting sollten selbsterklärend sein
  • Das Icon iconIdentifier muss mit Hilfe der IconFactory (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