I am trying to dynamically load a ton of controls based on the value of a dropdown and I do not want to load them in via PHP because the initial page load takes well over 15 seconds. I have a lot of controls.
So what exactly are the requirements for instantiating settings and (custom) controls via JS?
Does my custom control class need to be registered and have the content_template method fleshed out? My class needs the “type” set right? (I’ve done both of these things)
Can I register the settings in PHP (this is what I’ve done) and then instantiate the controls via JS later or do I also need to instantiate the settings in JS?
What about the JS? Do I need to repeat my markup code in the JS instantiation?
This is what I have thus far, but nothing actually shows up.. I’m clearly missing some fundamental thing but I’m not sure what:
var api = wp.customize;
var sectionID = 'site_variables_section'; // section.id.replace( '[', '-' ).replace( ']', '' )
api.controlConstructor.ColorVariable = api.Control.extend({
ready: function() {
var control = this;
wp.customize.Control.prototype.ready.call( control );
}
});
// The control
var controlID = 'site_variables_section[' + field.name.replace('$', '') + ']';
var controlParams = {
section: sectionID,
label: 'label',
settings: {
'default': controlID
},
active: true,
type: 'ColorVariable',
};
var control = new api.controlConstructor.ColorVariable( controlID, controlParams );
control.active.validate = function() {
return true;
};
api.control.add( control.id, control );
@weston-ruter got me on the right track, but I’m still a little confused by a couple things:
1- How is the value being passed into the field/saved when there is no data-customize-setting-link? Is that handled by JS content_template
2- I see that when a single setting is used for a control, the “default” key in the settings param is used, but what about when multiple settings and how do we grab the values for each setting: default
for 2, I was able to get it working with:
settings:{
variable : controlId+'[variable]',
control: controlId+'[control]',
value: controlId+'[value]',
percentage: controlId+'[percentage]'
}
I’m thinking something like this in the to_json()
function:
$this->json['percentage_value'] = $this->value('my-settings-id);
If you register your settings on the server then you do not need to register them on the client in JS, as this will be done for you. Here are some examples creating controls after you have created the settings:
- Customize Posts CSS
- Customize REST Resources
- Customize Featured Content Demo
In the first example for Customize Posts CSS this actually uses a new Code Editor control that is brand new in WordPress 4.9. You can refer to this pull request to see what is required. In particular, make sure you call $wp_customize->register_control_type( 'My_Custom_Control' )
to ensure the content template is actually output.
When you are dynamically creating settings on the client, then you should instantiate them first in JS before you instantiate their related controls.
Here are some examples of creating settings:
- Customize Posts
- Customize Featured Content Demo
- Customize REST Resources
When you are dynamically creating settings and controls, the other key thing to implement is “dynamic settings” on the server. If you aren’t creating the setting on the server, then it won’t be recognized. This is what the customize_dynamic_setting_args
and customize_dynamic_setting_class
filters are for. Some examples:
- Core Customize Nav Menus
- Core Customize Widgets
- Customize Posts
- Customize Featured Content Demo
Note that the APIs for instantiating settings and controls will be getting better in core.
1- How is the value being passed into the field/saved when there is no data-customize-setting-link
? Is that handled by JS content_template
?
Then you have to then create the Element
link between the Setting
and the input
yourself manually, like this:
element = new wp.customize.Element( input );
element.sync( setting );
element.set( setting() );
You can see from core that the data-customize-setting-link
is just a shortcut to do that automatically for you.
2- I see that when a single setting is used for a control, the “default” key in the settings param is used, but what about when multiple settings and how do we grab the values for each setting: default
Yes, you define other non-default
keys. There are some key improvements coming in WordPress 4.9 to how this all works which will make it much easier to instantiate controls with JS. In addition to a data-customize-setting-link
attribute there is now support for a data-customize-setting-key-link
attribute, which allows you to use the actual key in the template as opposed to the setting ID. In addition, the settings
you pass can reference either setting IDs, or Setting
objects, or even just arbitrary Value
instances.
For example, consider a template added via:
add_action( 'customize_controls_print_footer_scripts', function() {
?>
<script id="tmpl-site-identity-control-content" type="text/html">
<# var elementIdPrefix = _.uniqueId( 'site-identity-' ); #>
<details>
<summary class="customize-control-title">{{ data.label }}</summary>
<ul>
<li>
<label for="{{ elementIdPrefix }}_title">Title:</label>
<input id="{{ elementIdPrefix }}_title" type="text" data-customize-setting-key-link="title">
</li>
<li>
<label for="{{ elementIdPrefix }}_tagline">Tagline:</label>
<input id="{{ elementIdPrefix }}_tagline" type="text" data-customize-setting-key-link="tagline">
</li>
<li>
<label for="{{ elementIdPrefix }}_founded">Year founded:</label>
<input for="{{ elementIdPrefix }}_founded" type="number" min="1" max="9999" data-customize-setting-key-link="founded">
</li>
</ul>
</details>
</script>
<?php
} );
You can add a control that uses this template simply be passing its ID as templateId
along with the desired settings
:
var foundedValue = new api.Value( 2017 );
var control = new api.Control( 'site_identity', {
templateId: 'site-identity-control-content',
label: 'Site Identity',
priority: 5,
section: sectionId,
settings: {
title: 'blogname', // Setting ID which may not be registered yet.
tagline: api( 'blogdescription' ), // Existing registered Setting object.
founded: foundedValue // Non-setting Value.
}
} );
api.control.add( control );
foundedValue.bind( function( newYear ) {
console.info( 'The year is now', newYear );
} );
And it then looks like this:

The founded year being a Value
is just for demonstration purposes. It being a Value
and not a registered Setting
means that it would not be saved in the database. But a Value like this could be useful for meta controls in a given theme, like picking from among preset color schemes. This is also how the changeset status and date get populated in WordPress 4.9.
Please watch make.wordpress.org/core for a dev note that dives into all of the specific improvements to the JS APIs in WordPress 4.9.