Как загнать в dropdown данные полученные AJAXом?

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Аватара пользователя
wokster
Сообщения: 308
Зарегистрирован: 2013.09.06, 14:12
Контактная информация:

Как загнать в dropdown данные полученные AJAXом?

Сообщение wokster »

Интересует как правильнее в yii получить AJAXом данные в dropdown
есть урл:

Код: Выделить всё

https://api.vk.com/method/database.getCountries?need_all=1&count=1000 
это api контакта, он выдает список стран в json.
Не могу сообразить как их загнать в dropdown
Может спать пора, может мозг менять, так или иначе, буду благодарен за ссылочку или код.
Alex@
Сообщения: 568
Зарегистрирован: 2014.12.16, 09:24

Re: Как загнать в dropdown данные полученные AJAXом?

Сообщение Alex@ »

я на js написал событие

Код: Выделить всё

$('#idCountry').change(function() {....} 
загружаю регионы, дальше город
Аватара пользователя
wokster
Сообщения: 308
Зарегистрирован: 2013.09.06, 14:12
Контактная информация:

Re: Как загнать в dropdown данные полученные AJAXом?

Сообщение wokster »

Пробую передать это в select2 (http://demos.krajee.com/widget-details/select2)
Не понимаю как ему скормить результат AJAXa
Если изначально в виджет не передать данные, он выдает ошибку. То есть их надо как-то загнать в переменную до загрузки виджета?
Alex@
Сообщения: 568
Зарегистрирован: 2014.12.16, 09:24

Re: Как загнать в dropdown данные полученные AJAXом?

Сообщение Alex@ »

Блин я без виджета делал:
у меня сначала в форме изначально загружается в activedropDownList список стран,
список регионов и список городов пока пустой, после того как юзер выбирает из списка стран
на jQuery прописано событие $('#idCountry').change(function() {....} , которое посылает get запрос и возвращает результат в json формате и очищает список регионов (если он был не пуст) и заполняю теми регионами которые принадлежат выбранной стране, так же и с регионами.
Аватара пользователя
wokster
Сообщения: 308
Зарегистрирован: 2013.09.06, 14:12
Контактная информация:

Re: Как загнать в dropdown данные полученные AJAXом?

Сообщение wokster »

Alex@ писал(а):Блин я без виджета делал:
у меня сначала в форме изначально загружается в activedropDownList список стран,
список регионов и список городов пока пустой, после того как юзер выбирает из списка стран
на jQuery прописано событие $('#idCountry').change(function() {....} , которое посылает get запрос и возвращает результат в json формате и очищает список регионов (если он был не пуст) и заполняю теми регионами которые принадлежат выбранной стране, так же и с регионами.
Если стран 1000 - без автокомплита не обойтись
Последний раз редактировалось wokster 2015.02.03, 22:53, всего редактировалось 1 раз.
Аватара пользователя
wokster
Сообщения: 308
Зарегистрирован: 2013.09.06, 14:12
Контактная информация:

Re: Как загнать в dropdown данные полученные AJAXом?

Сообщение wokster »

Допустим есть виджет:

Код: Выделить всё

echo Select2::widget(
        [
            'name' => 'country',
            'value' => $model->country,
            'options' => ['multiple' => true],
            'pluginOptions' =>[
                'tags'=>['111','222','333']
            ]
        ]);
 
Как в 'tags' засунуть значения полученные через ajax?
Можно ли в контроллере сделать ajax запрос? Если да то как?
Последний раз редактировалось wokster 2015.02.03, 22:56, всего редактировалось 1 раз.
Alex@
Сообщения: 568
Зарегистрирован: 2014.12.16, 09:24

Re: Как загнать в dropdown данные полученные AJAXом?

Сообщение Alex@ »

я так понял что тебе нужен js - код для заполнения select?
Аватара пользователя
wokster
Сообщения: 308
Зарегистрирован: 2013.09.06, 14:12
Контактная информация:

Re: Как загнать в dropdown данные полученные AJAXом?

Сообщение wokster »

виджет создает структуру без select
Но есть в коде следующее:

Код: Выделить всё

<script type="text/javascript">var select2_e3a3cca2 = {"tags":["111","222","333"],"width":"resolve"};
</script>
и вот tags бы и изменить
Alex@
Сообщения: 568
Зарегистрирован: 2014.12.16, 09:24

Re: Как загнать в dropdown данные полученные AJAXом?

Сообщение Alex@ »

Код: Выделить всё

var TvojSelect = TvojSelect;
$.each(select2_e3a3cca2, function(i){
            TvojSelect.append('<option value="' + i + '">' + this + '</option>');
        });
        
что то вроде этого.....
Аватара пользователя
fdr
Сообщения: 35
Зарегистрирован: 2015.01.27, 15:52
Откуда: Yekaterinburg

Re: Как загнать в dropdown данные полученные AJAXом?

Сообщение fdr »

Рекомендую сначала добиться результата на чистом js в jsfiddle и ему подобным. Т.к. есть расхождения в версиях select2 плагина (документация не отображает действительность) и обёртка для Yii проглатывает все js ошибки.

Пример ajax поиска: http://embed.plnkr.co/FFJX0JTPAhhzmErqSVwA/preview. У вконтакта запрещены кроссдоменные запросы, поэтому использовал API яндекс карт. Код, разумеется сырой, но думаю понятно куда копать.
В виджете Yii есть опция "ajax", куда можно вставить почти весь код и он, вероятно, сработает.

Для "ajax запроса" в контроллере обычно используется php модуль curl.
german.igortcev
Сообщения: 251
Зарегистрирован: 2014.08.18, 14:01

Re: Как загнать в dropdown данные полученные AJAXом?

Сообщение german.igortcev »

Отправляешь данные на контроллер, обрабатываешь и возвращаешь новый список

Код: Выделить всё

return Html::activeDropDownList($model, 'group_id',
                ArrayHelper::map(Groups::find()->all(), 'id', 'title'),
                [
                    'prompt' => 'Нет',
                ]

            );

Код: Выделить всё

[
                'attribute' => 'group_id',
                'value' => function ($model, $key, $index, $column) {
                        //  var_dump($model); var_dump($key); exit;
                        return Html::activeDropDownList($model, 'group_id',
                            ArrayHelper::map(Groups::find()->all(), 'id', 'title'),
                            [
                                'prompt' => 'Нет',
                                'data-id' => $model->id,
                                'id' => "word-group_id-$model->id",
                                'onchange' => "

                                   $.ajax({
                                     url: \"/words/default/ajax\",
                                     type: \"post\",
                                     data: { word_id:  $key, group_id : $(\"#word-group_id-$model->id\").val()},
                                     success: function(response) {
                        тут заменяешь селект ответом от контроллера
                                           $('#word-group_id-$model->id').replaceWith(response);


                                        }

                                    });"
                            ]

                        );
                    },
                'format' => 'raw',
                'filter' => ArrayHelper::map(Groups::find()->all(), 'id', 'title')
            ],
прочитай разницу activeDropDownList и dropDownList

а вообще есть куча мануалов о зависимых dropdown , а если с городами, можешь подсмотреть у кого нибудь
Аватара пользователя
wokster
Сообщения: 308
Зарегистрирован: 2013.09.06, 14:12
Контактная информация:

Re: Как загнать в dropdown данные полученные AJAXом?

Сообщение wokster »

Частично решил вопрос с vk.api
в контроллере:

Код: Выделить всё

$lang = 0;
            $headerOptions = array(
                'http' => array(
                    'method' => "GET",
                    'header' => "Accept-language: en\r\n" .
                        "Cookie: remixlang=$lang\r\n"
                )
            );
            $methodUrl = 'http://api.vk.com/method/database.getCountries?v=5.5&need_all=1&count=1000';
            $streamContext = stream_context_create($headerOptions);
            $json = file_get_contents($methodUrl, false, $streamContext);
            $arr = json_decode($json, true);
            $arrforstr = [];
            foreach($arr['response']['items'] as $one)
            {
                $arrforstr[$one['id']] = $one['title'];
            }
            return $this->render('add', [
                 'arrforstr' => $arrforstr
            ]);
потом в вью:

Код: Выделить всё

use kartik\select2\Select2;
    echo Select2::widget(
        [
            'id' => 'country',
            'name' => 'country',
            'data' => $arrforstr,
            'options' => ['multiple' => false, 'placeholder' => 'Введите страну ...'],
            'pluginOptions' => [
                'allowClear' => true,
                ]
        ]);
Страны приходят и работает автокомплит, но теперь проблема с городами.
url для их получения должен содержать id страны и часть названия, пример:

Код: Выделить всё

$id = 1; //берем из выбранной страны, но не пойму как
$q = "моск"; //берем из второго виджета, но не пойму как
$url = 'http://api.vk.com/method/database.getCities?v=5.5&country_id='.$id.'&need_all=1&count=1000&q='.$q;
В доках к select2 нашел пример, но не могу в нем разобраться:

Код: Выделить всё

/*******
 * View
 ******/
 
// The controller action that will render the list
$url = \yii\helpers\Url::to(['city-list']);
 
// Script to initialize the selection based on the value of the select2 element
$initScript = <<< SCRIPT
function (element, callback) {
    var id=\$(element).val();
    if (id !== "") {
        \$.ajax("{$url}?id=" + id, {
            dataType: "json"
        }).done(function(data) { callback(data.results);});
    }
}
SCRIPT;
 
// The widget
echo $form->field($model, 'city')->widget(Select2::classname(), [
    'options' => ['placeholder' => 'Search for a city ...'],
    'pluginOptions' => [
        'allowClear' => true,
        'minimumInputLength' => 3,
        'ajax' => [
            'url' => $url,
            'dataType' => 'json',
            'data' => new JsExpression('function(term,page) { return {search:term}; }'),
            'results' => new JsExpression('function(data,page) { return {results:data.results}; }'),
        ],
        'initSelection' => new JsExpression($initScript)
    ],
]);
 
/*************
 * Controller
 ************/
public function actionCitylist($search = null, $id = null) {
    $out = ['more' => false];
    if (!is_null($search)) {
        $query = new Query;
        $query->select('id, name AS text')
            ->from('city')
            ->where('name LIKE "%' . $search .'%"')
            ->limit(20);
        $command = $query->createCommand();
        $data = $command->queryAll();
        $out['results'] = array_values($data);
    }
    elseif ($id > 0) {
        $out['results'] = ['id' => $id, 'text' => City::find($id)->name];
    }
    else {
        $out['results'] = ['id' => 0, 'text' => 'No matching records found'];
    }
    echo Json::encode($out);
}
Не понимаю как его изменить, что бы отправить запрос на мойурл и передать q, а так же не понимаю в каком формате ему нужно вернуть данные
Аватара пользователя
Insolita
Сообщения: 788
Зарегистрирован: 2011.06.06, 01:39
Контактная информация:

Re: Как загнать в dropdown данные полученные AJAXом?

Сообщение Insolita »

Код: Выделить всё

$query->select('id, name AS text')
            ->from('city')
            ->where('name LIKE "%' . $search .'%"')  //!!!! НИКОГДА ТАК НЕ ДЕЛАЙТЕ!!! Это уязвимость вашего сайта
            ->limit(20);

Код: Выделить всё

$query->select('id, name AS text')
            ->from('city')
            ->where(['like','name',$search])  //!!!! 
            ->limit(20);

Рабочий пример ajax AutoComplete

Код: Выделить всё

<?php
use \yii\helpers\Json;

            echo $form->field($model, 'streetid')->widget(\kartik\widgets\Select2::classname(), [
                    'language'=>'ru',
                    'options' => ['placeholder' => 'Улица ...'], 
                    'pluginOptions' => [
                        'minimumInputLength' => 3,
                        'allowClear' => false,
                        'ajax' => [
                            'url' => \yii\helpers\Url::toRoute(['/reference/street/list']),
                            'dataType' => 'json',
                            'data' => new \yii\web\JsExpression('function(term,page) { return {q:term}; }'),
                            'results' => new \yii\web\JsExpression('function(data,page) { return {results:data.results}; }'),
                        ],
                        /**
                           Вот это необходимо что при обновлении формы норм инициализировался выбранный элемент  - то что в доках - initScript 
                           По сути мы должны вернуть json - объект со стартовыми id и текст
                        **/
                        'initSelection' => new \yii\web\JsExpression('function(element,callback){
                            return callback('.
                               ($model->isNewRecord
                                  ? '{"id":null,"text":""}'
                                 :Json::encode(["id"=>$model->streetid,"text"=>$model->name."[".$model->city->name."]"])
                                                   ).')
                              }')
                    ],
                ]);?>
контроллер

Код: Выделить всё

 public function actionList($q){
        if(Yii::$app->request->isAjax){
            $model=new StreetSearch();
            Yii::$app->response->format=Response::FORMAT_JSON;
            return $model->ajsearch($q);
        }
        return false;
    }
 
модель

Код: Выделить всё

 public function ajsearch($q){
        $query = self::find()->active(true,'{{%street}}')->joinWith('city');
        $query->select(['{{%street}}.street_id','{{%street}}.name','{{%street}}.city_id','{{%city}}.name as gorod'])->active();
        if($q!='*'){
            $query->andFilterWhere(['like', '{{%street}}.name', $q]);
        }

        $query->orderBy('{{%street}}.name');
        $res=$query->all();
        $result=[];
        if(!empty($res)){
            foreach($res as $row){
                /**@var Street $row **/
                $result[]=['id'=>$row->street_id,'text'=>$row->name.($row->city_id?'['.$row->city->name.']':'')];
            }
        }
        $out=['more'=>false,'results'=>$result];
        return $out;
    }
 
Последний раз редактировалось Insolita 2015.02.04, 16:36, всего редактировалось 1 раз.
Аватара пользователя
denisOgr
Сообщения: 133
Зарегистрирован: 2012.02.02, 13:18
Контактная информация:

Re: Как загнать в dropdown данные полученные AJAXом?

Сообщение denisOgr »

А вы не хотите раз спарсить себе города и выбирать у себя с БД? Или там хорошая динамика изменений в этих данных?
Зачем дергать удаленный сервер каждый раз при выводе?

ПС Паша ушел. Контакт часто тормозит. Вам нужны иногда эти тормоза? А если API поменяют (я так понимаю это неофициальное API (могу ошибаться)) - вам нужно переделывать каждый dropdown?
Аватара пользователя
Insolita
Сообщения: 788
Зарегистрирован: 2011.06.06, 01:39
Контактная информация:

Re: Как загнать в dropdown данные полученные AJAXом?

Сообщение Insolita »

а если вам зависмые еще нужны http://demos.krajee.com/widget-details/ ... nced-usage вот пример зависмых select2
Аватара пользователя
Insolita
Сообщения: 788
Зарегистрирован: 2011.06.06, 01:39
Контактная информация:

Re: Как загнать в dropdown данные полученные AJAXом?

Сообщение Insolita »

denisOgr писал(а):А вы не хотите раз спарсить себе города и выбирать у себя с БД? Или там хорошая динамика изменений в этих данных?
Зачем дергать удаленный сервер каждый раз при выводе?

ПС Паша ушел. Контакт часто тормозит. Вам нужны иногда эти тормоза? А если API поменяют (я так понимаю это неофициальное API (могу ошибаться)) - вам нужно переделывать каждый dropdown?
+1
Аватара пользователя
wokster
Сообщения: 308
Зарегистрирован: 2013.09.06, 14:12
Контактная информация:

Re: Как загнать в dropdown данные полученные AJAXом?

Сообщение wokster »

denisOgr писал(а):А вы не хотите раз спарсить себе города и выбирать у себя с БД?
База страны и города = 85 мб. И перебирать ее у себя не быстрее чем юзать api. Тем более уже просто злость берет. Надо сделать и освободить мозг.
Аватара пользователя
wokster
Сообщения: 308
Зарегистрирован: 2013.09.06, 14:12
Контактная информация:

Re: Как загнать в dropdown данные полученные AJAXом?

Сообщение wokster »

Пока имею вот это:

Код: Выделить всё

<?php
//виджет Select2 с странами
    echo Select2::widget(
        [
            'id' => 'country',
            'name' => 'country',
            'data' => $arrforstr,
            'options' => ['multiple' => false, 'placeholder' => 'Введите страну ...'],
            'pluginOptions' => [
                'allowClear' => true,
                ]
        ]);
        //вставляем jquery будь он не ладен
    $this->registerJs("
    jQuery('#country').change(function(data){  //реагируем на событие change
                var data_id = data.added['id'];  //берем id страны, которая выбрана
    jQuery.ajax({
                      url: '".Url::to(['ajaxsity'])."',  //урл запроса
                      data: 'id=' + data_id + '&q=', //данные для запроса
                      success: function(data){ 
                      jQuery('#forsity').append(data);  //из контролера вернулся renderpartial запихалим его в заранее подготовленный див с id forsity
                    }});
    jQuery('#sity').select2(); 
    });

    ");
    ?>
Для счастья осталось настроить второй select2, но не могу понять как бы отловить то, что вбивает пользователь
Аватара пользователя
Insolita
Сообщения: 788
Зарегистрирован: 2011.06.06, 01:39
Контактная информация:

Re: Как загнать в dropdown данные полученные AJAXом?

Сообщение Insolita »

wokster писал(а):
denisOgr писал(а):А вы не хотите раз спарсить себе города и выбирать у себя с БД?
База страны и города = 85 мб. И перебирать ее у себя не быстрее чем юзать api. Тем более уже просто злость берет. Надо сделать и освободить мозг.
Ну как минимум страны выдрать - это один раз одним запросом их ксатит всего 234
это раз а во вторых - по апи тогда и валидировать геморройнее, а если не валидировать - ввести какой-нить id=100500 в обход select2 как два пальца...

но ладно... грузить аяксом каждый раз одну и ту же баз стран нет смысла...
поэтому это можно разово сделать
В контроллере

Код: Выделить всё

public function actionYourform()
    {
        if(!Yii::$app->request->isPost){
            $countries=$this->getVkCountries();
        } 
        $model new Model();
         if(Yii::$app->request->isPost){
         /// bla-bla
         }
          return $this->render('yourview',['countries'=>$countries, $model=>$model]);
    }

    public function actionVkcity()
    {
        $lang = 0;
        $headerOptions = array(
            'http' => array(
                'method' => "GET",
                'header' => "Accept-language: en\r\n" .
                    "Cookie: remixlang=$lang\r\n"
            )
        );
        $id = Yii::$app->request->get('countryid', null);
        $q = Yii::$app->request->get('q', null);
        if ($q && $id) {
            $methodUrl
                = 'http://api.vk.com/method/database.getCities?v=5.5&country_id=' . $id . '&need_all=1&count=1000&q='
                . $q;
            $streamContext = stream_context_create($headerOptions);
            $json = file_get_contents($methodUrl, false, $streamContext);
            $arr = json_decode($json, true);
            $result = [];
            foreach ($arr['response']['items'] as $one) {
                $result[] = ['id'=>$one['id'],'text'=>$one['title']];
            }
            Yii::$app->response->format = 'json';
            return $out=['more'=>false,'results'=>$result];
        }else{
           //корявый запрос
        }

    }
    
    function getVkCountries(){
    //можно закешить, читать из файла результат и т.п.
    $lang = 0;
$headerOptions = array(
            'http' => array(
                'method' => "GET",
                'header' => "Accept-language: en\r\n" .
                    "Cookie: remixlang=$lang\r\n"
            )
        );
        $methodUrl = 'http://api.vk.com/method/database.getCountries?v=5.5&need_all=1&count=1000';
        $streamContext = stream_context_create($headerOptions);
        $json = file_get_contents($methodUrl, false, $streamContext);
        $arr = json_decode($json, true);
        $arrforstr = [];
        foreach ($arr['response']['items'] as $one) {
            $arrforstr[$one['id']] = $one['title'];
        }
        return arrforstr;
}
 
во вьюхе

Код: Выделить всё

<?php
//виджет Select2 с странами
    echo Select2::widget(
        [
            'id' => 'country',
            'name' => 'country',
            'data' => $countries,
            'options' => ['multiple' => false, 'placeholder' => 'Введите страну ...'],
            'pluginOptions' => [
                'allowClear' => true,
                ], 
            'pluginEvents'=>[
                'change'=>'function(e){
                         $("#city").val(null).trigger("change");    //Сбрасываем выбор города при смене страны
                     }',
            ],
        ]);
        
        //виджет Select2 с городами
    echo Select2::widget(
        [
            'id' => 'city',
            'name' => 'city', 
            'options' => ['multiple' => false, 'placeholder' => 'Введите город ...'],
            'pluginOptions' => [
                'allowClear' => true,
                'ajax' => [
                    'url' => \yii\helpers\Url::toRoute(['/controller/vkcity']),
                    'dataType' => 'json',
                    'data' => new \yii\web\JsExpression('function(term,page) { var countryid=$("#country").val(); return {q:term,countryid:countryid}; }'),
                    'results' => new \yii\web\JsExpression(
                        'function(data,page) { return {results:data.results}; }'
                    ),
                ],
                ]
        ]);
Аватара пользователя
wokster
Сообщения: 308
Зарегистрирован: 2013.09.06, 14:12
Контактная информация:

Re: Как загнать в dropdown данные полученные AJAXом?

Сообщение wokster »

Кое-как решил. Похоже на Ваше решение, только замахался с корявыми доками Select2 и сделал выбор в пользу автокомплита от JqueryUI. Кстати вопрос не совсем в тему:
Есть ли разница несколько раз прописывать new \yii\web\JsExpression
или один раз прописать use \yii\web\JsExpression ?
Ответить