Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Advanced custom fields / populate select based on another acf field

regarding advanced custom fields repeater and select drop downs. i currently have a website which revolves around cars.

In an options page in wordpress, i have a repeater block with one text field named 'manufacturer' and another called 'models' as a text area field.

the text area is populated on each line with a model, so on one row for example:

manufacturer = audi models = A1 A2 A3 A4 etc..

on the custom post type 'lease' for the cars i have made 2 select drop downs for manufacturer and models.

I can auto-populate the manufacturer as per the example #2 code here:

http://www.advancedcustomfields.com/resources/dynamically-populate-a-select-fields-choices/

that works fine no problem!

then with the models drop down select, ONLY when the manufacturer select drop down changes to then auto populate it with the models from the repeater on the options page showing only the models based on the manufacturer...

Im aware this requires ajax to do so..

I have a sample code that i was testing the other week, i could get it to do what i wanted but when you go into the single car it would be blank, then when you select the options and then save/update the refreshed screen the models select drop down would be blank again, although with testing it shows that it has sent the data to the database and has saved.

so how do i fix the issue when i save the car post that it doesn't go blank?

here is the code i was testing

PHP

function acf_admin_enqueue( $hook ) {

$type = get_post_type(); // Check current post type
$types = array( 'lease' ); // Allowed post types

if( !in_array( $type, $types ) )
  return; // Only applies to post types in array

 wp_enqueue_script( 'populate-area', get_stylesheet_directory_uri() .    '/library/dist/js/acf_select.js' );

 wp_localize_script( 'populate-area', 'pa_vars', array(
    'pa_nonce' => wp_create_nonce( 'pa_nonce' ), // Create nonce which we    later will use to verify AJAX request
  )
  );
}

add_action( 'admin_enqueue_scripts', 'acf_admin_enqueue' );

// Return models by manufacturer
function model_by_manufacturer( $selected_manufacturer ) {

// Verify nonce
if( !isset( $_POST['pa_nonce'] ) || !wp_verify_nonce( $_POST['pa_nonce'], 'pa_nonce' ) )
die('Permission denied');

// Get manufacturer var
$selected_manufacturer = $_POST['manufacturer'];

// Get field from options page
$manufacturer_and_models = get_field('car_m_and_m', 'options');

// Simplify array to look like: manufacturer => models
  foreach ($manufacturer_and_models as $key => $value) {
    $manufacturer[$value['manufacturer']] = $value['models'];
  }

  // Returns model by manufacturer selected if selected manufacturer exists in array
  if (array_key_exists( $selected_manufacturer, $manufacturer)) {

  // Convert model to array
  $arr_data = explode( ', ', $manufacturer[$selected_manufacturer] );
return wp_send_json($arr_data);

} else {

$arr_data = array();
return wp_send_json($arr_data);
}

die();
}

add_action('wp_ajax_pa_add_areas', 'model_by_manufacturer');
add_action('wp_ajax_nopriv_pa_add_areas', 'model_by_manufacturer');

Javascript:

jQuery(document).ready(function($) {

/* Add default 'Select one'
$( '#acf-field_5548d019b55cb' ).prepend( $('<option></option>').val('0').html('Select Manufacturer').attr({ selected: 'selected', disabled: 'disabled'}) );
*/
/**
 * Get manufacturer option on select menu change
 *
 */
$( '#acf-field_5548d019b55cb' ).change(function () {

    var selected_manufacturer = ''; // Selected value

    // Get selected value
    $( '#acf-field_5548d019b55cb option:selected' ).each(function() {
        selected_manufacturer += $( this ).text();
    });

    $( '#acf-field_5548da7058203' ).attr( 'disabled', 'disabled' );

    // If default is not selected get models for selected manufacturer
    if( selected_manufacturer !== 'Select Manufacturer' ) {
        // Send AJAX request
        data = {
            action: 'pa_add_areas',
            pa_nonce: pa_vars.pa_nonce,
            manufacturer: selected_manufacturer,
        };

        // Get response and populate models select field
        $.post( ajaxurl, data, function(response) {

            if( response ){
                /* Disable 'Select model' field until country is selected
                $( '#acf-field_5548da7058203' ).html( $('<option></option>').val('0').html('Select Model').attr({ selected: 'selected', disabled: 'disabled'}) );
                */
                // Add models to select field options
                $.each(response, function(val, text) {
                    $( '#acf-field_5548da7058203' ).append( $('<option></option>').val(text).html(text) );
                });

                // Enable 'Select Model' field
                $( '#acf-field_5548da7058203' ).removeAttr( 'disabled' );
            }
        });
    }

}).change();
});

I assume it is something to do with the Javascript?

any advise or help would be appreciated,

thank you in advance.

R

like image 992
Richard Avatar asked May 26 '15 16:05

Richard


1 Answers

I know this question is a bit old but it has a few upvotes so I thought it would be worth posting an answer for this problem.

When you edit a 'lease' post, set the manufacturer then populate the model field from its value with AJAX, the models select box is populated with values for the chosen manufacturer. This value isn't validated server-side as a 'valid' choice, so it is saved in your database as post meta correctly.

On loading the edit screen again, the current value for model is not selected because when Advanced Custom Fields generates the HTML for a select field server-side, there are no choices to be pre-selected. The following PHP walker from fields/select.php is passed the fields $choices and $values for the field/post being edited:

function walk( $choices, $values ) {

    // bail ealry if no choices
    if( empty($choices) ) return;


    // loop
    foreach( $choices as $k => $v ) {

        // optgroup
        if( is_array($v) ){

            // optgroup
            echo '<optgroup label="' . esc_attr($k) . '">';


            // walk
            $this->walk( $v, $values );


            // close optgroup
            echo '</optgroup>';


            // break
            continue;

        }


        // vars
        $search = html_entity_decode($k);
        $pos = array_search($search, $values);
        $atts = array( 'value' => $k );


        // validate selected
        if( $pos !== false ) {

            $atts['selected'] = 'selected';
            $atts['data-i'] = $pos;

        }


        // option
        echo '<option ' . acf_esc_attr($atts) . '>' . $v . '</option>';

    }

}

As your field has no choices until AJAX uses the manufacturer field to populate them, the selected attribute isn't set. You should populate choices for the model field server-side too to retain the value on post edit screen after saving. For example:

function load_current_models($field){

  /** Get posts current manufacturer */
  $current_manufact = get_field('manufacturer');

  /** Manufacturer must be set */
  if($current_manufact) {

    /** Get manufacturers and models from options page */
    $all_models = get_field('car_m_and_m', 'options');

    /** Look for manufacturers models **/
    foreach($all_models as $manufacturer){
      if($manufacturer['manufacturer'] == $current_manufact){
        $field['choices'] = explode(', ', $model['models']);
        return $field;
      } 
    }
  }

  /** Disable models by default */
  $field['disabled'] = true;
  return $field;
}

add_filter('acf/load_field/key=field_5548da7058203', 'load_current_models');

This will also disable the model field on load if the manufacturer hasn't been set, I assume a new post, allowing you to remove the .change() call from your JS which is causing the manufacturer select changed event to be triggered on load too. This would append duplicate model options after those passed server-side otherwise.

You should update the JS to remove the 'old' options too on manufacturer change else if you selected a manufacturer then changed it to another, both manufacturers models would be included in the options. For example:

// Get models field jQuery object
var models = $('#acf-field_5548da7058203');

// Disable while waiting for server
models.prop('disabled', true);

// Remove old model field options
models.find('option').each(function(){
  if($(this).val() != 0) $(this).remove();
});

// Get response and populate models select field
$.post( ajaxurl, data, function(response) {
  if( response ){

    // Add models to select field options
    $.each(response, function(val, text) {
      models.append( $('<option></option>').val(text).html(text) );
    });

    // Enable 'Select Model' field
    models.removeAttr( 'disabled' );
  }
});
like image 78
Will Craig Avatar answered Oct 15 '22 21:10

Will Craig