-
Notifications
You must be signed in to change notification settings - Fork 11
Dynamic Blocks
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.
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
- Pass all the data in save.js using data attributes and writing javascript/jquery code that does changes to it
- Using render_callback function in PHP ( Recomended )
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');
- Code looks clean
- It can be generalised for rest of the components
- 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)
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;
}
- The DOM would not be filled unnecessary extra elements
- Writting HTML Code inline is not that readable and sometime writing this way can prone some errors
- script tags will appear in between the HTML code.