Skip to content

Instantly share code, notes, and snippets.

@atanas-dev
Last active October 31, 2020 20:41
Show Gist options
  • Save atanas-dev/9c360340d12db751954f9d8ded411769 to your computer and use it in GitHub Desktop.
Save atanas-dev/9c360340d12db751954f9d8ded411769 to your computer and use it in GitHub Desktop.

Using Dynamic Content

Declaring which fields should support Dynamic Content

Your first step to enable Dynamic Content is to add a new configuration option to your fields of choice:

'title' => array(
	'label'           => esc_html__( 'Title', 'simp-simple-extension' ),
	'type'            => 'text',
	'option_category' => 'basic_option',
	'description'     => esc_html__( 'Input your desired title here.', 'simp-simple-extension' ),
	'toggle_slug'     => 'main_content',
	// Enable dynamic content. The value should be 'text', 'image' or 'url'
	// depending on what type of content you wish to have in your field.
	'dynamic_content' => 'text',
),

Using dynamic content on the frontend

Using dynamic content on the frontend requires a minor adjustment to the render() method:

public function render( $unprocessed_props, $content = null, $render_slug ) {
	$title   = $this->_esc_attr( 'title' );
	$content = $this->_esc_attr( 'content', 'full' );

	return sprintf(
		'<div>
			<h2>%1$s</h2>
			%2$s
		</div>',
		$title,
		$content
	);
}

In the above example you can see the usage of the new _esc_attr( $attribute, $html ) utility method which:

  1. Escapes html in static user input, unless $html is set to 'full'.
  2. Resolves dynamic content, if the field contains any.

Using dynamic content in the Visual Builder

Fields which have dynamic content support declared will automatically become available in a special utility dynamic prop in your React component:

render() {
  const title   = this.props.dynamic.title;
  const content = this.props.dynamic.content;

  return (
    <div>
      <h2>{title.render()}</h2>
      <div>{content.render('full')}</div>
    </div>
  );
}

Each field in the dynamic prop is an object with a couple of properties:

Property Type Description
value String The resolved value. If the user has not selected dynamic content, this will hold the value the user has entered manually.
type String The type of dynamic content as specified in the field configuration.
dynamic Boolean Shows whether the current value is dynamic or not.
loading Boolean Shows whether the current value is still being resolved.
hasValue Boolean Shows whether the value is loading or is not empty.
render Function A utility function which handles rendering a loading state, dynamic content or static content. Renders an editable rich text field for tiny_mce fields when the value is not dynamic.

A full example:

Here we will demonstrate how you can build a simple blurb module with a title, a title link, an image and a content field.

The module's PHP class:

<?php

class SIMP_SimpleBlurb extends ET_Builder_Module {

	public $slug       = 'simp_simple_blurb';
	public $vb_support = 'on';

	protected $module_credits = array(
		'module_uri' => '',
		'author'     => '',
		'author_uri' => '',
	);

	public function init() {
		$this->name = esc_html__( 'Simple Blurb', 'simp-simple-extension' );
	}

	public function get_fields() {
		return array(
			'title'       => array(
				'label'           => esc_html__( 'Title', 'simp-simple-extension' ),
				'type'            => 'text',
				'option_category' => 'basic_option',
				'description'     => esc_html__( 'Input your desired title here.', 'simp-simple-extension' ),
				'toggle_slug'     => 'main_content',
				// We need text values so we specify 'text' as the dynamic content type.
				'dynamic_content' => 'text',
			),
			'title_link'  => array(
				'label'           => esc_html__( 'Title Link', 'simp-simple-extension' ),
				'type'            => 'text',
				'option_category' => 'basic_option',
				'description'     => esc_html__( 'Input your desired title link here.', 'simp-simple-extension' ),
				'toggle_slug'     => 'main_content',
				// We need URL values so we specify 'url' as the dynamic content type.
				'dynamic_content' => 'url',
			),
			'image'       => array(
				'label'           => esc_html__( 'Heading', 'simp-simple-extension' ),
				'type'            => 'upload',
				'option_category' => 'basic_option',
				'description'     => esc_html__( 'Select your desired image here.', 'simp-simple-extension' ),
				'toggle_slug'     => 'main_content',
				// We need image URL values so we specify 'image' as the dynamic content type.
				'dynamic_content' => 'image',
			),
			'content'     => array(
				'label'           => esc_html__( 'Content', 'simp-simple-extension' ),
				'type'            => 'tiny_mce',
				'option_category' => 'basic_option',
				'description'     => esc_html__( 'Content entered here will appear below the title text.', 'simp-simple-extension' ),
				'toggle_slug'     => 'main_content',
				// We need text values so we specify 'text' as the dynamic content type.
				'dynamic_content' => 'text',
			),
		);
	}

	public function render( $attrs, $content = null, $render_slug ) {
		$title      = $this->_esc_attr( 'title' );
		$title_link = $this->_esc_attr( 'title_link' );
		$image      = $this->_esc_attr( 'image' );
		// We pass 'full' as the second argument so html is not escaped in the resulting value.
		$content    = $this->_esc_attr( 'content', 'full' );

		if ( ! empty( $title ) ) {
			if ( ! empty( $title_link ) ) {
				$title = sprintf(
					'<a href="%1$s">%2$s</a>',
					esc_url( $title_link ),
					$title
				);
			}

			$title = sprintf(
				'<h2>%1$s</h2>',
				$title
			);
		}

		if ( ! empty( $image ) ) {
			$image = sprintf(
				'<img src="%1$s" alt="" />',
				esc_url( $image )
			);
		}

		return sprintf(
			'
				%2$s
				%1$s
				%3$s
			',
			$title,
			$image,
			$content
		);
	}
}

new SIMP_SimpleBlurb;

The module's React component:

// External Dependencies
import React, { Component, Fragment } from 'react';

// Internal Dependencies
import './style.css';

class SimpleBlurb extends Component {

  static slug = 'simp_simple_blurb';

  _renderTitle() {
    const title     = this.props.dynamic.title;
    const titleLink = this.props.dynamic.title_link;

    let titleComponent = title.render();

    if (title.loading) {
      // Let Divi render the loading placeholder.
      return titleComponent;
    }

    if (titleLink.hasValue) {
      // Wrap the title in a link
      titleComponent = (
        <a href={titleLink.value}>
          {titleComponent}
        </a>
      );
    }

    return (
      <h2>
        {titleComponent}
      </h2>
    );
  }

  _renderImage() {
    const image = this.props.dynamic.image;

    if (image.loading) {
      // Let Divi render the loading placeholder.
      return image.render();
    }

    if (!image.hasValue) {
      // No image selected or the dynamic content selected does resolves to an empty value.
      // This can happen in cases where the user selects "Featured Image" as dynamic
      // content, but has not assigned a feature image to the post.
      return null;
    }

    return (
      <img src={image.value} alt="" />
    );
  }

  _renderContent() {
    const content = this.props.dynamic.content;

    // Let Divi handle all of the rendering.
    return content.render('full');
  }

  render() {
    return (
      <Fragment>
        {this._renderImage()}
        {this._renderTitle()}
        {this._renderContent()}
      </Fragment>
    );
  }
}

export default SimpleBlurb;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment