Different City select field depending on selected country in WooCommerce

188 Views Asked by At

In checkout I've made separate city selects for few countries, (their value will go to hidden default text field billing_address_1). City selects I did myself, they are individual.

I also use the popular solution to update all fields using ajax. On change all works fine - when I change the country, all country selects are hidden, and the one city select for chosen country is visible, but it doesn't work on screen load. I'll show only on 2 countries, but there's more, the code is the same for others - simply more fields to hide.

add_action("wp_footer", "add_script_update_shipping_method");
function add_script_update_shipping_method()
{
    if (is_checkout()) { ?>

    <script>
    jQuery(document).ready(function($) {
        jQuery('select#billing_city_ua,select#billing_city_pl').select2();

        /* DIDN'T HELP

         jQuery(window).on('load', function () { 
         var countryval =  jQuery('#billing_country option:selected').val();                    
               jQuery('#billing_country').val(countryval);      
       });

        TO COPY ALL STRINGS FROM CHANGE TOO
       */

        $(document.body).on('updated_checkout updated_shipping_method', function(event, xhr, data) {

            $('input[name^="shipping_method"]').on('change', function() {
                $('.woocommerce-billing-fields__field-wrapper').block({
                    message: null,
                    overlayCSS: {
                        background: '#fff',
                        'z-index': 1000000,
                        opacity: 0.3
                    }
                });
            });


            jQuery('select#billing_country').on('change', function() {
                jQuery('select#billing_city_ua, select#billing_city_pl, p#billing_city_field input').val('');
                if (jQuery(this).val() == "UA") {
                    jQuery('select#billing_city_pl').addClass('d-none').removeClass('d-flex').closest('p').hide();
                    jQuery('select#billing_city_ua').addClass('d-flex').removeClass('d-none').closest('p').show();
                }
                if (jQuery(this).val() == "PL") {
                    jQuery('select#billing_city_ua').addClass('d-none').removeClass('d-flex').closest('p').hide();
                    jQuery('select#billing_city_pl').addClass('d-flex').removeClass('d-none').closest('p').show();
                }

            });


            jQuery("select#billing_city_ua").on("change", function(e) {
                let cityval = jQuery("select#billing_city_ua").val();
                jQuery(this).find('.woocommerce-billing-fields__field-wrapper p#billing_city_field>span>input').val(cityval);
            });
            jQuery("select#billing_city_pl").on("change", function(e) {
                let cityval = jQuery("select#billing_city_pl").val();
                jQuery(this).find('.woocommerce-billing-fields__field-wrapper p#billing_city_field>span>input').val(cityval);
            });



            var first_name = $('#billing_first_name').val(),
                last_name = $('#billing_last_name').val(),
                phone = $('#billing_phone').val(),
                email = $('#billing_email').val(),
                city = $('#billing_city').val(),
                cityua = $('#billing_city_ua').val(),
                citypl = $('#billing_city_pl').val(),
                address = $('#billing_address_1').val(),
                postcode = $('#billing_postcode').val(),
                country = $('#billing_country').val();

            $(".woocommerce-billing-fields__field-wrapper").html(xhr.fragments[".woocommerce-billing-fields"]);
            $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_first_name"]').val(first_name);
            $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_last_name"]').val(last_name);
            $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_phone"]').val(phone);
            $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_email"]').val(email);
            $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_city"]').val(city);
            $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_city_ua"]').val(cityua);
            $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_city_pl"]').val(citypl);
            $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_postcode"]').val(postcode);
            $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_country"]').val(country);
            $(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_address_1"]').val(address);
            $('.woocommerce-billing-fields__field-wrapper').unblock();
        });
    });
    </script>
    <?php }
}

I tried many tricks - to trigger change event on body change, even on hover or scroll just for test. All country selects are visible on page loading, and when you select the country, all works fine. I wanted to re-select already selected value on load - using cityval too. To copy all the code from on,change code before xhr function didn't help. What's the right way?

EDITED - ADDED THE CODE How I added city select fields

add_filter( 'woocommerce_checkout_fields' , 'dropdown' );

// Our hooked in function - $fields is passed via the filter!
function dropdown( $fields ) {
     $fields['billing']['billing_city_ua'] = array(
    'label'     => __('City', 'woocommerce'),
    'placeholder'   => _x('billing_city_ua', 'Select your city', 'woocommerce'),
    'required'  => false,
    'class'     => array('form-row-wide cities-select'),
    'clear'     => true,
    'type'      => 'select',
  'priority'   => 88,
     'options'     => array(
         '' => __('Select city', 'woocommerce' ),
        'City1' => __('City1', 'woocommerce' ),
        'City2' => __('City2', 'woocommerce' ),
        'City3' => __('City3', 'woocommerce' ),
        'City4' => __('City4', 'woocommerce' ),
        )
     ); 

   $fields['billing']['billing_city_pl'] = array(
    'label'     => __('City', 'woocommerce'),
    'placeholder'   => _x('billing_city_pl', 'Select your city', 'woocommerce'),
    'required'  => false,
    'class'     => array('form-row-wide cities-select'),
    'clear'     => true,
    'type'      => 'select',
  'priority'   => 88, 
        'options'     => array(
         '' => __('Select city', 'woocommerce' ),
        'City1' => __('City1', 'woocommerce' ),
        'City2' => __('City2', 'woocommerce' ),
        'City3' => __('City3', 'woocommerce' ),
        'City4' => __('City4', 'woocommerce' ),
        )
     );    


     return $fields;
}
1

There are 1 best solutions below

2
LoicTheAztec On BEST ANSWER

I have completely revisited your JavaScript code, and added some missing things.

Replace all your related code with the following:

// Add custom city select fields
add_filter( 'woocommerce_checkout_fields' , 'add_custom_checkout_city_dropdowns' );
function add_custom_checkout_city_dropdowns( $fields ) {
    $fields['billing']['billing_city']['class'][] = 'update_totals_on_change hidden'; 

    $fields['billing']['billing_city_ua'] = array(
        'label'         => __('City', 'woocommerce'),
        'placeholder'   => _x('billing_city_ua', 'Select your city', 'woocommerce'),
        'required'      => false,
        'class'         => array('form-row-wide cities-select update_totals_on_change hidden'),
        'clear'         => true,
        'type'          => 'select',
        'priority'      => 88,
        'options'       => array(
            ''          => __('Select city', 'woocommerce' ),
            'City1'     => __('City1', 'woocommerce' ),
            'City2'     => __('City2', 'woocommerce' ),
            'City3'     => __('City3', 'woocommerce' ),
            'City4'     => __('City4', 'woocommerce' ),
        ),
    );

    $fields['billing']['billing_city_pl'] = array(
        'label'         => __('City', 'woocommerce'),
        'placeholder'   => _x('billing_city_pl', 'Select your city', 'woocommerce'),
        'required'      => false,
        'class'         => array('form-row-wide cities-select update_totals_on_change hidden'),
        'clear'         => true,
        'type'          => 'select',
        'priority'      => 89, 
        'options'       => array(
            ''          => __('Select city', 'woocommerce' ),
            'City1'     => __('City1', 'woocommerce' ),
            'City2'     => __('City2', 'woocommerce' ),
            'City3'     => __('City3', 'woocommerce' ),
            'City4'     => __('City4', 'woocommerce' ),
        ),
    );
    return $fields;
}

// Custom inline CSS
add_action('wp_head', 'checkout_city_dropdowns_css');
function checkout_city_dropdowns_css() {
    if ( is_checkout() && ! is_wc_endpoint_url() ) : ?>
    <style>
        .cities-select.hidden {display:none;}
        #billing_city_field.hidden {display:none !important;}
    </style>
    <?php endif;
}

// PHP: Remove "(optional)" from some non required fields
add_filter( 'woocommerce_form_field' , 'remove_checkout_optional_fields_label', 10, 4 );
function remove_checkout_optional_fields_label( $field, $key, $args, $value ) {
    // Only on checkout page
    if( is_checkout() && ! is_wc_endpoint_url() && in_array($key, ['billing_city_ua', 'billing_city_pl']) ) {
        $optional = '&nbsp;<span class="optional">(' . esc_html__( 'optional', 'woocommerce' ) . ')</span>';
        $field = str_replace( $optional, '', $field );
    }
    return $field;
}

// JavaScript: Show/hide City Select fields
add_action( 'woocommerce_checkout_init' , 'checkout_city_dropdowns_js' );
function checkout_city_dropdowns_js() {
    $country  = WC()->customer->get_billing_country();
    $city     = WC()->customer->get_billing_city();

    wc_enqueue_js("
        const city   = '#billing_city',       cityUA  = '#billing_city_ua', 
            cityPL = '#billing_city_pl',    country = '#billing_country';

        var countryVal = '{$country}', 
            cityVal    = '{$city}';

        function showHideCityFields( city, cityUA, cityPL, cityVal, countryVal ) {
            if ( countryVal === 'UA' ) {
                $(city).val(cityVal).closest('p').addClass('hidden');
                $(cityUA).val('').closest('p').removeClass('hidden');
                $(cityPL).val(cityVal).closest('p').addClass('hidden');
            } else if ( countryVal === 'PL' ) {
                $(city).val(cityVal).closest('p').addClass('hidden');
                $(cityUA).val(cityVal).closest('p').addClass('hidden');
                $(cityPL).val('').closest('p').removeClass('hidden');
            } else {
                $(city).val(cityVal).closest('p').removeClass('hidden');
                $(cityPL).val('').closest('p').addClass('hidden');
                $(cityUA).val('').closest('p').addClass('hidden');
            }
        }

        // On Start: Get country value
        if( $(country).val() != '' && $(country).val() != undefined ) {
            countryVal = $(country).val();
        }

        // On Start: Get city value
        if( $(city).val() != '' && $(city).val() != undefined ) {
            cityVal = $(city).val();
            if ( countryVal === 'UA' ) {
                $(cityUA).val(cityVal).change();
            } else if ( countryVal === 'PL' ) {
                $(cityPL).val(cityVal).change();
            }
        }

        // On start: Enable WooCommerce Select2 for custom select fields
        $(cityUA+','+cityPL).selectWoo();

        // On start: Remove '(optional)' from label
        $(document.body).on('update_checkout', function(){
            $(cityUA+'_field label > .optional').remove();
            $(cityPL+'_field label > .optional').remove();
        });

        // On start: Show/Hide city fields
        showHideCityFields( city, cityUA, cityPL, cityVal, countryVal );

        // On Country change: Show/Hide city fields
        $('form.woocommerce-checkout').on('change', country, function() {
            showHideCityFields( city, cityUA, cityPL, '', $(this).val() );
        });

        // For country 'UA', on city change
        $('form.woocommerce-checkout').on('change', cityUA, function() {
            $(city).val($(this).val());
        });

        // For country 'PL', on city change
        $('form.woocommerce-checkout').on('change', cityPL, function() {
            $(city).val($(this).val());
        });
    ");
}

Code goes in functions.php file of your child theme (or in a plugin). It should work.