Skip to content

Instantly share code, notes, and snippets.

@lots0logs
Forked from atanas-dev/dynamic-content-3ps-docs.md
Last active October 31, 2020 20:41
Show Gist options
  • Save lots0logs/904ce3971128e2c7ee80d7ed8ab32092 to your computer and use it in GitHub Desktop.
Save lots0logs/904ce3971128e2c7ee80d7ed8ab32092 to your computer and use it in GitHub Desktop.

Note: This tutorial series is intended for advanced users. At least a basic understanding of coding in PHP and JavaScript is required.

Divi module setting fields support the use of Dynamic Content, allowing you to build dynamic modules that display information pulled from the site's database.

Enable Dynamic Content

Dynamic content can be enabled for a module setting using the dynamic_content parameter in the setting's definition.

  • dynamic_content (string) — Type of content. Accepts text, image, or url.
<script src="https://gist.github.com/lots0logs/1a2d175ce90ee4110528891c304f5c5e.js?ts=4"></script>

Render Dynamic Content

Frontend

Rendering dynamic content on the frontend requires a minor adjustment to the module's render() method:

<script src="https://gist.github.com/lots0logs/89fb4046a93262d67f6d54173862d43b.js?ts=4"></script>

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

  1. Escapes html in the prop's value, unless $html is set to 'full'.
  2. Resolves dynamic content, if the field contains any.

New Divi Builder

Fields which have dynamic content support declared will have their values automatically available in an object passed to your React component using the dynamic prop:

<script src="https://gist.github.com/lots0logs/9b6bb0b3d494f4d0bdf957955d97cb26.js=2"></script>

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