Conditional field - Creating a 'Flexible Content Field' (like 'Advanced Custom Fields' wordpress plugin)

WORKING 'FLEXIBLE CONTENT FIELD’

WHAT YOU NEED TO DO TO IMPLEMENT THIS:

  1. Extend your theme to add/load some css & js into the admin
  2. Add the css & js file in the theme folder
  3. Add the <templateName>.yaml file to display the Flexible Content Field in the admin
  4. Loop through the ‘Flexible Content Field’ inside your <templateName>.html.twig file to display the data.

MORE DETAILED EXPLANATION:

#1. Extend your theme
Inside your theme folder you have a <themename>.php file. Add the code:
(In this example my theme name is ‘Base’, change this name to your theme name.)

<?php
namespace Grav\Theme;

use Grav\Common\Theme;

class Base extends Theme
{
    // Add assets to the Admin
    public function onAssetsInitialized() {
        if ($this->isAdmin()) {

            // add JS
            $this->grav['assets']->addJs('theme://js/admin.js');
            // add CSS
            $this->grav['assets']->addCss('theme://css/admin.css');

        }
    }
}

#2. Add css & js files.
Your theme has a css folder and a js folder.

  • In the css folder create a file named ‘admin.css’.
  • In the js folder create a file named ‘admin.js’

Add this CSS:

/* Hide form-fieldset by default */
ul.field-selection > li > .form-fieldset {
    display: none;
}

/* Show form-fieldset that is selected */
ul.field-selection > li > .form-fieldset.field-selection--show {
    display: block;
}

Add this JS:

var flexibleContentField = (function (w, d, $, undefined) {

    'use strict';

    var s = {
            selectors: {
                theRepeater: '.field-selection',
                theSelect: '.field-selection__select'
            }
        },
        els = {},
        init = function () {

            // define elements
            els.theRepeater = $(s.selectors.theRepeater);

            // no elements
            if (!els.theRepeater.length) { return; }

            // theloop
            els.theRepeater.each(function() {
                // the bind
                $(this).on('change', checkTarget);

                // adjust existing selections
                var theRepeaterItems = $(this).children('li');
                theRepeaterItems.each(function(index, element) {
                    var theRepeaterItem = $(element),
                        theSelect = theRepeaterItem.find(s.selectors.theSelect);

                    showHide(theRepeaterItem, theSelect);
                });
            });
            
        },
        checkTarget = function(event) {

            // The vars
            var theRepeaterItem = $(event.target).closest('li'),
                theSelect = event.target;

            // Target is Select?
            theSelect.nodeName.toLowerCase() === 'select' ? showHide(theRepeaterItem, theSelect) : '';
        },
        showHide = function (theRepeaterItem, theSelect) {

            // the vars
            var theRepeaterItem = $(theRepeaterItem),
                theSelect = $(theSelect),
                theSelectParent = theSelect.closest('.form-field'),

                theValue = theSelect.val(),
                theRefererPrefix = 'field-selection__',
                theReferer = theRefererPrefix + theValue,

                theSelectedFields = theRepeaterItem.find('input[type="hidden"][value="'+theReferer+'"]'),
                theFieldToShow = theSelectedFields.closest('.form-fieldset');

            // Exeption - default selected
            if (theValue === 'default') { return; }

            // show the selected field
            theFieldToShow.addClass('field-selection--show');

            // hide the select
            theSelectParent.css('display','none');
        }

    return {
        init: init
    };

}(window, window.document, window.jQuery));

// theCall - on window loaded
(function (w, d, undefined) {

    "use strict";

    var raf = requestAnimationFrame || mozRequestAnimationFrame || webkitRequestAnimationFrame || msRequestAnimationFrame,
        init = function () { w.flexibleContentField.init(); };

    // when all is loaded
    raf ? raf(function () { w.setTimeout(init, 0); }) : w.addEventListener('load', init);

}(window, window.document));

#3. Define the fields

Define the fields for your ‘Flexible Content Field’ inside a <templateName>.yaml file.
As example, you could need this Flexible Content Field inside an article.html.twig template, so the yaml file for this template would be article.yaml.

Inside your <templateName>.yaml file add this code to display the Flexible Content Field.
As example this Flexible Content Field code is used to let a user choose between a tinyMCE, a slideshow, a testimonial and a video.

Where is states # Define your grouped field here..., thats where you define the fields you need for your pre-designed components.

    # - Flexible Content Field -
    #
    # The Flexible Content Field is used to let a user
    # define groups of sub fields (layouts) that he/she can add, edit,
    # and re-order to create highly customized content.

    fieldSeletion:
      type: fieldset
      title: Field selection
      text: Flexible Content Fields
      icon: puzzle-piece
      collapsed: false
      collapsible: false
      fields:

        # Flexible Content Field - Repeater
        #
        # requirements:
        # - classes: field-selection
        header.fieldSelection:
          type: list
          style: vertical
          label: Repeater/list with conditional field selection
          classes: field-selection
          fields:

            # Flexible Content Field - Selector
            # 
            # requirements:
            # - type: select
            # - classes: field-selection__select
            # - the 'keys' of the options are the 'field option' names.
            # - 'default' option is required
            .select:
              type: select
              size: long
              classes: field-selection__select
              label: Select the type of field you would like to add
              default: 'default'
              options:
                default: Maak een keuze
                tinymce: Text editor
                slideshow: Slideshow
                testimonial: Testimonial
                video: Video

            # Field Option
            #
            # requirements:
            # - type: fieldset
            .tinymce:
              type: fieldset
              title: Text editor
              collapsed: true
              collapsible: true
              fields:

                # Field Option Referer
                #
                # requirements:
                # - type: hidden
                # - default: field-selection__<Field Option>
                referer:
                  type: hidden
                  default: field-selection__tinymce

                # Fields
                #
                # Define your grouped field here...
                .tinymce.title:
                  type: text
                  label: title

            # Field Option
            .slideshow:
              type: fieldset
              title: Slideshow
              collapsed: true
              collapsible: true
              fields:

                # Field Option Referer
                referer:
                  type: hidden
                  default: field-selection__slideshow

                # Fields
                .slideshow.title:
                  type: text
                  label: title

            # Field Option
            .testimonial:
              type: fieldset
              title: Testimonial
              collapsed: true
              collapsible: true
              fields:

                # Field Option Referer
                referer:
                  type: hidden
                  default: field-selection__testimonial

                # Fields
                .testimonial.title:
                  type: text
                  label: title

            # Field Option
            .video:
              type: fieldset
              title: Video
              collapsed: true
              collapsible: true
              fields:

                # Field Option Referer
                referer:
                  type: hidden
                  default: field-selection__video

                # Fields
                .video.title:
                  type: text
                  label: title

This is what the ‘Flexible Content Field’ looks like in the admin:


When the user add an item:

When the user selected a component:

The user can add/remove/replace his selected components:

#4. The Markdown file

This is an example output in the markdown file.
You can loop throught the fieldSelection and use the select to determine which type of component you have to render, and in which order.

In this example the user whats to use:

  • a slideshow firlst
  • then a video
  • and finally a text editor
---
fieldSelection:
    -
        select: slideshow
        tinymce:
            title: ''
        slideshow:
            title: 'Example user input'
        testimonial:
            title: ''
        video:
            title: ''
    -
        select: video
        tinymce:
            title: ''
        slideshow:
            title: ''
        testimonial:
            title: ''
        video:
            title: 'Example user input'
    -
        select: tinymce
        tinymce:
            title: 'Example user input'
        slideshow:
            title: ''
        testimonial:
            title: ''
        video:
            title: ''
---

IF YOU MANAGE TO IMPROVE ON THIS CODE, AND/OR MAKE A PLUGIN OUT OF IT. PLEASE SHARE :slight_smile:

4 Likes