if ( ! defined( 'ABSPATH' ) ) {
class WPForms_Field_Select extends WPForms_Field {
const CHOICES_VERSION = '10.2.0';
const STYLE_CLASSIC = 'classic';
const STYLE_MODERN = 'modern';
* Primary class constructor.
// Define field type information.
$this->name = esc_html__( 'Dropdown', 'wpforms-lite' );
$this->keywords = esc_html__( 'choice', 'wpforms-lite' );
$this->icon = 'fa-caret-square-o-down';
'label' => esc_html__( 'First Choice', 'wpforms-lite' ),
'label' => esc_html__( 'Second Choice', 'wpforms-lite' ),
'label' => esc_html__( 'Third Choice', 'wpforms-lite' ),
$this->default_settings = [
'choices' => $this->defaults,
// Define additional field properties.
add_filter( 'wpforms_field_properties_' . $this->type, [ $this, 'field_properties' ], 5, 3 );
// Form frontend CSS enqueues.
add_action( 'wpforms_frontend_css', [ $this, 'enqueue_frontend_css' ] );
// Form frontend JS enqueues.
add_action( 'wpforms_frontend_js', [ $this, 'enqueue_frontend_js' ] );
add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_block_editor_assets' ] );
* Define additional field properties.
* @param array $properties Field properties.
* @param array $field Field settings.
* @param array $form_data Form data and settings.
public function field_properties( $properties, $field, $form_data ) {
unset( $properties['inputs']['primary'] );
$form_id = absint( $form_data['id'] );
$field_id = wpforms_validate_field_id( $field['id'] );
$choices = $field['choices'];
$dynamic = wpforms_get_field_dynamic_choices( $field, $form_id, $form_data );
if ( $dynamic !== false ) {
$field['show_values'] = true;
// Set options container (<select>) properties.
$properties['input_container'] = [
'id' => "wpforms-{$form_id}-field_{$field_id}",
'name' => "wpforms[fields][{$field_id}]",
foreach ( $choices as $key => $choice ) {
// Used for dynamic choices.
$depth = isset( $choice['depth'] ) ? absint( $choice['depth'] ) : 1;
$properties['inputs'][ $key ] = [
'class' => [ "choice-{$key}", "depth-{$depth}" ],
'for' => "wpforms-{$form_id}-field_{$field_id}_{$key}",
'class' => [ 'wpforms-field-label-inline' ],
'text' => $choice['label'],
'name' => "wpforms[fields][{$field_id}]",
'value' => isset( $field['show_values'] ) ? $choice['value'] : $choice['label'],
'id' => "wpforms-{$form_id}-field_{$field_id}_{$key}",
'required' => ! empty( $field['required'] ) ? 'required' : '',
'default' => isset( $choice['default'] ),
// Add class that changes the field size.
if ( ! empty( $field['size'] ) ) {
$properties['input_container']['class'][] = 'wpforms-field-' . esc_attr( $field['size'] );
// Required class for pagebreak validation.
if ( ! empty( $field['required'] ) ) {
$properties['input_container']['class'][] = 'wpforms-field-required';
// Add additional class for container.
! empty( $field['style'] ) &&
in_array( $field['style'], [ self::STYLE_CLASSIC, self::STYLE_MODERN ], true )
$properties['container']['class'][] = "wpforms-field-select-style-{$field['style']}";
* Field options panel inside the builder.
* @param array $field Field settings.
public function field_options( $field ) {
$this->field_option( 'label', $field );
$this->field_option( 'choices', $field );
'value' => esc_html__( 'Generate Choices', 'wpforms-lite' ),
$this->field_option( 'description', $field );
$this->field_option( 'required', $field );
* Advanced field options.
// Show Values toggle option. This option will only show if already used
// or if manually enabled by a filter.
if ( ! empty( $field['show_values'] ) || wpforms_show_fields_options_setting() ) {
$show_values = $this->field_element(
'value' => isset( $field['show_values'] ) ? $field['show_values'] : '0',
'desc' => esc_html__( 'Show Values', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Check this option to manually set form field values.', 'wpforms-lite' ),
'content' => $show_values,
// Multiple options selection.
$fld = $this->field_element(
'value' => ! empty( $field['multiple'] ),
'desc' => esc_html__( 'Multiple Options Selection', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Allow users to select multiple choices in this field.', 'wpforms-lite' ) . '<br>' .
wp_kses( /* translators: %s - URL to WPForms.com doc article. */
esc_html__( 'For details, including how this looks and works for your site\'s visitors, please check out <a href="%s" target="_blank" rel="noopener noreferrer">our doc</a>.', 'wpforms-lite' ),
esc_url( wpforms_utm_link( 'https://wpforms.com/docs/how-to-allow-multiple-selections-to-a-dropdown-field-in-wpforms/', 'Field Options', 'Multiple Options Selection Documentation' ) )
$lbl = $this->field_element(
'value' => esc_html__( 'Style', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Classic style is the default one generated by your browser. Modern has a fresh look and displays all selected options in a single row.', 'wpforms-lite' ),
$fld = $this->field_element(
'value' => ! empty( $field['style'] ) ? $field['style'] : self::STYLE_CLASSIC,
self::STYLE_CLASSIC => esc_html__( 'Classic', 'wpforms-lite' ),
self::STYLE_MODERN => esc_html__( 'Modern', 'wpforms-lite' ),
'content' => $lbl . $fld,
$this->field_option( 'size', $field );
$this->field_option( 'placeholder', $field );
// Dynamic choice auto-populating toggle.
$this->field_option( 'dynamic_choices', $field );
// Dynamic choice source.
$this->field_option( 'dynamic_choices_source', $field );
$this->field_option( 'css', $field );
$this->field_option( 'label_hide', $field );
* Field preview inside the builder.
* @since 1.6.1 Added a `Modern` style select support.
* @param array $field Field settings.
public function field_preview( $field ) {
$this->field_preview_option( 'label', $field );
! empty( $field['style'] ) &&
self::STYLE_MODERN === $field['style']
$args['class'] = 'choicesjs-select';
$this->field_preview_option( 'choices', $field, $args );
$this->field_preview_option( 'description', $field );
* Field display on the form front-end and admin entry edit page.
* @since 1.5.0 Converted to a new format, where all the data are taken not from $deprecated, but field properties.
* @since 1.6.1 Added a multiple select support.
* @param array $field Field data and settings.
* @param array $deprecated Deprecated array of field attributes.
* @param array $form_data Form data and settings.
public function field_display( $field, $deprecated, $form_data ) {
$container = $field['properties']['input_container'];
$field_placeholder = ! empty( $field['placeholder'] ) ? $field['placeholder'] : '';
$is_multiple = ! empty( $field['multiple'] );
$is_modern = ! empty( $field['style'] ) && $field['style'] === self::STYLE_MODERN;
$choices = $field['properties']['inputs'];
// Do not display the field with empty choices on the frontend.
if ( ! $choices && ! is_admin() ) {
// Display a warning message on Entry Edit page.
if ( ! $choices && is_admin() ) {
$this->display_empty_dynamic_choices_message( $field );
if ( ! empty( $field['required'] ) ) {
$container['attr']['required'] = 'required';
// If it's a multiple select.
$container['attr']['multiple'] = 'multiple';
// Change a name attribute.
if ( ! empty( $container['attr']['name'] ) ) {
$container['attr']['name'] .= '[]';
// Add a class for Choices.js initialization.
$container['class'][] = 'choicesjs-select';
// Add a size-class to data attribute - it is used when Choices.js is initialized.
if ( ! empty( $field['size'] ) ) {
$container['data']['size-class'] = 'wpforms-field-row wpforms-field-' . sanitize_html_class( $field['size'] );
$container['data']['search-enabled'] = $this->is_choicesjs_search_enabled( count( $choices ) );
// Check to see if any of the options were selected by default.
foreach ( $choices as $choice ) {
if ( ! empty( $choice['default'] ) ) {
// Preselect default if no other choices were marked as default.
wpforms_html_attributes( $container['id'], $container['class'], $container['data'], $container['attr'] )
if ( ! empty( $field_placeholder ) || $is_modern ) {
'<option value="" class="placeholder" disabled %s>%s</option>',
selected( false, $has_default || $is_multiple, false ),
esc_html( $field_placeholder )
// Build the select options.
foreach ( $choices as $key => $choice ) {
$label = $this->get_choices_label( $choice['label']['text'] ?? '', $key, $field );
$value = isset( $choice['attr']['value'] ) && ! wpforms_is_empty_string( $choice['attr']['value'] ) ? $choice['attr']['value'] : $label;
'<option value="%s" %s>%s</option>',
selected( true, ! empty( $choice['default'] ), false ),