Фильтрация второго выпадающего списка при выборе значения в первом

5 февр. 2015 г., 05:56:23
Просмотры: 13.9K
Голосов: 0

У меня есть таблица с двумя выпадающими списками. Первый - это бренд, который является пользовательским типом записи WordPress. Второй - это вкусы, которые также являются пользовательским типом записи. Бренд является родителем для вкусов. Когда я выбираю бренд, мне нужно обновить второй выпадающий список вкусами этого бренда (дочерними элементами).

Я создал шорткоды для своих списков, и они заполняются правильно. Но я не знаю, как отфильтровать второй выпадающий список при изменении первого.

Вот моя таблица:

<table id="fla_inf" width="100%">
<tbody>
<tr>
<th class="tab_header" colspan="6">Вкусы и добавки</th>
</tr>
<tr>
<th class="tab_header_nam">Бренд вкуса</th>
<th class="tab_header_nam">Название вкуса</th>
<th class="tab_header_nam">Тип дозатора</th>
<th class="tab_header_nam">Единица измерения</th>
<th class="tab_header_nam">Количество</th>
<th class="tab_header_nam">Добавить/Удалить строку</th>
</tr>
<tr class="flavors">
<td>[brand_list]</td>
<td>[flavor_list]</td>
<td><select id="dropper0" class="dropper">
<option selected="selected" value="type1">тип 1</option>
<option value="type2">тип 2-3</option>
</select></td>
<td><select id="qtyunit0" class="qtyunit">
<option value="ml">мл</option>
<option value="drops">капли</option>
<option selected="selected" value="perc">%</option>
</select></td>
<td><input id="quantity0" class="quantity" type="number" /></td>
<td><input class="addline" src="http://spitilab.com/wp-content/uploads/2015/01/add.png" type="image" /><input class="remline" src="http://spitilab.com/wp-content/uploads/2015/01/delete.png" type="image" /></td>
</tr>
</tbody>
</table>

Вот мои шорткоды:

function GetBrandList() {
    $brands = wp_dropdown_pages(array('id'=>'marque0','post_type' => 'marque-type','echo'=>0));
    return $brands;
} 

function GetFlavorList() {
   $flavors = wp_dropdown_pages(array('id'=>'arome0','post_type'=>'aromes-type','echo'=>0));
   return $flavors;
}   

add_shortcode('brand_list', 'GetBrandList');
add_shortcode('flavor_list', 'GetFlavorList');

Вот мои обновления:

Я добавил следующее в свой functions.php

add_action( 'wp_ajax_brand_children', 'GetBrandChildren');
add_action( 'wp_ajax_nopriv_brand_children', 'GetBrandChildren');

function GetBrandChildren($parent_id,$id) {
   $children = wp_dropdown_pages(array('id'=>'arome$id','post_type'=>'aromes-type','child_of'=>$parent_id,'echo'=>0));
   return $children;
}

и вот мой jQuery:

// При выборе бренда обновляем список вкусов
$(document).on('change', "select[id^='marque']", function() {

        var $brandid =  $(this).val();
        var $brand_dd_id = $(this).attr('id');
        var $flav_dd_id = $brand_dd_id.substr($brand_dd_id.length-1);
        $("#arome"+$flav_dd_id).empty();

        // Делаем AJAX запрос, используя выбранное значение как GET
        $.ajax({data: '{"parent_id":"' + $brandid + '","id":"'+ $flav_dd_id +'","action":"brand_children"}',
                success: function(output) {
                    alert(output);
                    $("#arome"+$flav_dd_id).html(output);
                }
         });

});

Однако мой выпадающий список вкусов остается пустым. В выводе, показанном в alert, возвращается вся HTML-страница.

ОБНОВЛЕНИЕ:

add_action( 'wp_ajax_brand_children', 'GetBrandChildren');
add_action( 'wp_ajax_nopriv_brand_children', 'GetBrandChildren');

function GetBrandChildren() {
   $parent_id = $_POST['brandid'];
   $id = $_POST['flav_dd_id'];
   echo wp_dropdown_pages(array("id"=>"arome$id",'post_type'=>'aromes-type','child_of'=>$parent_id,'echo'=>0));
  //ob_clean();
  //echo "working";
  wp_die();
}

// Нужны эти две строки, чтобы найти admin-ajax.php внутри jQuery
wp_enqueue_script( 'my-ajax-request', get_template_directory_uri() . '/js/ajax.js', array( 'jquery' ) );
wp_localize_script( 'my-ajax-request', 'MyAjax', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) ) );

jQuery:

// При выборе бренда обновляем список вкусов
$(document).on('change', "select[id^='marque']", function() {

        var $brandid =  $(this).val();
        var $brand_dd_id = $(this).attr('id');
        var $flav_dd_id = $brand_dd_id.substr($brand_dd_id.length-1);
        $("#arome"+$flav_dd_id).empty();

        // Делаем AJAX запрос, используя выбранное значение как GET
        //var ajax_url = admin_url('admin-ajax.php');
        $.ajax({
                url: MyAjax.ajaxurl,
                data: {
                        'parent_id': $brandid,
                        'id': $flav_dd_id,
                        'action': 'brand_children'
                      },
                success: function(output) {
                    console.log(output);
                    $("#arome"+$flav_dd_id).html(output);
                },
                error: function (xhr, ajaxOptions, thrownError) {
                    alert(xhr.status + " "+ thrownError);
        }});

});

Теперь мой второй выпадающий список обновляется.

0
Все ответы на вопрос 1
10

Вам нужно использовать Ajax, чтобы второй выпадающий список загружался после выбора первого. Я не совсем уверен насчёт некоторых моментов в вашем коде, поэтому просто напишу решение с нуля — возможно, вам придётся что-то подправить. Итак, первым делом нужно настроить Ajax-обработчик. Лучше всего это сделать в плагине. Функция is_admin() используется потому, что при отправке Ajax-запроса со страницы, содержащей выпадающий список, запрос будет выполняться в админке. Если кажется, что мы движемся в обратном порядке, это просто вопрос "что было раньше: запрос или обработчик". Обработчик, конечно же, поэтому начнём с него.

<?php
if ( is_admin() ) {
    // Добавляем 2 действия: первое для авторизованных пользователей, второе для гостей.
    add_action( 'wp_ajax_dynamic_dropdown', 'dynamic_dropdown_func' );
    add_action( 'wp_ajax_nopriv_dynamic_dropdown', 'dynamic_dropdown_func' );
}

// Эта функция обрабатывает Ajax-запрос и формирует ответ
function dynamic_dropdown_func () {
    global $wpdb;
    if (isset($_POST['value'])) {
        $selected_option = $_POST['value'];
        // Здесь будет второй выпадающий список, который вернётся в Ajax-ответе
        $args = array(
            'show_option_none' => __( 'Выберите другой вариант' ),
            'class' => 'dropper',
            'id' => 'dropper0',
            'orderby' => 'ID',
            'order' => 'ASC',
            'hide_empty' => 0,
            'hierarchical' => 1,
            'depth' => 1,
            // Это позволит получить дочерние элементы выбранного варианта
            'child_of' => (int)$selected_option,
            'echo' => 0,
            // Убедитесь, что заменили эту константу на вашу таксономию
            'taxonomy' => MY_CUSTOM_TAX,
        );

        $my_second_dropdown = wp_dropdown_categories( $args );
        // Если нужно выполнить preg_replace или str_replace, сделайте это перед ob_clean()
    }
    // ob_clean() предотвращает ошибки, выводы и другие данные, которые могли быть отправлены до этого момента
    ob_clean();
    return $my_second_dropdown;
    wp_die();
}

Теперь перейдём к фронтенд-части кода, которую увидит пользователь. Когда он инициирует Ajax-запрос, после получения ответа должен загрузиться второй выпадающий список. Обычно URL для Ajax и другие PHP-переменные передаются в JavaScript через локализацию скрипта, но мы немного схитрим и просто выведем скрипт на страницу. Убедитесь, что скрипт выводится в конце тега <body>, иначе придётся использовать обработчик document.ready (что в любом случае не помешает).

function my_frontend_javascript () {
    $ajax_url = admin_url( 'admin-ajax.php' );
    $javascript = "
        <script>
            var ajaxUrl = {$ajax_url},
            // Получаем наши выпадающие списки
                dropdownArome   = jQuery('#arome0'),
                dropdownDropper = jQuery('#dropper0');

            if (dropdownArome.length && dropdownDropper.length) {
                // Элементы существуют
                dropdownArome.on('change', function (e) {
                    var value = e.target.selectedOptions[0].value,
                        success,
                        data;
                    if (!!value) {
                        // Отправляем значение в Ajax-обработчик (обратите внимание, что 'action' совпадает с хуком, который мы использовали ранее)
                        data = {
                            'my_value' : value,
                            'action'   : 'dynamic_dropdown'
                        };
                        success = function ( response ) {
                            // В ответе будут наши <option>
                            dropdownDropper.html( response );
                        };
                        jQuery.post( ajaxUrl, data, success );
                    }
                });
            }
        </script>";
    return $javascript;
}

/** Здесь формируем страницу/форму/выпадающие списки */

$args = array(
    'show_option_none' => __( 'Выберите вариант' ),
    'class' => 'arome',
    'id' => 'arome0"',
    'orderby' => 'ID',
    'order' => 'ASC',
    'hide_empty' => 0,
    'hierarchical' => 1,
    'depth' => 1,
    'echo' => 0,
    // Убедитесь, что заменили эту константу
    'taxonomy' => MY_CUSTOM_TAX,
);

$my_first_dropdown = wp_dropdown_categories( $args );

// Выводим оба выпадающих списка и JavaScript из написанной выше функции
echo $my_first_dropdown;
echo '<br /><div id="dropper0-container"></div><br />';
echo my_frontend_javascript();

/** Готово! Как я уже говорил, возможно, вам придётся подправить некоторые параметры под ваш сайт. */

Вот так можно реализовать то, что вы задумали, причём одним из самых простых способов. Я говорю "простым", потому что мы не добавили валидацию, nonce-проверки и просто предположили, что всё пройдёт гладко. Рекомендую почитать про Ajax в WordPress — даже если вы уже что-то знаете, лишним не будет. Надеюсь, этот код поможет вам после небольшой доработки.

http://codex.wordpress.org/AJAX_in_Plugins

http://www.smashingmagazine.com/2011/10/18/how-to-use-ajax-in-wordpress/

5 февр. 2015 г. 07:37:12
Комментарии

Привет oneThingSimple,

Я не ожидал такого подробного ответа, большое спасибо за то, что уделили время и объяснили мне. Я совсем новичок в ajax, это фактически первый раз, когда мне нужно его использовать. Попробую адаптировать ваш ответ под свои нужды, это действительно помогает.

Еще раз спасибо.

Cyrille

user3515709 user3515709
5 февр. 2015 г. 17:10:40

@user3515709 Хорошо, без проблем. С Ajax поначалу может быть немного сложно работать. Тебе стоит ознакомиться с тем, как работает jQuery Ajax Object — это основной объект, который мы используем в WP для асинхронных запросов. Как разработчик могу сказать, что jQuery стоит того уже только из-за Ajax Object. Если понадобится помощь, можешь связаться со мной, постараюсь сделать процесс менее болезненным, так как поначалу это может быть сложно, пока не разберёшься во всём процессе, а потом станет так же просто, как и всё остальное. Так что если возникнут трудности — дай знать.

OnethingSimple OnethingSimple
5 февр. 2015 г. 18:36:14

Обновил код, у меня проблема с выводом. Наверное, где-то ошибся.

user3515709 user3515709
5 февр. 2015 г. 18:45:59

@user3515709 Я закончу работу через 4 часа. Тогда, если хочешь, чтобы я посмотрел код, я смогу сделать это вместе с тобой. Постараюсь помочь тебе заставить его работать. Либо в чате здесь на stackexchange, либо напиши мне на mike@stayshine.com, и мы сможем обсудить это вне форума. Удачи.

OnethingSimple OnethingSimple
5 февр. 2015 г. 19:36:13

До сих пор не получается. Думаю, проблема как в моем коде, так и в функции wp_dropdown_pages, потому что я создал шорткод для отображения дочерних страниц определенного родителя, а выпадающий список показывает: "Main Page (no parent)".

user3515709 user3515709
6 февр. 2015 г. 01:18:27

В твоей функции function GetBrandChildren($parent_id,$id) ты фактически не получаешь данные, отправленные в Ajax-запросе в качестве аргументов. Тебе нужно получить эти значения из массива $_POST. Нет необходимости пытаться вручную преобразовывать свой объект данных в строку. Просто используй обычный JavaScript Object. jQuery позаботится о сериализации аргументов. Также не забудь вывести Children с помощью echo, а не возвращать его. После того как ты выведешь его, используй wp_die(), чтобы страница была завершена и отправлена, и ничего лишнего не добавилось.

OnethingSimple OnethingSimple
6 февр. 2015 г. 01:59:30

спасибо за ваши рекомендации, я изменил код согласно вашим советам. Есть ли способ проверить, что возвращаемый вывод корректен? Когда я показываю вывод в alert, кажется, что возвращается вся страница

user3515709 user3515709
6 февр. 2015 г. 03:25:34

1 В вашем ajax-объекте отсутствует url, поэтому данные никуда не отправляются. Сначала попробуйте использовать console.log(msg) вместо alert(msg), не только для вашего спокойствия, но и потому что вывод в консоль дает лучший доступ для инспектирования, в то время как alert блокирует и показывает только текст. Простой способ убедиться, что обработчики ajax-действий вызываются - просто написать внутри функции-обработчика ob_clean();echo "I WORK!";wp_die();. Это очистит вывод других функций и вернет только "I WORK" в $.ajax success, если весь запрос настроен правильно.

OnethingSimple OnethingSimple
6 февр. 2015 г. 03:37:30

теперь это работает, выпадающий список функционирует, осталась только проблема с wp_dropdown_pages, который не фильтруется по parent id.

user3515709 user3515709
6 февр. 2015 г. 05:32:10

@user3515709 Вам следует привести $parent_id к целочисленному типу. Это будет выглядеть так: 'child_of' => (int)$parent_id, Я предпочитаю хранить свои $args в переменной, а не передавать их прямо в вызов функции, чтобы при отладке можно было использовать var_dump(), print_r(), var_export(). Теперь, когда у вас работает ajax, эти данные будут возвращаться в ответе, что поможет в отладке. Я ухожу на сегодня. Если у вас получится заставить это работать, отметьте мой ответ. Если вам понадобится дополнительная помощь, вы можете написать на email, который я оставил в комментариях, или мы можем продолжить обсуждение в чате завтра. Удачи

OnethingSimple OnethingSimple
6 февр. 2015 г. 05:52:32
Показать остальные 5 комментариев