JS/Underscore-template-rendered Custom Customizer Controls in WordPress 4.1

The Customizer is a JavaScript-driven feature of WordPress core, but until recently, most of the APIs for extending it in themes and plugins were PHP-oriented. In WordPress 4.1, we’re introducing more complete JS models for the different UI objects that comprise the Customizer. In the process, all controls are now placed into the DOM with JavaScript, rather than being output directly in PHP.

At the same time, we’ve been working on issues of scalability and performance. In particular, bringing the navigation menu management experience into the Customizer has highlighted several areas with room for improvement. With menus, each menu item is a Customizer control with several fields, so a site with hundreds of menu items across different menus will end up sending a lot of repetitive HTML down from PHP, and we currently have to send the full markup for a menu item control down from the server when adding menu items in an Ajax call.

#29572 offered a solution to these challenges: an optional API that allows Customizer controls to be written as JavaScript templates. Rather than populating a control’s container with markup rendered on the server and obtained via an Ajax call, we can now use JS templates to render these controls on the client without any server-side call. In the future, new controls could be added dynamically (lazy-loaded, #28580) by leveraging the control-type templates already loaded in the Customizer.

In the remainder of this post, I’ll walk through how to use this API, its benefits, and example use-cases that are already benefiting WordPress core in 4.1.

Registered Control Types

In order to introduce a concept of having one template for multiple Customizer controls of the same type, we needed to introduce a way to register a type of control with the Customize Manager. Previously, custom control objects were only encountered when custom controls were added using WP_Customize_Manager::add_control(). But detecting added control types to render one template per type wouldn’t allow new controls to be created dynamically if no other instances of that type were loaded. So we’ve introduced WP_Customize_Manager::register_control_type(). Usage is simple:

add_action( 'customize_register', '29527_customize_register' );
function 29527_customize_register( $wp_customize ) {
	// Define a custom control class, WP_Customize_Custom_Control.
	// Register the class so that it's JS template is available in the Customizer.
	$wp_customize->register_control_type( 'WP_Customize_Custom_Control' );
}

All registered control types will have their templates printed to the Customizer by WP_Customize_Manager::print_control_templates().

Sending PHP Control Data to JavaScript

While Customizer control data has always been passed to the control JS models, and this has always been able to be extended, you’re much more likely to need to send data down when working with JS templates. Anything that you would want access to in render_content() in PHP will need to be exported to JavaScript to be accessible in your control template. WP_Customize_Control exports the following control class variables by default:

  • type
  • label
  • description
  • active (boolean state)

You can add additional parameters specific to your custom control by overriding WP_Customize_Control::to_json() in your custom control subclass. In most cases, you’ll want to call the parent class’ to_json method also, to ensure that all core variables are exported as well. Here’s an example from the core color control:

public function to_json() {
	parent::to_json();
	$this->json['statuses'] = $this->statuses;
	$this->json['defaultValue'] = $this->setting->default;
}

JS/Underscore Templating, + examples

Once you’ve registered your custom control class as a control type and exported any custom class variables, you can create the template that will render the control UI. You’ll override WP_Customize_Control::content_template() (empty by default) as a replacement for WP_Customize_Control::render_content(). Render content is still called, so be sure to override it with an empty function in your subclass as well.

Underscore-style custom control templates are very similar to PHP. As more and more of WordPress core becomes JavaScript-driven, these templates are becoming increasingly more common. Some sample template code in core can be found in media, revisions, the theme browser, and even in the Twenty Fifteen theme, where a JS template is used to both save the color scheme data and instantly preview color scheme changes in the Customizer. The best way to learn how these templates work is to study similar code in core and, accordingly, I’ll briefly explain an example here now.

class WP_Customize_Color_Control extends WP_Customize_Control {
	public $type = 'color';
// ...
	/**
	 * Render a JS template for the content of the color picker control.
	 */
	public function content_template() {
		?>
		<# var defaultValue = '';
		if ( data.defaultValue ) {
			if ( '#' !== data.defaultValue.substring( 0, 1 ) ) {
				defaultValue = '#' + data.defaultValue;
			} else {
				defaultValue = data.defaultValue;
			}
			defaultValue = ' data-default-color=' + defaultValue; // Quotes added automatically.
		} #>
		<label>
			<# if ( data.label ) { #>
				<span class="customize-control-title">{{{ data.label }}}</span>
			<# } #>
			<# if ( data.description ) { #>
				<span class="description customize-control-description">{{{ data.description }}}</span>
			<# } #>
			<div class="customize-control-content">
				<input class="color-picker-hex" type="text" maxlength="7" placeholder="<?php esc_attr_e( 'Hex Value' ); ?>" {{ defaultValue }} />
			</div>
		</label>
		<?php
	}
}

In the above template for the core custom color control, you can see that after the closing PHP tag, we have a JS template. <# #> notation is used around statements to be evaluated – in most cases, this is used for conditionals. All of the control instance data that we exported to JS is stored in the `data` object, and we can print a variable using double or triple brace notation {{ }}. As I said before, the best way to get the hang of writing controls like this is to read through existing examples. WP_Customize_Upload_Control was recently updated to leverage this API as well, integrating nicely with the way the media manager is implemented, and squeezing a ton of functionality out of a minimal amount of code. If you want some really good practice, try converting some of the other core controls to use this API – and submit patches to core too, of course!

Working with Controls in JavaScript

The Customizer has always had an API for working with controls in JavaScript. Now that the Customizer supports JS-rendered controls, this API will be even more useful, as you can do things like re-rendering the entire control if its data changes significantly (think media attachment previewing, for example), rather than doing direct DOM manipulation. Again, the core code is the best place to start getting a feel for this API, but it essentially works like subclasses do in PHP. See @westonruter‘s post for details on how this side of the API has evolved in 4.1, and take a look at the control-related models in wp-admin/js/customize-controls.js.

Putting the pieces together

Here’s a summary of what’s needed to leverage the new API in a custom Customizer control subclass:

  1. Make your render_content() function empty (but it does need to exist to override the default one).
  2. Create a new function, content_template(), and put the old contents of render_content() there.
  3. Add any custom class variables that are needed for the control to be exported to the JavaScript in the browser (the JSON data) by modifying the to_json() function (see WP_Customize_Color_Control for an example).
  4. Convert the PHP from render_content() into a JS template, using <# #> around JS statements to evaluate and {{ }} around variables to print. PHP class variables are available in the data object; for example, the label can be printed with {{ data.label }}.
  5. Register the custom control class/type. This critical step tells the Customizer to print the template for this control. This is distinct from just printing templates for all controls that were added because the ideas are that many instances of this control type could be rendered from one template, and that any registered control types would be available for dynamic control-creation in the future. Just do something like $wp_customize->register_control_type( 'WP_Customize_Color_Control' );.

The PHP-only parts of the API are still fully supported and perfectly fine to use. But, with WordPress 4.0’s decreased need for custom controls, and given our long term goals for making the Customizer more flexible for doing things like switching themes in the Customizer without a pageload, I strongly encourage using this new API for all custom Customizer controls where feasible.

#4-1, #customize, #dev-notes, #menu-customizer, #menus