Skip to content

Dynamic Blocks

eswarsaladi edited this page Jan 5, 2023 · 1 revision

Dynamic Blocks

Description

Dynamic blocks are the components that change with user interaction or have a dependency on extenal library. These blocks cannot be created with the same way as the normal blocks using save.js.

Issue

The frontend of a wordpress block ( save.js ) can only handle pure components i.e there cannot be any usage of useEffect hooks in save.js, (hopefully they change this in future !)

To solve this issue two methods can be used

  1. Pass all the data in save.js using data attributes and writing javascript/jquery code that does changes to it
  2. Using render_callback function in PHP ( Recomended )

1. Passing data using data attributes

In the case of timeline-v2 block, the attributes that are needed to be passed to create a timeline are passed as data-attributes in div

// save.js

// ... 
export default function save({ attributes }) {
	const { files } = attributes;
	const blockProps = useBlockProps.save();
	return (
		<div {...blockProps}>
			{attributes.files.map((item, index) => (
				<div
					className="data-timeline-item"
					key={index}
					data-creator={item.creator}
					data-date={item.date}
					data-description={item.description.toString()}
					data-fileUrl={item.fileUrl}
					data-thumbnail={item.thumbnail}
					data-id={item.id}
				></div>
			))}
			<div
				id="timeline-embed"
				style={{ width: "600px", height: "600px" }}
			></div>
		</div>
	);
}

To handle this data attributes a seperate file is to be written either in javascript or jquery, the corresponding code for this component that handles the timeline object intitialisation is below

// frontend.js

// ...

// jquery code that checks for wp-block-drs-tk-timelinev2 class and create a timeline using the data from children with data-timeline-item data attributes
// wait untill the page is loaded
jQuery(document).ready(function ($) {
	$(".wp-block-drs-tk-timelinev2").each(function () {
		var $timeline = $(this);
		var files = [];

		// get all divs with data-timeline-item class
		$timeline.find(".data-timeline-item").each(function () {
			var $item = $(this);
			// get data from data attributes
			var file = {
				fileUrl: $item.data("fileurl"),
				creator: $item.data("creator"),
				date: $item.data("date"),
				description: $item.data("description"),
			};
			// push data to files array
			files.push(file);
		});

		//options
		var options = {
			width: "100%",
			height: "100%",
			// source: convertToTimelineJSON(files),
		};
		// check if timeline-embed div exists
		const timelineEmbed = $timeline.find("#timeline-embed");
		console.log(timelineEmbed);
		const timelineFiles = convertToTimelineJSON(files);
		console.log(timelineFiles);
		// createTimeline
		new TL.Timeline(timelineEmbed[0], timelineFiles, options);
	});
});

This frontend code is to be intialised in drs-tk.php as follows

// drs-tk.php

wp_register_script(
    'drs-tk-gallery-carousel-blocks-frontend',
    plugins_url('slick/js/frontend.js', _FILE_),
    ['jquery'],
    filemtime(plugin_dir_path(_FILE_) . 'slick/js/frontend.js'),
    true
);
wp_enqueue_script('drs-tk-gallery-carousel-blocks-frontend');

Pros

  1. Code looks clean
  2. It can be generalised for rest of the components

Cons

  1. One biggest draw back that I feel is creation of unnecessary divs, this can increase the size of the html file sent from wordpress server. ( Function can be written to remove the divs but that happens only after the dom is loaded so it does not provide any major improvement in the load times)

2. Using render_callback function in PHP ( Recomended )

In this the save.js is made to send nothing and everything is handled in PHP

//save.js

export default function save({ attributes }) {
    return ;
}

And in drs-tk.php the following changes are needed to be made

// drs-tk.php

// ...

function drs_tk_timeline_v2_init()
{
    // change the below line
    // register_block_type(__DIR__ . '/build/timeline_v2/block.json');
    register_block_type_from_metadata(
        __DIR__ . '/build/timeline_v2/block.json',
        ['render_callback' => 'render_timeline_call']
    );
}
add_action('init', 'drs_tk_timeline_v2_init');

function render_timeline_call($attributes)
{
    $file_attributes = json_encode($attributes);
    $content = '
        <div id="timeline-embed" style="width: 100%; height: 600px"></div>
        <script type="text/javascript">
            const attributes = ' . $file_attributes . ';
            function convertToTimelineJSON(files) {
            const timelineJSON = files.map((file) => {
                return {
                    media: {
                        url: file.fileUrl,
                        caption: "",
                        credit: file.creator,
                    },
                    start_date: {
                        year: file.date,
                    },
                    text: {
                        headline: "",
                        text: file.description.toString(),
                    },
                };
            });
        
            return {
                events: timelineJSON,
            };
        }
        const timeline = new TL.Timeline("timeline-embed",convertToTimelineJSON(attributes.files)) ;
        </script>
        ';
    return $content;
}

Pros

  1. The DOM would not be filled unnecessary extra elements

Cons

  1. Writting HTML Code inline is not that readable and sometime writing this way can prone some errors
  2. script tags will appear in between the HTML code.