2 Responding to follow-up questions
source | link

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.

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.

1
source | link

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.