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);

  • i think that your issue its more the load of the 15 options, do you have addtional JS? because the customizer loads all the widgets etc, and i had more than 25 @_@ – David Lee Sep 20 '17 at 14:05
up vote 4 down vote accepted

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:

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:

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:

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:

site identity control

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.

  • Perfect, this got me going! I updated my question with a small follow up. – rugbert Oct 4 '17 at 14:02
  • @rugbert I updated my response to include answers to your follow-up questions. Watch for WordPress 4.9! – Weston Ruter Oct 13 '17 at 6:24
  • oh man this is great, thanks! I now have customizer loading in 10-20 fields when switching "tabs", values are passed in, and I have things saving (using obj.set instead of elm.sync) – rugbert Oct 23 '17 at 15:58

Your Answer

 
discard

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.