• I’ve created a dynamic Gutenberg Block with <ServerSideRender /> and the Block is rendered using PHP. A cronjob imports customer reviews into a MySQL database every night. The Block displays those customer reviews inside a Slick Carousel.

    Everything is working great in the frontend but the Block won’t initialize the Slick Carousel in the editor (backend). It looks like no JavaScript is executed at all. I’ve stripped down my rendering PHP file to this:

    index.php

    /**
     * Server-side rendering of the dynamic 'mna/testimonial' block.
     */
    function render_block_mna_testimonial($attributes) {
      $foo = '<h1>I am confused</h1>';
      $bar = '<script type="text/javascript">alert("HEY HEY");</script>';
      return $foo.$bar;
    }
    
    /**
     * Registers the 'mna/testimonial' block on server.
     */
    function register_block_mna_testimonial() {
      register_block_type('mna/testimonial', array(
        'attributes' => array(
    		...
        ),
        'render_callback' => 'render_block_mna_testimonial',
      ));
    }
    add_action('init', 'register_block_mna_testimonial');

    Behaviour in the frontend: H1 is displayed, Alert is displayed

    Behaviour in the editor: H1 is displayed, Alert is not displayed

    I added setTimeout out of desperation <script type="text/javascript">setTimeout(function(){ alert("HEY HEY"); }, 10000);</script> and got the same result, still no alert.

    I tried using a customEvent as coderaaron described in this comment on GitHub: https://github.com/WordPress/gutenberg/issues/12603#issuecomment-513945557

    But since even the alert doesn’t work it won’t execute document.addEventListener either.

    Any ideas why the JS is not beeing executed in the editor?

Viewing 3 replies - 1 through 3 (of 3 total)
  • Moderator bcworkz

    (@bcworkz)

    @maaarsl — sorry, that other reply was a profile spammer. Their reply and your confused response have been removed from public view.

    Do you mean the JS for the carousel is not running? I suspect it’s not even loaded onto the page. JS code is often only loaded for front end requests. For back end screens, the script would need to be enqueued from a completely different action hook (admin_enqueue_scripts)

    Thread Starter maaarsl

    (@maaarsl)

    @bcworkz – thanks for your reply!

    I suspect it’s not even loaded onto the page.

    It’s loaded, it’s inside the DOM but it does not get executed. I’ll try to give a deeper insight of what I’ve built:

    index.php
    Both files (slick.css and slick.min.js) are loaded in the frontend and in the editor. I’ve checked this multiple times.

    
    /*
     * Enqueues 3rd party JavaScript and CSS.
     */
    function mna_gutenberg_enqueue_external_assets() {
    	wp_enqueue_style(
    		'slick-carousel-style', // Handle, should be unique.
    		plugins_url(trailingslashit(basename(__DIR__)).'external/slick-carousel/slick.css'),
    		array(), // Dependencies
    		filemtime(PLUGIN_DIR.'external/slick-carousel/slick.css') // Version
    	);
      
    	wp_enqueue_script(
    		'slick-carousel', // Handle, should be unique.
    		plugins_url(trailingslashit(basename(__DIR__)).'external/slick-carousel/slick.min.js'),
    		array('jquery'),
    		filemtime(PLUGIN_DIR.'external/slick-carousel/slick.min.js'), // Version
    		true // Load script in footer.
    	);
    }
    add_action('enqueue_block_assets', 'mna_gutenberg_enqueue_external_assets');
    

    index.js
    Registers a block category and all of my blocks.

    
    /**
     * WordPress dependencies
     */
    import { registerBlockType } from '@wordpress/blocks';
    
    /**
     * Register block category first.
     */
    import './utils/block-category';
    
    /**
     * Internal dependencies
     */
    import * as testimonial from './blocks/testimonial';
    import * as mediaTeaser from './blocks/media-teaser';
    
    /**
     * Function to register an individual block.
     * 
     * @param {Object} block The block to be registered.
     */
    const registerBlock = (block) => {
      if(!block) { return; }
    
      const {name, settings} = block;
      registerBlockType(name, settings);
    };
    
    /**
     * Function to register blocks.
     */
    export const registerMnaBlocks = () => {
      [
        testimonial,
        mediaTeaser
      ].forEach(registerBlock);
    };
    registerMnaBlocks();
    

    block/index.js
    Everything is pretty default here, except the save function, which returns null, because it’s a dymanic block with Server Side Rendering.

    
    import icons from '../../utils/icons';
    import edit from './edit';
    
    export const name = 'mna/testimonial';
    
    export const settings = {
    	title: __('Testimonial', 'mna-gutenberg-blocks'),
    	description: __('Pr?sentieren Sie, was Ihre Kunden zu Ihrem Produkt oder Ihrer Dienstleistung sagen.', 'mna-gutenberg-blocks'),
    	category: 'mna-gutenberg-category',
    	icon: icons.testimonial,
    	supports: {
    		align: true,
    		alignWide: true
    	},
    	edit,
    	save: ( props ) => {
    		return null; // This block is rendered server-side.
    	},
    };
    

    block/edit.js
    Contains the edit function of the block.

    
    /**
     * Block edit function
     */
    class TestimonialEdit extends Component {
      render () {
        const {
          attributes,
          setAttributes,
        } = this.props;
    
        return (
    		<>
    			<InspectorControls>
    				// I've removed all of the Inspector Controls here to make it easier to read.
    				// You can set autoPlay true/false, arrows on/off, animationSpeed etc. for the carousel here.
    			</InspectorControls>
    
    			<ServerSideRender
    				block="mna/testimonial"
    				attributes={ attributes }
    				/>
    		</>
    	);
      }
    }
    
    export default TestimonialEdit;
    

    block/index.php
    Contains the rendering PHP function and an inline JavaScript block for the initialization of the slick carousel.

    
    function render_block_mna_testimonial($attributes) {
      $root_id = uniqid('mna-');
      $output = '';
      $js_output = '';
    
      $review_rows = // I've removed the function which gets the reviews out of the db to make it easier to read. $review_rows is a php array with objects inside.
      
      if(!empty($review_rows)) {
        $output .= '<div class="mna-block-testimonial-carousel">';
        
        foreach($review_rows as $review_row) {
          $output .=  '<div class="mna-block-testimonial-slide">'
                  .     '<p class="mna-block-testimonial-text">'.$review_row->gr_text.'</p>'
                  .     '<p class="mna-block-testimonial-meta"><a href="'.$review_row->gr_author_url.'" target="_blank">'.$review_row->gr_author.'</a> am '.date('d.m.Y', $review_row->gr_time).' über Google</p>'
                  .   '</div>';
        }
    
        $output .=  '</div>';
    
        $js_output  = '<script type="text/javascript">'
    				.	'alert("This is just a test alert!");'
                    .   'jQuery(document).ready(function() {'
                    .     'jQuery("#'.$root_id.' .mna-block-testimonial-carousel").slick({'
                    .       'speed: '.$attributes['animationSpeed'].','
                    .       'autoplay: '.($attributes['autoplay'] ? 'true' : 'false').','
                    .       'autoplaySpeed: '.($attributes['autoplaySpeed']).','
                    .       'dots: '.($attributes['dots'] ? 'true' : 'false').','
                    .       'arrows: '.($attributes['arrows'] ? 'true' : 'false').','
                    .     '});'
                    .   '});'
                    . '</script>';
      }
    
      return '<div id="'.$root_id.'" class="mna-block-testimonial '.$css_class.'">'.$output.'</div>'.$js_output;
    }
    
    /**
     * Registers the 'mna/testimonial' block on server.
     */
    function register_block_mna_testimonial() {
      register_block_type('mna/testimonial', array(
        'attributes' => array(
    		// I've removed all of the attributes here to make it easier to read.
        ),
        'render_callback' => 'render_block_mna_testimonial',
      ));
    }
    add_action('init', 'register_block_mna_testimonial');
    

    $js_output is just a inline JavaScript block which is present in the DOM in both, frontend and editor. But it does work in the frontend only. In the frontend I get the alert and it will init the carousel. In the editor I’m not getting the alert and it doesn’t init the carousel.

    I know a inline JavaScript block isn’t a fancy solution at all, but I have no idea how to make it better, because I need the values from $attributes inside the PHP file to init the carousel.

    Moderator bcworkz

    (@bcworkz)

    My faulty diagnosis was a hunch while not really knowing much of anything about the situation. I’m not surprised to be wrong. Thanks for the detailed information. Sadly, I don’t have a solution. There’s apparently a disconnect between what’s happening on the edit page vs. what’s happening in the editor itself. Almost like it’s a different DOM, though that’s obviously not literally the problem.

    If the alert() doesn’t even execute, apparently inline script in block rendering isn’t feasible. We normally pass PHP values to JS when the script is enqueued with wp_localize_script(). That doesn’t help you since block attributes are unknown at that point. I’d think the carousel init script should be part of some block callback which is part of its registration settings. But I’m not up to speed with custom blocks, so I don’t really know what I’m talking about. All I can do is wish you luck.

Viewing 3 replies - 1 through 3 (of 3 total)
  • The topic ‘Dynamic Gutenberg Block not initializing Slick Carousel in Editor’ is closed to new replies.