Passing data from customize-controls.js to the customize-preview.js

All we need is an easy explanation of the problem, so here it is.

Is this the correct way to pass data from customize-controls.js to customize-preview.js?

( function( api ) {

api.controlConstructor['typography'] = api.Control.extend( {
    ready: function() {
        var control = this;

        function addGoogleFont(fontName) {
                var font = control.params.ogf_fonts[fontName];
                var weights = $.map(font.variants, function(value, key) {
                  return key;
                });
                var weightsURL = weights.join(',');
                var fontURL = font.family.replace(' ','+') + ':' + weightsURL;
                wp.customize.previewer.send( 'olympusFontURL', "<link href='https://fonts.googleapis.com/css?family=" + fontURL + "' rel='stylesheet' type='text/css'>" );
        }

        control.container.on( 'change', '.typography-font-family select',
            function() {
                var value = jQuery( this ).val();
                control.settings['family'].set( value );
                if( value != 'default' ) {

                    addGoogleFont( value );

                    var font = control.params.ogf_fonts[value];
                    var weightsSelect = jQuery( '.typography-font-weight select' );
                    var newWeights = font.variants;
                    weightsSelect.empty();
                    $.each( newWeights, function( key, val ) {
                        weightsSelect.append( $( "<option></option>" )
                             .attr( "value", key ).text( val ) );
                    });
                }
            }
        );

    }
} );

} )( wp.customize );

Also is this better than trying to .append() to the preview iframe directly in customize-controls.js?

Here is customize-preview.js for anyone interested:

jQuery( document ).ready( function() {

    wp.customize.bind( 'preview-ready', function() {
      wp.customize.preview.bind( 'olympusFontURL', function( url ) {
         console.log(url);
         $("head").append(url);
      } );
    } );

} ); // jQuery( document ).ready

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

Customizer Message Passing

What follows are some snippets that show the required order of operations for getting the message-passing handshakes to work. In your example code, the control is sending the message on change so it’s likely that this will not happen before the overall ready event and it should be ok.

Pane → Preview

/* Pane, enqueue w/ customize-controls dependency at customize_controls_enqueue_scripts */
wp.customize.bind( 'ready', function() {
  wp.customize.previewer.bind( 'ready', function() {
     wp.customize.previewer.send( 'greeting', 'Howdy, Preview!' );
  } );
} );

/* Preview, enqueue /w customize-preview dependency at wp_enqueue_scripts if is_customize_preview() */
wp.customize.bind( 'preview-ready', function() {
  wp.customize.preview.bind( 'greeting', function( message ) {
     console.info( Pane sent message:', message );
  } );
} );

Preview → Pane

/* Preview, enqueue /w customize-preview dependency at wp_enqueue_scripts if is_customize_preview() */
wp.customize.bind( 'preview-ready', function() {
  wp.customize.preview.bind( 'active', function() {
     wp.customize.preview.send( 'greeting', 'Howdy, Pane!' );
  } );
} );

/* Pane, enqueue w/ customize-controls dependency at customize_controls_enqueue_scripts */
wp.customize.bind( 'ready', function() {
  wp.customize.previewer.bind( 'greeting', function( message ) {
     console.info( 'Preview sent greeting:', message );
  } );
} );

Suggestion

Instead of adding a change event listener for the select element via jQuery, the select element should rather be linked with a setting which you listen to instead. So your ready method should really look more like this:

this.setting.bind( function( newFont ) {
    if ( 'default' !== newFont ) {
        addGoogleFont( newFont );
    }
} );

But this addGoogleFont is passing the entire link tag to the preview, in addition to the setting change being sent. Wouldn’t it be better to move all of the addGoogleFont logic into the preview itself? For example:

wp.customize( fontSettingId, function( setting ) {
    setting.bind( function( newFont ) {
        if ( 'default' !== newFont ) {
            addGoogleFont( newFont );
        }
    } );
} )

So then you just listen to the change to the font setting change in the preview, then construct the link tag to update in the preview there.

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply