Contao-Summit 2026

Ankündigung

Collapse
No announcement yet.

CSS-Klassen einer Elementgruppe in der "Detailansicht" der Elementgruppe ausgeben

Collapse
X
 
  • Filter
  • Zeit
  • Show
Clear All
new posts

    CSS-Klassen einer Elementgruppe in der "Detailansicht" der Elementgruppe ausgeben

    Es gibt bestimmt einen besseren Weg, aber mit den folgenden Anpassungen können die CSS-Klassen von Elementgruppen in der "Detailansicht" der Elementgruppe ausgegeben werden um bspw. bei Spaltenansichten die Inhaltselemente nebeneinander anzuordnen.

    Es müssen drei Dateien erstellt werden:
    /public_html/contao57.com/src/InsertTag/ElementGroupCssClassInsertTag.php
    /public_html/contao57.com/templates/backend/data_container/table/view/_record_listing.html.twig
    /public_html/contao57.com/templates/backend/data_container/table/view/parent.html.twig

    ElementGroupCssClassInsertTag.php
    HTML-Code:
    <?php
    
    declare(strict_types=1);
    
    namespace App\InsertTag;
    
    use Contao\ContentModel;
    use Contao\CoreBundle\DependencyInjection\Attribute\AsInsertTag;
    use Contao\CoreBundle\Framework\ContaoFramework;
    use Contao\CoreBundle\InsertTag\InsertTagResult;
    use Contao\CoreBundle\InsertTag\OutputType;
    use Contao\CoreBundle\InsertTag\ResolvedInsertTag;
    use Contao\CoreBundle\InsertTag\Resolver\InsertTagResolverNestedResolvedInterface;
    use Contao\StringUtil;
    
    #[AsInsertTag('element_group_cssclass')]
    class ElementGroupCssClassInsertTag implements InsertTagResolverNestedResolvedInterface
    {
        public function __construct(private readonly ContaoFramework $framework) {}
    
        public function __invoke(ResolvedInsertTag $insertTag): InsertTagResult
        {
            $this->framework->initialize();
    
            $id = (int) $insertTag->getParameters()->get(0);
            $model = $this->framework->getAdapter(ContentModel::class)->findByPk($id);
    
            if (!$model || !$model->cssID) {
                return new InsertTagResult('', OutputType::text);
            }
    
            $cssId = StringUtil::deserialize($model->cssID, true);
    
            return new InsertTagResult($cssId[1] ?? '', OutputType::text);
        }
    }
    _record_listing.html.twig
    HTML-Code:
    {% extends '@Contao/backend/data_container/table/view/_record_listing.html.twig' %}
    
    {% set group_css_class = insert_tag('element_group_cssclass::' ~ pid) %}
    {% set listing_attributes = attrs()
        .addClass(group_css_class, group_css_class)
        .mergeWith(listing_attributes|default)
    %}
    parent.html.twig
    HTML-Code:
    {% trans_default_domain 'contao_default' %}
    {% extends '@Contao/backend/data_container/table/view/_base.html.twig' %}
    
    {% set view_attributes = attrs()
        .set('data-controller', 'contao--limit-height', limit_height)
        .set('data-contao--limit-height-max-value', limit_height, limit_height)
        .set('data-contao--limit-height-expand-value', 'MSC.expandNode'|trans, limit_height)
        .set('data-contao--limit-height-collapse-value', 'MSC.collapseNode'|trans, limit_height)
        .set('data-contao--limit-height-expand-all-value', 'DCA.expandNodes.0'|trans, limit_height)
        .set('data-contao--limit-height-expand-all-title-value', 'DCA.expandNodes.1'|trans, limit_height)
        .set('data-contao--limit-height-collapse-all-value', 'DCA.collapseNodes.0'|trans, limit_height)
        .set('data-contao--limit-height-collapse-all-title-value', 'DCA.collapseNodes.1'|trans, limit_height)
    %}
    
    {% block listing %}
        {% if has_clipboard_content %}
            <div id="paste_hint"><p>{{ 'MSC.selectNewPosition'|trans }}</p></div>
        {% endif %}
    
        {% embed '@Contao/backend/data_container/table/view/_record_listing.html.twig' with {
            listing_attributes: attrs().addClass('parent_view').addClass('as-grid', display_grid),
        } %}
            {% trans_default_domain 'contao_default' %}
    
            {% block records %}
                <div class="tl_header hover-div" data-controller="contao--deeplink contao--operations-menu" data-action="contextmenu->contao--operations-menu#open click->contao--check-all#toggleInput">
                    <div class="tl_content_right">
                    
                        {# Hinzufügen von CSS-Klassen - start #}
                        {% set group_css_class = insert_tag('element_group_cssclass::' ~ pid) %}
                        {% if group_css_class %}
                            <span class="cssIdClass">
                                <code class="cssClass">
                                    {% for class in group_css_class|split(' ') %}
                                        {% if class %}
                                            <span>{{ class }}</span>
                                        {% endif %}
                                    {% endfor %}
                                </code>
                            </span>
                        {% endif %}
                        {# Hinzufügen von CSS-Klassen - end #}
                        
                        {{ block('select_all_button') }}
    
                        {{ header_operations|default|raw }}
                    </div>
                    <table class="tl_header_table">
                        {% for label, value in table_headers %}
                            <tr>
                                <td><span class="tl_label">{{ label }}</span></td>
                                <td>{{ value }}</td>
                            </tr>
                        {% endfor %}
                    </table>
                </div>
    
                {% if records|length %}
                    {% set list_attributes = attrs()
                        .set('data-controller', 'contao--sortable', is_sortable)
                        .set('data-contao--sortable-handle-value', '.drag-handle', is_sortable)
                        .set('data-contao--sortable-parent-mode-value', 'true', is_sortable)
                        .set('data-contao--sortable-request-token-value', contao.request_token, is_sortable)
                        .set('data-id', pid, is_sortable)
                    %}
                    <ul{{ list_attributes }}>
                        {% for record in records %}
                            <li{{ attrs().set('data-id', record.id, is_sortable) }}>
                                {% if record.group_header is defined %}
                                    <div class="tl_content_header">{{ record.group_header|raw }}</div>
                                {% endif %}
    
                                {% set record_attributes = attrs()
                                    .addClass('tl_content')
                                    .addClass('wrapper_start', record.display.wrapper_start|default)
                                    .addClass('wrapper_separator', record.display.wrapper_separator|default)
                                    .addClass('wrapper_stop', record.display.wrapper_stop|default)
                                    .addClass(['indent', "indent_#{record.display.wrap_level|default}"], record.display.wrap_level|default)
                                    .addClass('indent_first', record.display.indent_first|default)
                                    .addClass('indent_last', record.display.indent_last|default)
                                    .addClass('draft', record.is_draft)
                                    .addClass(record.class)
                                    .set('data-turbo', 'false')
                                    .set('data-action', 'click->contao--check-all#toggleInput')
                                %}
                                <div{{ record_attributes }}>
                                    <div class="inside hover-div" data-controller="contao--deeplink contao--operations-menu" data-action="contextmenu->contao--operations-menu#open">
                                        {# Right column #}
                                        <div class="tl_content_right" data-turbo="true">
                                            {{ include(('@Contao/backend/data_container/table/view/_record_operations.html.twig')) }}
                                        </div>
    
                                        {# Record #}
                                        {% if record.legacy_data is defined %}
                                            {{ record.legacy_data|raw }}
                                        {% else %}
                                            {% if display_grid %}
                                                {# Record displayed as box with optional preview #}
                                                {% set record_label_attributes = attrs()
                                                    .addClass('cte_type')
                                                    .addClass('draggable', record.allow_dragging)
                                                    .addClass(record.state, record.state)
                                                %}
                                                <div{{ record_label_attributes }}>
                                                    {% block label %}
                                                        {% if record.allow_dragging %}
                                                            <button type="button" class="drag-handle" data-action="keydown->contao--sortable#move">
                                                                {{ backend_icon('drag.svg', record.drag_handle_label) }}
                                                            </button>
                                                            <div>{{ record.label|raw }}</div>
                                                        {% else %}
                                                            {{ record.label|raw }}
                                                        {% endif %}
                                                    {% endblock %}
                                                </div>
    
                                                {% if record.preview %}
                                                    <div class="cte_content" data-contao--limit-height-target="node">
                                                        <div class="cte_preview" style="contain:paint">{{ record.preview|raw }}</div>
                                                    </div>
                                                {% endif %}
                                            {% else %}
                                                {# Record displayed flat #}
                                                {% set record_label_attributes = attrs()
                                                    .addClass('tl_content_left')
                                                    .addClass('draggable', record.allow_dragging)
                                                    .addClass(record.state, record.state)
                                                %}
                                                <div{{ record_label_attributes }}>
                                                    {{ block('label') }}
                                                </div>
                                            {% endif %}
                                        {% endif %}
                                    </div>
                                </div>
                            </li>
                        {% endfor %}
                    </ul>
                {% else %}
                    <p class="tl_empty_parent_view">{{ (panel_active ? 'MSC.noResultWithFilter' : 'MSC.noResult')|trans }}</p>
                {% endif %}
            {% endblock %}
        {% endembed %}
    {% endblock %}
    optionales Backend CSS:
    HTML-Code:
    /* Vorschau der Elementgruppe - Start */
    .cte_preview > :is(.Spalten2, .Spalten3, .Spalten4, .Spalten5, .Spalten6) {
    	& > * {
    		background: var(--content-bg);
    		border: 1px solid var(--border);
    		border-radius: var(--border-radius);
    		padding: 10px;
    	}
    }
    
    .tl_content:has(.Spalten2, .Spalten3, .Spalten4, .Spalten5, .Spalten6) {
    	.cte_preview { background: var(--panel-bg); }
    	[data-contao--limit-height-target][style*=max-height] .limit_toggler {
    		background: linear-gradient(transparent,var(--panel-bg) 60%);
    	}
    }
    /* Vorschau der Elementgruppe - Ende */
    
    /* Operations-Icons ausblenden um Platz zu sparen - Start */
    .Spalten2, .Spalten3, .Spalten4, .Spalten5, .Spalten6 {
    	& > ul .tl_content_right .operations > ul > li {
    		display: none;
    		&:first-child, &:has([data-label="Verstecken"]), &.operations-menu-container { display: block; }
    	}
    }
    /* Operations-Icons ausblenden um Platz zu sparen - Ende */
    
    /* Abstand oben korrigieren - Start */
    .Spalten2, .Spalten3, .Spalten4, .Spalten5, .Spalten6 {
    	.tl_content { margin-top: 0; }
    }
    /* Abstand oben korrigieren - Ende */
    
    .cte_preview :is(.Spalten2, .Spalten3, .Spalten4, .Spalten5, .Spalten6) { gap: 10px; }
    .Spalten2 > ul, .cte_preview .Spalten2,
    .Spalten3 > ul, .cte_preview .Spalten3,
    .Spalten4 > ul, .cte_preview .Spalten4,
    .Spalten5 > ul, .cte_preview .Spalten5,
    .Spalten6 > ul, .cte_preview .Spalten6 { 
    	display: grid;
    	gap: 15px;
    }
    .Spalten2 > ul, .cte_preview .Spalten2 { grid-template-columns: repeat(2, 1fr); }
    .Spalten3 > ul, .cte_preview .Spalten3 { grid-template-columns: repeat(3, 1fr); }
    .Spalten4 > ul, .cte_preview .Spalten4 { grid-template-columns: repeat(4, 1fr); }
    .Spalten5 > ul, .cte_preview .Spalten5 { grid-template-columns: repeat(5, 1fr); }
    .Spalten6 > ul, .cte_preview .Spalten6 { grid-template-columns: repeat(6, 1fr); }
    
    /* Backend Swiper Vorschau - Start */
    .cte_preview .content-custom-swiper .swiper-wrapper {
        display: grid;
        grid-template-columns: 1fr 1fr 1fr;
        gap: 10px;
    }
    
    .cte_preview img:not(.backend-icon) {
    	max-width: 100%;
    }
    /* Backend Swiper Vorschau - Ende */
    
    div[class^='Spalten'], div[class*=' Spalten'] {
    	
    	&.Spalte1Breite10 ul { --ErsteSpalte: 10fr; } &.Spalte1Breite15 ul { --ErsteSpalte: 15fr; } &.Spalte1Breite20 ul { --ErsteSpalte: 20fr; } &.Spalte1Breite25 ul { --ErsteSpalte: 25fr; } &.Spalte1Breite30 ul { --ErsteSpalte: 30fr; } &.Spalte1Breite35 ul { --ErsteSpalte: 35fr; } &.Spalte1Breite40 ul { --ErsteSpalte: 40fr; } &.Spalte1Breite45 ul { --ErsteSpalte: 45fr; } &.Spalte1Breite50 ul { --ErsteSpalte: 50fr; } &.Spalte1Breite55 ul { --ErsteSpalte: 55fr; } &.Spalte1Breite60 ul { --ErsteSpalte: 60fr; } &.Spalte1Breite65 ul { --ErsteSpalte: 65fr; } &.Spalte1Breite70 ul { --ErsteSpalte: 70fr; } &.Spalte1Breite75 ul { --ErsteSpalte: 75fr; } &.Spalte1Breite80 ul { --ErsteSpalte: 80fr; } &.Spalte1Breite85 ul { --ErsteSpalte: 85fr; } &.Spalte1Breite90 ul { --ErsteSpalte: 90fr; } &.Spalte1Breite95 ul { --ErsteSpalte: 95fr; }
    	&.Spalte2Breite10 ul { --ZweiteSpalte: 10fr; } &.Spalte2Breite15 ul { --ZweiteSpalte: 15fr; } &.Spalte2Breite20 ul { --ZweiteSpalte: 20fr; } &.Spalte2Breite25 ul { --ZweiteSpalte: 25fr; } &.Spalte2Breite30 ul { --ZweiteSpalte: 30fr; } &.Spalte2Breite35 ul { --ZweiteSpalte: 35fr; } &.Spalte2Breite40 ul { --ZweiteSpalte: 40fr; } &.Spalte2Breite45 ul { --ZweiteSpalte: 45fr; } &.Spalte2Breite50 ul { --ZweiteSpalte: 50fr; } &.Spalte2Breite55 ul { --ZweiteSpalte: 55fr; } &.Spalte2Breite60 ul { --ZweiteSpalte: 60fr; } &.Spalte2Breite65 ul { --ZweiteSpalte: 65fr; } &.Spalte2Breite70 ul { --ZweiteSpalte: 70fr; } &.Spalte2Breite75 ul { --ZweiteSpalte: 75fr; } &.Spalte2Breite80 ul { --ZweiteSpalte: 80fr; } &.Spalte2Breite85 ul { --ZweiteSpalte: 85fr; } &.Spalte2Breite90 ul { --ZweiteSpalte: 90fr; } &.Spalte2Breite95 ul { --ZweiteSpalte: 95fr; }
    	&.Spalte3Breite10 ul { --DritteSpalte: 10fr; } &.Spalte3Breite15 ul { --DritteSpalte: 15fr; } &.Spalte3Breite20 ul { --DritteSpalte: 20fr; } &.Spalte3Breite25 ul { --DritteSpalte: 25fr; } &.Spalte3Breite30 ul { --DritteSpalte: 30fr; } &.Spalte3Breite35 ul { --DritteSpalte: 35fr; } &.Spalte3Breite40 ul { --DritteSpalte: 40fr; } &.Spalte3Breite45 ul { --DritteSpalte: 45fr; } &.Spalte3Breite50 ul { --DritteSpalte: 50fr; } &.Spalte3Breite55 ul { --DritteSpalte: 55fr; } &.Spalte3Breite60 ul { --DritteSpalte: 60fr; } &.Spalte3Breite65 ul { --DritteSpalte: 65fr; } &.Spalte3Breite70 ul { --DritteSpalte: 70fr; } &.Spalte3Breite75 ul { --DritteSpalte: 75fr; } &.Spalte3Breite80 ul { --DritteSpalte: 80fr; } &.Spalte3Breite85 ul { --DritteSpalte: 85fr; } &.Spalte3Breite90 ul { --DritteSpalte: 90fr; } &.Spalte3Breite95 ul { --DritteSpalte: 95fr; }
    	&.Spalte4Breite10 ul { --VierteSpalte: 10fr; } &.Spalte4Breite15 ul { --VierteSpalte: 15fr; } &.Spalte4Breite20 ul { --VierteSpalte: 20fr; } &.Spalte4Breite25 ul { --VierteSpalte: 25fr; } &.Spalte4Breite30 ul { --VierteSpalte: 30fr; } &.Spalte4Breite35 ul { --VierteSpalte: 35fr; } &.Spalte4Breite40 ul { --VierteSpalte: 40fr; } &.Spalte4Breite45 ul { --VierteSpalte: 45fr; } &.Spalte4Breite50 ul { --VierteSpalte: 50fr; } &.Spalte4Breite55 ul { --VierteSpalte: 55fr; } &.Spalte4Breite60 ul { --VierteSpalte: 60fr; } &.Spalte4Breite65 ul { --VierteSpalte: 65fr; } &.Spalte4Breite70 ul { --VierteSpalte: 70fr; } &.Spalte4Breite75 ul { --VierteSpalte: 75fr; } &.Spalte4Breite80 ul { --VierteSpalte: 80fr; } &.Spalte4Breite85 ul { --VierteSpalte: 85fr; } &.Spalte4Breite90 ul { --VierteSpalte: 90fr; } &.Spalte4Breite95 ul { --VierteSpalte: 95fr; }
    	&.Spalte5Breite10 ul { --FünfteSpalte: 10fr; } &.Spalte5Breite15 ul { --FünfteSpalte: 15fr; } &.Spalte5Breite20 ul { --FünfteSpalte: 20fr; } &.Spalte5Breite25 ul { --FünfteSpalte: 25fr; } &.Spalte5Breite30 ul { --FünfteSpalte: 30fr; } &.Spalte5Breite35 ul { --FünfteSpalte: 35fr; } &.Spalte5Breite40 ul { --FünfteSpalte: 40fr; } &.Spalte5Breite45 ul { --FünfteSpalte: 45fr; } &.Spalte5Breite50 ul { --FünfteSpalte: 50fr; } &.Spalte5Breite55 ul { --FünfteSpalte: 55fr; } &.Spalte5Breite60 ul { --FünfteSpalte: 60fr; } &.Spalte5Breite65 ul { --FünfteSpalte: 65fr; } &.Spalte5Breite70 ul { --FünfteSpalte: 70fr; } &.Spalte5Breite75 ul { --FünfteSpalte: 75fr; } &.Spalte5Breite80 ul { --FünfteSpalte: 80fr; } &.Spalte5Breite85 ul { --FünfteSpalte: 85fr; } &.Spalte5Breite90 ul { --FünfteSpalte: 90fr; } &.Spalte5Breite95 ul { --FünfteSpalte: 95fr; }
    	&.Spalte6Breite10 ul { --SechsteSpalte: 10fr; } &.Spalte6Breite15 ul { --SechsteSpalte: 15fr; } &.Spalte6Breite20 ul { --SechsteSpalte: 20fr; } &.Spalte6Breite25 ul { --SechsteSpalte: 25fr; } &.Spalte6Breite30 ul { --SechsteSpalte: 30fr; } &.Spalte6Breite35 ul { --SechsteSpalte: 35fr; } &.Spalte6Breite40 ul { --SechsteSpalte: 40fr; } &.Spalte6Breite45 ul { --SechsteSpalte: 45fr; } &.Spalte6Breite50 ul { --SechsteSpalte: 50fr; } &.Spalte6Breite55 ul { --SechsteSpalte: 55fr; } &.Spalte6Breite60 ul { --SechsteSpalte: 60fr; } &.Spalte6Breite65 ul { --SechsteSpalte: 65fr; } &.Spalte6Breite70 ul { --SechsteSpalte: 70fr; } &.Spalte6Breite75 ul { --SechsteSpalte: 75fr; } &.Spalte6Breite80 ul { --SechsteSpalte: 80fr; } &.Spalte6Breite85 ul { --SechsteSpalte: 85fr; } &.Spalte6Breite90 ul { --SechsteSpalte: 90fr; } &.Spalte6Breite95 ul { --SechsteSpalte: 95fr; }
    
    	&.Spalten2 ul { grid-template-columns: minmax(var(--Mindestbreite, 200px), var(--ErsteSpalte, 1fr) ) minmax(var(--Mindestbreite, 200px), var(--ZweiteSpalte, 1fr) ); }
    	&.Spalten3 ul { grid-template-columns: minmax(var(--Mindestbreite, 200px), var(--ErsteSpalte, 1fr) ) minmax(var(--Mindestbreite, 200px), var(--ZweiteSpalte, 1fr) ) minmax(var(--Mindestbreite, 200px), var(--DritteSpalte, 1fr) ); }
    	&.Spalten4 ul { grid-template-columns: minmax(var(--Mindestbreite, 200px), var(--ErsteSpalte, 1fr) ) minmax(var(--Mindestbreite, 200px), var(--ZweiteSpalte, 1fr) ) minmax(var(--Mindestbreite, 200px), var(--DritteSpalte, 1fr) ) minmax(var(--Mindestbreite, 200px), var(--VierteSpalte, 1fr) ); }
    	&.Spalten5 ul { grid-template-columns: minmax(var(--Mindestbreite, 200px), var(--ErsteSpalte, 1fr) ) minmax(var(--Mindestbreite, 200px), var(--ZweiteSpalte, 1fr) ) minmax(var(--Mindestbreite, 200px), var(--DritteSpalte, 1fr) ) minmax(var(--Mindestbreite, 200px), var(--VierteSpalte, 1fr) ) minmax(var(--Mindestbreite, 200px), var(--FünfteSpalte, 1fr) ); }
    	&.Spalten6 ul { grid-template-columns: minmax(100px, var(--ErsteSpalte, 1fr) ) minmax(100px, var(--ZweiteSpalte, 1fr) ) minmax(100px, var(--DritteSpalte, 1fr) ) minmax(100px, var(--VierteSpalte, 1fr) ) minmax(100px, var(--FünfteSpalte, 1fr) ) minmax(100px, var(--SechsteSpalte, 1fr) ); }
    
    }

    Danach muss noch der Cache geleert werden.

    Damit können dann, mit eigenem CCS im Backend, folgende Ansichten umgesetzt werden:
    Click image for larger version

Name:	Bildschirmfoto 2026-05-12 um 10.35.55.png
Views:	1
Size:	601,1 KB
ID:	950284
    Click image for larger version

Name:	Bildschirmfoto 2026-05-12 um 10.36.24.png
Views:	1
Size:	1,93 MB
ID:	950285

    So habe ich es auch in der Erweiterung Contao-Custom-Backend-Settings umgesetzt:
    Contribute to heimseiten/contao-custom-backend-settings-bundle development by creating an account on GitHub.
    Last edited by Niels H; 12.05.2026, 09:49.
    heimseiten.de - Webdesign aus Köln

    tech-events.info

    #2
    Statt dem Insert-Tag solltest du wenn dann eher eine Twig-Funktion nutzen.
    » sponsor me via GitHub or Revolut

    Comment


      #3
      Zitat von Spooky Beitrag anzeigen
      Statt dem Insert-Tag solltest du wenn dann eher eine Twig-Funktion nutzen.
      Hab ich hier als Issue geöffnet, hat sich aber nix getan (also Null Antwort), daher hab ich es wieder geschlossen (Ist dann ja wohl nicht geplant)
      It would be better to use twig functions instead of relying on insert tags contao-custom-backend-settings-bundle/contao/templates/backend/data_container/table/view/_record_listing.html.twig Line 3 ...
      Contao-Association unterstützen

      Comment


        #4
        Danke Spooky. Dass hatte Zoglo auch schon angemerkt. Ich habe es im ersten Schritt nur so geschafft. Wenn ich mich das nächste Mal damit beschäftige, versuche ich eine Lösung ohne insert_tag zu finden.
        heimseiten.de - Webdesign aus Köln

        tech-events.info

        Comment

        Working...
        X