WordPress Customizer selective Refresh and Partials for Multiple Settings

There are several good references about how to set up the Customizer to avoid refreshing the entire page with each change. here, and there's some deeper explanation here. What's not described much is how to map several settings to a single area of the page (called a Partial).

This tutorial will go into updating Partials that use several settings. I assume you have already done the other tutorials.

I wanted to create a Partial that covered several settings because the Customizer automatically displays a "pencil" button next to editable settings. When you click on the pencil, the Customizer displays the control that alters the Partial.

That's fine for the logo, but I had created a settings group with several business address fields. Creating a separate Partial for each setting resulted in displaying one "pencil" button for each setting: ugly, and potentially confusing.

Partials

A Partial is chunk of HTML code that you get from the server and insert into the document. When you update a setting, the program can tell the server to save the setting, and get back a chunk of formatted HTML to insert into the page.

The first thing to know is that most of the tutorials, and most of your code, assumes a 1:1:1 mapping from the Customizer control to the Settings to the Partial.

For example, if I have a Setting called "business_identity_url", I can create a control called "business_identity_url", and a Partial called "business_identity_url", and the tutorials explain how these things are wired together. The rest of this tutorial assumes you have gone through these other tutorials -- I'm not going to list any of the boilerplate code that surrounds the example code.

What we want is a Partial that is associated with more than one setting. For example, suppose I have two settings, business_identity_name and business_identity_city:

    $wp_customize->add_setting('business_identity_name', array(
        'type' => 'option',
        'capability' => 'manage_options',
        'default' => '',
        'transport' => 'postMessage',
        'sanitize_callback' => 'sanitize_text_field'
    )); 

    $wp_customize->add_setting('business_identity_city', array(
        'type' => 'option',
        'capability' => 'manage_options',
        'default' => '',
        'transport' => 'postMessage',
        'sanitize_callback' => 'sanitize_text_field'
    )); 

These are both saved as independent options in the wp_options table. We want to use partial page refreshes, so we set the transport to "postMessage", otherwise, the entire page will refresh.

Next up, we add these to a Customizer section called business_identity.

    $wp_customize->add_control('business_identity_name', array(
        'type' => 'text',
        'priority' => 10,
        'section' => 'business_identity',
        'label' => __( 'Business Name' ),
        'input_attrs' => array(
            'placeholder' => ''
        )
    ));

    $wp_customize->add_control('business_identity_city', array(
        'type' => 'text',
        'priority' => 10,
        'section' => 'business_identity',
        'label' => __( 'City' ),
        'input_attrs' => array(
            'placeholder' => ''
        )
    ));

Then we create a Partial. Now, normally, I create the function to display the data first, but we'll get to that in a moment. The code to set up the Partial is:

$wp_customize->selective_refresh->add_partial( 'business_address', array(
    'selector' => '.business-address',
    'settings' => array( 'business_identity_name', ' 'business_identity_city' ),
    'container_inclusive' => false,
    'render_callback' => 'get_customize_partial_business_address',
    'fallback_refresh' => false,
) );

Other tutorials aren't getting into this, but the original blog post on Make.wordpress did. Typically, you see only one field name for "settings", but you can specify an array. You can set "settings" to an array of all the settings that affect the Partial.

The callback to render the settings into HTML is:

function get_customize_partial_business_address() {
    $name = get_option( 'business_identity_name' );
    $city = get_option( 'business_identity_city' );
    $out = implode(", ", array($name, $city));
    return $out;
}

Note that I also use this in my template, like this:

<?php echo get_customize_partial_business_address(); ?>

My plugin doesn't do it that way, exactly, but that's the idea. Define the display logic in one place, and use it via the regular templates and over the REST interface.

Also, you need to identify the DOM element that will receive the Partial. I used the class name "business-address".

<span class="business-address"><?php echo get_customize_partial_business_address(); ?></span>

By using class names, one partial can update more than one element. It's useful if you have the business name in more than one place.

Also, there's a question whether the partial should include the SPAN element. I didn't do it that way, but I think it's a valid way to proceed, especially if you re-use the code in several places, and need the HTML updated in all those places.

Finally, on the client side, you need a little Javascript glue code to inject the Partial into the page, using the identifier we specified. This code runs in Customizer page (not in the preview iframe).

    wp.customize('business_address', function(v) {
        v.bind( function( to ) {
            jQuery('.business-address').html( to );
        });
    });

When the settings are changed in the control, the customizer will post a request to the server to update the setting on the server. The server saves the value, then looks for a partial that's affected by the setting, and if it finds one, it runs the render_callback function specified in the add_partial() method call.

The render callback returns the formatted HTML (which in our case is just plain text). This is passed back to the client, along with the name of the partial, 'business_address'. The customizer client code finds the function registered to 'business_address' and calls it with the partial data. The function causes the partial to update the document.

It's an involved round-trip, but it only takes a fraction of a second, and you barely notice that it's interacting with the server.

Mini Rant

In more than one comment, I've read that people are upset that they need to do this round-trip. They'd rather have all the rendering done via JS and templates. Store the data on the server. Render the data via templates in Angular, React, Backbone/Underscore, Ember, whatever.

While it's a great idea, I don't think it's going to become that popular outside of web-browser-based applications. Google and Facebook both use HTML. While they are starting to add support for JS, I don't think it's going to be all that easy. To make your data visible to their platforms, you will need to jump through their hoops.

Google spiders index HTML pages. Bing, DDG, Baidu, Yandex all spider HTML. Google's new accelerated mobile pages also like HTML formatted in specific ways. Facebook's previews scrape the HTML in addition to the OG meta tags. In fact, the existence of all these meta tags for various website really forces you to create a "page" for each document you publish. Once you've eaten the cost of producing a page to hold the HEAD element... why stop there?

Not only that: even if you have a dynamic set of data... the search engines still prefer if you publish it like a page. Look at what the mobile apps do - they turn your mobile data into web pages, so search engines can find it. Whatever the search engines like is what users get, and because users primarily reach your site via search engines, we're going to see server-generated HTML for the forseeable future.