Control: Select2 - 10quality/wpmvc-addon-administrator GitHub Wiki

Control used as a field type to display a Select2 control (<select>).

Select2

Content

Options

Control options

Control Data type Description
wide bool Flag that indicates if the input should display wider or small. Default: false
add_empty bool Flag that indicates if an empty option should be added. Default: false
empty_label string Empty option label. Default: ``
attributes array HTML attributes listed as an array. Select2 JS options can be set as a data attribute.

Supported HTML attributes

Attribute Data type Description
placeholder string String to display as input placeholder. Default: ``
multiple bool Flag that indicates if supports multiple selection. Default: false
data-allow-clear int See select2 configuration options. Default: 0
data-tags int See select2 configuration options. Default: 0
data-select-on-close int See select2 configuration options. Default: 0
data-scroll-after-select int See select2 configuration options. Default: 0
data-min-input-length int See select2 configuration options. Default: 0
data-max-input-length int See select2 configuration options. Default: 0
data-max-selection-length int See select2 configuration options. Default: 0
data-min-results-search int See select2 configuration options. Default: 0
data-container-class int See select2 configuration options. Default: ``
data-ajax string Remote data source URL. See select2 ajax options. Default: ``
data-ajax-data-type string See select2 ajax options. Default: json
data-ajax-delay int See select2 ajax options. Default: ``
data-ajax-cache int See select2 ajax options. Default: ``
data-ajax-request string Name of a window JS function that will process the request sent to the server. See select2 ajax options ajax.data. Default: Custom addon function
data-ajax-results string Name of a window JS function that will process the results received from the server. See select2 ajax options ajax.processResults. Default: Custom addon function
data-template-results string Name of a window JS function that will format the results. See select2 configuration options templateResult. Default: ``
data-template-selection string Name of a window JS function that will format the selection. See select2 configuration options templateSelection. Default: ``
data-sorter string Name of a window JS function that will sort results. See select2 configuration options sorter. Default: ``
data-tokenizer string Name of a window JS function that will tokenize. See select2 configuration options tokenizer. Default: ``
data-init-selection string Name of a window JS function that will process the initial selection. See select2 configuration options initSelection. Default: ``
data-matcher string Name of a window JS function that will match results. See select2 configuration options matcher. Default: ``

Single usage

// Namespace and use statement...

class Settings extends Model
{
    // Class properties...

    protected function init()
    {
        // Other properties...

        $this->tabs = [
            'tab_id' => [
                'fields' => [

                    'field_id' => [
                        'type' => 'select2',
                        'title' => __( 'Select 2', 'my-domain' ),
                        'description' => __( 'Replacement for regular select.' ),
                        'options' => [
                            'a' => __( 'Option A', 'my-domain' ),
                            'b' => __( 'Option B', 'my-domain' ),
                            'c' => __( 'Option C', 'my-domain' ),
                        ],
                        'control' => [
                            'wide' => true,
                        ],
                    ],

                    // Other fields...
                ],
            ],
        ];
    }
}

Multiple usage

Select2

// Namespace and use statement...

class Settings extends Model
{
    // Class properties...

    protected function init()
    {
        // Other properties...

        $this->tabs = [
            'tab_id' => [
                'fields' => [

                    'field_id' => [
                        'type' => 'select2',
                        'title' => __( 'Select 2 multiple', 'my-domain' ),
                        'description' => __( 'Multiple selection.' ),
                        'options' => [
                            'a' => __( 'Option A', 'my-domain' ),
                            'b' => __( 'Option B', 'my-domain' ),
                            'c' => __( 'Option C', 'my-domain' ),
                            'd' => __( 'Option D', 'my-domain' ),
                            'e' => __( 'Option E', 'my-domain' ),
                        ],
                        'control' => [
                            'wide' => true,
                            'attributes' => [
                                'placeholder' => __( 'Select an option...', 'my-domain' ),
                                'multiple' => true,
                                'data-allow-clear' => 1,
                            ]
                        ],
                    ],

                    // Other fields...
                ],
            ],
        ];
    }
}

Remote usage

Select2

Field ID

// Namespace and use statement...

class Settings extends Model
{
    // Class properties...

    protected function init()
    {
        // Other properties...

        $this->tabs = [
            'tab_id' => [
                'fields' => [

                    'field_id' => [
                        'type' => 'select2',
                        'title' => __( 'Select 2 remote', 'my-domain' ),
                        'description' => __( 'Use select 2 with a remote AJAX url. Example searches users.' ),
                        'control' => [
                            'wide' => true,
                            'attributes' => [
                                'data-ajax' => admin_url( '/admin-ajax.php?action=select2_users' ),
                            ]
                        ],
                    ],

                    // Other fields...
                ],
            ],
        ];
    }
}

Ajax endpoint

You will need to create an ajax endpoint handler in order to provide data to the select2.

Add a similar hook at your Main.php file:

$this->add_action( 'wp_ajax_select2_users', 'AjaxController@ajax_users' );

And inside your AjaxController place a similar code to:

/**
 * @hook wp_ajax_select2_users
 */
public function ajax_users()
{
    $response = new Response;
    $response->data = [];
    try {
        // Request
        $request = [
            'term' => Request::input( 'term' ),
        ];
        if ( empty( $request['term'] ) ) {
            $response->error( 'term', 'Empty search term.' );
        }
        if ( $response->passes ) {
            $response->data = QueryBuilder::create()
                ->select( 'users.ID as `id`' )
                ->select( 'users.user_email as `text`' )
                ->select( 'users.user_login as `username`' )
                ->from( 'users as users' )
                ->join( 'usermeta as name', [
                    [
                        'key_a' => 'name.user_id',
                        'key_b' => 'users.ID',
                    ],
                    [
                        'key' => 'name.meta_key',
                        'operator' => 'IN',
                        'value' => ['first_name', 'last_name'],
                    ],
                ] )
                ->keywords( $request['term'], ['users.user_login', 'name.meta_value'] )
                ->group_by( 'users.ID' )
                ->get( ARRAY_A );
            $response->success = true;
        }
    } catch ( Exception $e ) {
        Log::error( $e );
    }
    $response->json();
}

NOTE: The example above uses the QueryBuilder module class to query the database with the provided search term. Select2 needs to recieve at least an id and a text field on every results row.

Adding pagination

Change the response data structure to like this:

/**
 * @hook wp_ajax_select2_users
 */
public function ajax_users()
{
    $response = new Response;
    $response->data = [];
    try {
        // Request
        $request = [
            'term' => Request::input( 'term' ),
            'page' => Request::input( 'page', 1 ),
            'limit' => 10,
        ];
        // Some validations...
        if ( $response->passes ) {
            $response->data['results'] = QueryBuilder::create()
                // 
                ->limit( $request['limit'] )
                ->offset( $request['limit'] * ( $request['page'] - 1 ) )
                ->get( ARRAY_A );
            $response->data['pagination'] = count( $response->data['results'] ) === $request['limit'];
            $response->success = true;
        }
    } catch ( Exception $e ) {
        Log::error( $e );
    }
    $response->json();
}

Filter initial selection

Once saved, the data stored by the results' row property id. If no filter is added, the id value will be displayed when the settings page is rendered.

To fix this, use filter administrator_value_{field_id} to change what should be displayed instead, for example:

/**
 * @hook administrator_value_{field_id}
 */
public function field_id_value( $id )
{
    $user = get_user_by( 'id', $id );
    return $user->user_email;
}

Format results

Via HTML parsing

The addon comes with the global JS function select2_format_html that will convert the property text of every result's row into HTML. Usage example:

// Namespace and use statement...

class Settings extends Model
{
    // Class properties...

    protected function init()
    {
        // Other properties...

        $this->tabs = [
            'tab_id' => [
                'fields' => [

                    'field_id' => [
                        'type' => 'select2',
                        'title' => __( 'Select 2 remote', 'my-domain' ),
                        'control' => [
                            'wide' => true,
                            'attributes' => [
                                'data-ajax' => admin_url( '/admin-ajax.php?action=select2_users' ),
                                'data-template-results' => 'select2_format_html',
                            ]
                        ],
                    ],

                    // Other fields...
                ],
            ],
        ];
    }
}

Change the server response

An example on how to change the text returned by the response into HTML`:

/**
 * @hook wp_ajax_select2_users
 */
public function ajax_users()
{
    $response = new Response;
    $response->data = [];
    try {
        // ...
        if ( $response->passes ) {
            $view_engine = $this->view;
            $response->data = QueryBuilder::create()
                // ...
                ->get( ARRAY_A, function( $row ) use( &$view_engine ) {
                    $row['text'] = $view_engine->get( 'view.key', $row );
                    return $row;
                } );
            $response->success = true;
        }
    } catch ( Exception $e ) {
        Log::error( $e );
    }
    $response->json();
}

IMPORTANT: Take into consideration, sending HTML from the server is slower than doing it via Javascript.

Via Javascript

You can format and change the way results are presented by adding a custom window (global) JS function and a custom HTML template.

Create a JS script in your assets, something like this:

function format_user_results( row ) {
    if ( row.loading ) {
        return row.text;
    }
    var $template = jQuery( jQuery( '#select2-user' ).html() );
    for ( var prop in row ) {
        $template.find( '*[role="' + prop + '"]' ).html( row[prop] );
    }
    return $template;
}

Add the compiled script to your auto-enqueue configuration, like this:

return [
    // Other settings...
    'autoenqueue' => [
        'enabled'       => true,
        'assets'        => [
            [
                'asset' => 'js/admin.js',
                'id' => 'admin-settings',
                'dep' => ['jquery'],
                'footer' => true,
                'is_admin' => true,
                'enqueue' => false,
            ]
        ],
    ],
];

Enqueue the file using method enqueue() inside your settings model:

public function enqueue()
{
    wp_enqueue_script( 'admin-settings' );
}

Add a template view at the footer of the settings page, like this at Main.php:

$this->add_action( 'administrator_footer_{model id}', '[email protected]' );

And with a view that look like this:

<script id="select2-user" type="text/template">
    <div class="select2-result-user clearfix">
        <div class="user-email"><strong role="text"></strong></div>
        <div class="user-details"><small role="username"></small></div>
    </div>
</script>

Finally, indicate the name of the global function at the control options of the field:

// Namespace and use statement...

class Settings extends Model
{
    // Class properties...

    protected function init()
    {
        // Other properties...

        $this->tabs = [
            'tab_id' => [
                'fields' => [

                    'field_id' => [
                        'type' => 'select2',
                        'title' => __( 'Select 2 remote', 'my-domain' ),
                        'control' => [
                            'wide' => true,
                            'attributes' => [
                                'data-ajax' => admin_url( '/admin-ajax.php?action=select2_users' ),
                                'data-template-results' => 'format_user_results',
                            ]
                        ],
                    ],

                    // Other fields...
                ],
            ],
        ];
    }
}
⚠️ **GitHub.com Fallback** ⚠️