3

The answer to the above question helped me, but I still have a problem: Show hide custom Woocommerce checkout field based on selected payment method

I want the Phone field (a field is required) to be displayed when the customer selects the cheque payment gateway, and the mobile field is not displayed and disabled if he selects other payment gateways.

// Conditional Show hide checkout fields based on chosen payment methods
add_action( 'wp_footer', 'conditionally_show_hide_billing_custom_field' );
function conditionally_show_hide_billing_custom_field(){
    // Only on checkout page
     if ( is_checkout() && ! is_wc_endpoint_url() ) :
    ?>
    <script>
        jQuery(function($){
            var a = 'input[name="payment_method"]',
                b = a + ':checked',
                c = '#billing_phone_field'; // The checkout field <p> container selector

            // Function that shows or hide checkout fields
            function showHide( selector = '', action = 'show' ){
                if( action == 'show' )
                    $(selector).show( 200, function(){
                        $(this).addClass("validate-required");
                    });
                else
                    $(selector).hide( 200, function(){
                        $(this).removeClass("validate-required");
                    });
                $(selector).removeClass("woocommerce-validated");
                $(selector).removeClass("woocommerce-invalid woocommerce-invalid-required-field");
            }

            // Initialising: Hide if choosen payment method is "cheque"
            if( $(b).val() !== 'cheque' )
                showHide( c, 'hide' );
            else
                showHide( c );

            // Live event (When payment method is changed): Show or Hide based on "cheque"
            $( 'form.checkout' ).on( 'change', a, function() {
                if( $(b).val() !== 'cheque' )
                    showHide( c, 'hide' );
                else
                    showHide( c );
            });
        });
    </script>
    <?php
    endif;
}

The problem is when I choose other payment gateways besides cheque, even though the Phone field is hidden, it is still validated and displays the error (Billing Phone is a required field) - and this field needs to be filled, I don't want this!

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
Adam Luper
  • 230
  • 9

2 Answers2

4

The following will do the job, adding a hidden field to handle the billing phone validation when the billing phone field is visible. When the billing phone field is hidden and empty, no validation error will be prompted, allowing placing the order silently:

// Your settings goes in here
function my_checkout_settings()
{
    return array(
        'payment_id'   => 'cheque', // The payment Id
        'field_key_id' => 'billing_phone', // The checkout field key ID
    );
}

// Add a hidden billing input field for phone validation purpose
add_action('woocommerce_billing_fields', 'add_an_hidden_billing_fields');
function add_an_hidden_billing_fields($billing_fields) {
    extract(my_checkout_settings()); // Load your settings

    // Add a hidden input field
    $billing_fields[$field_key_id . '_is_valid'] = array(
        'type' => 'hidden',
        'required' => false,
        'default' => '',
    );

    return $billing_fields;
}

// Disabling conditionnally the Billing phone validation
add_action('woocommerce_after_checkout_validation', 'disable_specific_checkout_field_validation_conditionally', 20, 2);
function disable_specific_checkout_field_validation_conditionally($data, $errors) {
    extract(my_checkout_settings()); // Load your settings

    $validation_key =  $field_key_id . '_is_valid'; // The key Id of the hidden field

    if (empty($data[$field_key_id]) && isset($data[$validation_key]) && $data[$validation_key]) {
        $errors->remove($field_key_id . '_required'); // Remove unwanted error for this field
    }
}

// Conditional Show hide billing phone checkout fields based on chosen payment methods
add_action('woocommerce_checkout_init', 'enqueue_checkout_custom_script');
function enqueue_checkout_custom_script() {
    extract(my_checkout_settings()); // Load your settings

    wc_enqueue_js("const a = 'input[name=payment_method]',
        b = a + ':checked',
        c = '#{$field_key_id}',
        d = c + '_field',
        v = c + '_is_valid',
        p = '{$payment_id}';
    
    function triggerShowHide(b, d, p, v) {
        $(b).val() !== p ? $(d).show(200) : $(d).hide(200);
        $(b).val() !== p ? $(v).val('') : $(v).val('1');
    }
    
    // On the first load
     triggerShowHide(b, d, p, v);
    
    // On payment method 'change' live event
    $('form.checkout').on('change', a, function() {
        triggerShowHide(b, d, p, v);
    });");
}

Code goes in functions.php file of your active child theme (or in a plugin). Tested and works.

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
0

You can use jquery .prop('disabled', true); and .prop('disabled', false); to the input selector. Try below code.

add_action( 'wp_footer', 'conditionally_show_hide_billing_custom_field' );
function conditionally_show_hide_billing_custom_field(){
    // Only on checkout page
    if ( is_checkout() && ! is_wc_endpoint_url() ) :
    ?>
    <script>
        jQuery(function($){
            var a = 'input[name="payment_method"]',
                b = a + ':checked',
                c = '#billing_phone_field'; // The checkout field <p> container selector

            // Function that shows or hides checkout fields
            function showHide( selector = '', action = 'show' ){
                if( action == 'show' ) {
                    $(selector).show( 200, function(){
                        $(this).addClass("validate-required");
                    });
                    $(selector + ' input').prop('disabled', false);
                } else {
                    $(selector).hide( 200, function(){
                        $(this).removeClass("validate-required");
                    });
                    $(selector + ' input').prop('disabled', true);
                }
                $(selector).removeClass("woocommerce-validated");
                $(selector).removeClass("woocommerce-invalid woocommerce-invalid-required-field");
            }

            // Initialising: Hide if chosen payment method is not "cheque"
            if( $(b).val() !== 'cheque' )
                showHide( c, 'hide' );
            else
                showHide( c );

            // Live event (When payment method is changed): Show or Hide based on "cheque"
            $( 'form.checkout' ).on( 'change', a, function() {
                if( $(b).val() !== 'cheque' )
                    showHide( c, 'hide' );
                else
                    showHide( c );
            });
        });
    </script>
    <?php
    endif;
}

To remove (Billing Phone is a required field)! when the payment gateway is not cheque then You can use woocommerce_checkout_fields hook.

add_filter( 'woocommerce_checkout_fields', 'conditionally_disable_phone_field_validation' );
function conditionally_disable_phone_field_validation( $fields ) {
    // Only on checkout page
    if ( is_checkout() && ! is_wc_endpoint_url() ) {
        $chosen_payment_method = WC()->session->get( 'chosen_payment_method' );

        // Hide and disable phone field if chosen payment method is not "cheque"
        if ( $chosen_payment_method !== 'cheque' ) {
            $fields['billing']['billing_phone']['required'] = false;
            $fields['billing']['billing_phone']['validate'] = array();
        }
    }

    return $fields;
}
Bhautik
  • 11,125
  • 3
  • 16
  • 38
  • Thank you for your answer, but it still does not work and displays the error message (Billing Phone is a required field)! – Adam Luper Jul 24 '23 at 07:57
  • 1
    Thank you again, I don't want the required phone field to be deleted and I want this field to always be required. Because if it is not of the required type, all these codes can be said to be ineffective, because when a field is not of the required type, whether it is filled or empty, it does not matter, this is not my goal. – Adam Luper Jul 24 '23 at 08:13