Статьи

Программирование на Yii2: использование Ajax

Конечный продукт
Что вы будете создавать

Если вы спрашиваете: «Что такое Yii?», Ознакомьтесь с разделом Введение в Yii Framework , в котором рассматриваются преимущества Yii и содержится обзор Yii 2.0, выпущенного в октябре 2014 года.

В этой серии «Программирование с Yii2» я расскажу читателям, как использовать Yii2 Framework для PHP. В этом уроке мы рассмотрим реализацию интерактивных страниц с использованием Ajax. В частности, я собираюсь осветить использование Ajax в двух областях приложения Meeting Planner, о котором параллельно пишу серию Building Your Startup .

Сначала мы рассмотрим, как мы загружаем карту Google на страницу в ответ на вход пользователя в определенное место. Как показано ниже, после того, как я вхожу в Plum Bistro и нажимаю return, карта справа загружается динамически без обновления страницы.

Yii Ajax - пример Google Maps UX

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

Yii Ajax - Пример планировщика собраний UX

Ajax значительно упрощает и ускоряет процесс, позволяя пользователям перемещать несколько элементов управления переключателями, чтобы указать свои предпочтения без обновления страницы.

Просто напоминание, я участвую в комментариях ниже. Мне особенно интересно, если у вас есть другие подходы, дополнительные идеи или вы хотите предложить темы для будущих уроков. Если у вас есть вопрос или предложение по теме, пожалуйста, напишите ниже. Вы также можете связаться со мной через Twitter @reifman напрямую.

Yii Ajax - Википедия Ajax Flow против HTTP
Автор: DanielSHaischt, via Wikimedia Commons , CC BY-SA 3.0

Если вы только начинаете с Ajax и хотите начать медленно, на Yii Playground есть два простых примера Ajax, которые могут быть полезны для обзора. Один изменяет текст на странице через Ajax, а другой загружает ответ на форму на той же странице, причем оба без обновления, и каждый включает подробные примеры кода.

Yii Ajax - Yii Playground AJAX Примеры

Давайте углубимся в два наших основных примера. Все источники этих примеров можно найти в репозитории кода Планировщика собраний на GitHub .

Когда форма « Создать место» (/frontend/views/place/create_place_google.php) изначально загружается, она включает виджет поиска Google Places в реальном времени:

Yii Ajax - Создание места в Планировщике собраний с помощью Google Мест

Форма загружает библиотеку JavaScript Карт Google и связывает ее с полем ввода place-searchbox:

01
02
03
04
05
06
07
08
09
10
11
12
13
$gpJsLink= ‘https://maps.googleapis.com/maps/api/js?’
                          ‘key’ => Yii::$app->params[‘google_maps_key’],
                          ‘libraries’ => ‘places’,
                  ));
  echo $this->registerJsFile($gpJsLink);
 
  $options = ‘{«types»:[«establishment»],»componentRestrictions»:{«country»:»us»}}’;
  echo $this->registerJs(«(function(){
        var input = document.getElementById(‘place-searchbox’);
        var options = $options;
        searchbox = new google.maps.places.Autocomplete(input, options);
        setupListeners(‘place’);
})();» , \yii\web\View::POS_END );

Частичная форма _formPlaceGoogle.php включает в себя некоторые скрытые поля, в которых результаты карты могут быть сохранены до отправки всей страницы, а также скрытый div для отображения карты через Ajax.

01
02
03
04
05
06
07
08
09
10
11
12
13
use frontend\assets\MapAsset;
MapAsset::register($this);
    <?= BaseHtml::activeHiddenInput($model, ‘name’);
    <?= BaseHtml::activeHiddenInput($model, ‘google_place_id’);
    <?= BaseHtml::activeHiddenInput($model, ‘location’);
    <?= BaseHtml::activeHiddenInput($model, ‘website’);
    <?= BaseHtml::activeHiddenInput($model, ‘vicinity’);
    <?= BaseHtml::activeHiddenInput($model, ‘full_address’);
<div class=»col-md-6″>
  <article></article>
</div> <!— end col2 —>

В таблице Place of Planner Meeting хранятся имя Google, place_id, местоположение, веб-сайт, окрестности и полный адрес для использования в приложении.

MapAsset, включенный выше, загружает наш файл create_place.js, который работает между Google и нашей формой; он в основном управляет передачей и ответом данных через Ajax.

Я проведу вас через create_place.js по частям. Во-первых, есть setupListeners() , вызываемый родительской формой:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
function setupListeners(model) {
    // searchbox is the var for the google places object created on the page
    google.maps.event.addListener(searchbox, ‘place_changed’, function() {
      var place = searchbox.getPlace();
      if (!place.geometry) {
        // Inform the user that a place was not found and return.
        return;
      } else {
        // migrates JSON data from Google to hidden form fields
        populateResult(place,model);
      }
    });
    var place_input = document.getElementById(model+’-searchbox’);
    google.maps.event.addDomListener(place_input, ‘keydown’, function(e) {
        if (e.keyCode == 13) {
            e.preventDefault();
        }
      });
}

Когда пользователь начинает печатать, виджет опускает опции ввода текста для реальных мест, и событие place_changed обрабатывается при каждом нажатии клавиши. Слушатель keydown выше не дает ключу возврата (ASCII 13 или 0xD для вас, гиков), отправлять форму.

Вот как это выглядит при вводе. Я вхожу в Plum для Plum Bistro:

Yii Ajax - выпадающий список Google Мест

Если человек выбрал ввод или щелкнул место в раскрывающемся списке, вызывается populateResult() ; если нет, мы ничего не делаем.

01
02
03
04
05
06
07
08
09
10
11
function populateResult(place,model) {
  // moves JSON data retrieve from Google to hidden form fields
  // so Yii2 can post the data
    $(‘#’+model+’-location’).val(JSON.stringify(place[‘geometry’][‘location’]));
    $(‘#’+model+’-google_place_id’).val(place[‘place_id’]);
    $(‘#’+model+’-full_address’).val(place[‘formatted_address’]);
    $(‘#’+model+’-website’).val(place[‘website’]);
    $(‘#’+model+’-vicinity’).val(place[‘vicinity’]);
    $(‘#’+model+’-name’).val(place[‘name’]);
    loadMap(place[‘geometry’][‘location’],place[‘name’]);
}

Это заполняет все скрытые поля данными из Google и вызывает loadMap() для отображения карты:

Yii Ajax - Google Places Load Map через Ajax

Функция loadMap() очень специфична для API Google Place и отображает карту, loadMap() выше справа:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function loadMap(gps,name) {
  var gps_parse = gps.toString().replace(«(«, «»).replace(«)», «»).split(«, «);
  var gps_lat = parseFloat(gps_parse[0]);
  var gps_lng = parseFloat(gps_parse[1]);
 
  if (document.querySelector(‘article’).children.length==0) {
    var mapcanvas = document.createElement(‘div’);
    mapcanvas.id = ‘mapcanvas’;
    mapcanvas.style.height = ‘300px’;
    mapcanvas.style.width = ‘300px’;
    mapcanvas.style.border = ‘1px solid black’;
    document.querySelector(‘article’).appendChild(mapcanvas);
  }
  var latlng = new google.maps.LatLng(gps_lat,gps_lng);
 
  var myOptions = {
    zoom: 16,
    center: latlng,
    mapTypeControl: false,
    navigationControlOptions: {style: google.maps.NavigationControlStyle.SMALL},
    mapTypeId: google.maps.MapTypeId.ROADMAP
  };
 
  var map = new google.maps.Map(document.getElementById(«mapcanvas»), myOptions);
  var marker = new google.maps.Marker({
      position: latlng,
      map: map,
      title:name
  });
}

Пользовательский опыт быстрый и впечатляющий. Попробуй это !

Далее, давайте посмотрим, как мы записываем изменения в планы встреч в режиме реального времени. Здесь нет Google API; это больше ванильный AJAX в Yii Framework.

Когда люди добавляют даты, время и места в свои планы встреч, вы увидите страницу, подобную этой:

Yii Ajax - представление планирования встречи с помощью ползунков

Столбцы « Вы» и « Они» показывают предпочтения каждого участника в отношении мест и времени. Увеличенный слайдер выбора позволяет человеку принять окончательное решение о месте и времени встречи.

Есть много данных, которые нужно собрать от людей, и мы не хотим требовать обновления страницы при каждом изменении. Ajax является идеальным решением для этой проблемы.

Я пройдусь по коду для панели «Место встречи» выше. Панель Meeting-Time выше работает аналогично.

Из-за инфраструктуры MVC и моего желания повторно использовать партиалы кода, поток здесь может показаться сложным для отслеживания. Вспомогательные функции PHP и JavaScript иногда приходилось размещать в родительских файлах, а не в частностях, с которыми они были наиболее тесно связаны. Я постараюсь дать вам обзор в первую очередь. Я призываю вас сделать несколько проходов, читая это, чтобы полностью понять это. И снова, вы можете просматривать код через GitHub .

Подсказка: имейте в виду, что имена файлов для частичных имен обычно начинаются с подчеркивания.

  1. Страница планировщика собраний загружается по адресу /frontend/views/meeting/view.php. Этот файл также содержит вспомогательные функции JavaScript для управления состоянием кнопок, таких как « Отправить» и « Завершить» (т. Е. Может ли пользователь отправить это приглашение после этого изменения? С помощью Планировщика собраний, как правило, необходимо выбрать одно место и один раз, прежде чем его можно будет отправить). ) и отображение визуальных уведомлений о том, что изменения будут отправлены по электронной почте другим участникам, когда пользователь закончит.
  2. При отображении панели « Где» для мест загружается файл /frontend/views/meeting-place/_panel.php. Этот файл включает вспомогательные PHP-функции showOwnerStatus() и showParticipantStatus() , которые будут повторно использоваться его дочерним элементом _list.php. Но, самое главное, _panel.php включает методы JavaScript для события switchChange ползунка switchChange .
  3. Файл _panel.php использует _list.php для отображения каждой отдельной строки для каждого места. Этот файл будет отображать ползунки Bootstrap путем вызова функций showOwnerStatus() и showParticipantStatus() .
  4. Функции switchChange будут switchChange Ajax вызовы MeetingPlaceChoiceController.php.
  5. И, наконец, MeetingPlaceChoiceController.php вызывает модель MeetingPlaceChoice.php для записи изменений в базе данных.

Извините, размещение соответствующего кода сложное и разложенное.

Теперь я пошагово проведу вас по ключевым компонентам.

Вот Meeting / view.php рендеринг Meeting-Place / _panel.php. Здесь отображается частичное для строк возможных мест и выбора участников:

01
02
03
04
05
06
07
08
09
10
11
<?php
         // where
         if (!($model->meeting_type == \frontend\models\Meeting::TYPE_PHONE || $model->meeting_type == \frontend\models\Meeting::TYPE_VIDEO)) {
           echo $this->render(‘../meeting-place/_panel’, [
             ‘model’=>$model,
             ‘placeProvider’ => $placeProvider,
             ‘isOwner’ => $isOwner,
             ‘viewer’ => $viewer,
         ]);
         }
          ?>

Ниже приведен JavaScript, относящийся к действиям, которые отвечают на результаты Ajax, но не нужны непосредственно для Ajax. Вам не нужно понимать, что делают эти функции, чтобы понять этот пример Ajax, но я включил их, поскольку они вызываются в ответ на события Ajax.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?php
$script = <<< JS
var notifierOkay;
 
if ($(‘#notifierOkay’).val() == ‘on’) {
  notifierOkay = true;
} else {
  notifierOkay = false;
}
 
function displayNotifier(mode) {
  if (notifierOkay) {
    if (mode == ‘time’) {
      $(‘#notifierTime’).show();
    } else if (mode == ‘place’) {
       $(‘#notifierPlace’).show();
    } else {
      alert(«We\’ll automatically notify the organizer when you’re done making changes.»);
    }
    notifierOkay=false;
  }
}
 
function refreshSend() {
  $.ajax({
     url: ‘$urlPrefix/meeting/cansend’,
     data: {id: $model->id, ‘viewer_id’: $viewer},
     success: function(data) {
       if (data)
         $(‘#actionSend’).removeClass(«disabled»);
        else
        $(‘#actionSend’).addClass(«disabled»);
       return true;
     }
  });
}
 
function refreshFinalize() {
  $.ajax({
     url: ‘$urlPrefix/meeting/canfinalize’,
     data: {id: $model->id, ‘viewer_id’: $viewer},
     success: function(data) {
       if (data)
         $(‘#actionFinalize’).removeClass(«disabled»);
        else
        $(‘#actionFinalize’).addClass(«disabled»);
       return true;
     }
  });
}
 
JS;
$position = \yii\web\View::POS_READY;
$this->registerJs($script, $position);
?>

Здесь, в Meeting-Place / _panel.php, создается таблица с указанием мест и выборов, вызывающая _list.php:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
<table class=»table»>
    <thead>
    <tr class=»small-header»>
      <td></td>
      <td ><?=Yii::t(‘frontend’,’You’) ?></td>
      <td ><?=Yii::t(‘frontend’,’Them’) ?></td>
       <td >
         <?php
          if ($placeProvider->count>1 && ($isOwner || $model->meetingSettings[‘participant_choose_place’])) echo Yii::t(‘frontend’,’Choose’);
         ?></td>
   </tr>
   </thead>
   <?= ListView::widget([
          ‘dataProvider’ => $placeProvider,
          ‘itemOptions’ => [‘class’ => ‘item’],
          ‘layout’ => ‘{items}’,
          ‘itemView’ => ‘_list’,
          ‘viewParams’ => [‘placeCount’=>$placeProvider->count,’isOwner’=>$isOwner,’participant_choose_place’=>$model->meetingSettings[‘participant_choose_place’]],
      ]) ?>
 </table>
 <?php else: ?>
 <?php endif;

Что еще более важно, он также включает в себя приведенный ниже JavaScript, который мы используем для выполнения вызовов Ajax, когда пользователь перемещает переключатель, изменяя его состояние. Функции выбора соответствуют большему синему ползунку выбора, в то время как функции выбора соответствуют ползункам предпочтения.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
$script = <<< JS
placeCount = $placeProvider->count;
// allows user to set the final place
$(‘input[name=»place-chooser»]’).on(‘switchChange.bootstrapSwitch’, function(e, s) {
  // console.log(e.target.value);
  // turn on mpc for user
  $.ajax({
     url: ‘$urlPrefix/meeting-place/choose’,
     data: {id: $model->id, ‘val’: e.target.value},
     // e.target.value is selected MeetingPlaceChoice model
     success: function(data) {
       displayNotifier(‘place’);
       refreshSend();
       refreshFinalize();
       return true;
     }
  });
});
 
// users can say if a place is an option for them
$(‘input[name=»meeting-place-choice»]’).on(‘switchChange.bootstrapSwitch’, function(e, s) {
  //console.log(e.target.id,s);
  // set intval to pass via AJAX from boolean state
  if (s)
    state = 1;
  else
    state =0;
  $.ajax({
     url: ‘$urlPrefix/meeting-place-choice/set’,
     data: {id: e.target.id, ‘state’: state},
     success: function(data) {
       displayNotifier(‘place’);
       refreshSend();
       refreshFinalize();
       return true;
     }
  });
});
 
JS;
$position = \yii\web\View::POS_READY;
$this->registerJs($script, $position);
?>

Функции выше делают вызов actionSet() в MeetingPlaceChoiceController для ответа на изменение коммутатора с помощью Ajax-запросов:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
public function actionSet($id,$state)
   {
     Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
     // caution — incoming AJAX type issues with val
     $id=str_replace(‘mpc-‘,»,$id);
     //if (Yii::$app->user->getId()!=$mpc->user_id) return false;
     if (intval($state) == 0 or $state==’false’)
       $status = MeetingPlaceChoice::STATUS_NO;
     else
       $status = MeetingPlaceChoice::STATUS_YES;
     //$mpc->save();
     MeetingPlaceChoice::set($id,$status,Yii::$app->user->getId());
     return $id;
   }

Действия контроллера, которые отвечают через Ajax, должны иметь формат ответа JSON (таким образом, Yii знает, что они не предназначены для доставки HTML):

1
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

Вот метод MeetingPlaceChoice::set() , который записывает действия пользователя в базе данных и создает запись MeetingLog, которая отслеживает все изменения во время планирования.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
public static function set($id,$status,$user_id = 0,$bulkMode=false)
   {
     $mpc = MeetingPlaceChoice::findOne($id);
     if ($mpc->user_id==$user_id) {
       $mpc->status = $status;
       $mpc->save();
       if (!$bulkMode) {
         // log only when not in bulk mode ie accept all
         // see setAll for more details
         if ($status==MeetingPlaceChoice::STATUS_YES) {
           $command = MeetingLog::ACTION_ACCEPT_PLACE;
         } else {
           $command = MeetingLog::ACTION_REJECT_PLACE;
         }
         MeetingLog::add($mpc->meetingPlace->meeting_id,$command,$mpc->user_id,$mpc->meeting_place_id);
       }
       return $mpc->id;
     } else {
       return false;
     }
   }

В Meeting Planner я веду журнал каждого отдельного изменения. Это позволяет мне узнать, когда прошло несколько минут с момента последнего изменения человека, и уведомить других участников собрания. Я пытаюсь провести эксперимент с этим сервисом, вместо того, чтобы требовать, чтобы участники нажимали кнопку «Отправить» каждый раз, когда они хотят внести изменения.

Тем не менее, это требует обучения их, чтобы понять, что все в порядке, чтобы изменить его и оставить его, то есть закрыть окно браузера. Таким образом, функции displayNotifier() отображают некоторые флеш-оповещения, чтобы помочь с этим — в конечном счете, я их отполирую и удалю для опытных пользователей.

MeetingLog также позволяет мне генерировать текстовую сводку истории планирования собрания. Если вы хотите узнать больше об этом, я написал об этом в статье «Создание стартапа: Уведомление людей об изменениях в собраниях и доставка уведомлений» .

Я надеюсь, что эти примеры помогут вам понять основы Ajax в Yii. Если вы заинтересованы в более продвинутом Ajax, я планирую включить загруженные Ajax формы в серию Meeting Planner. И, по общему признанию, Ajax — это область, где сообщество Yii не поделилось множеством примеров. Как правило, Ajax работает в Yii так же, как и в PHP и других фреймворках, поэтому вы можете учиться на примерах из других сообществ фреймворков.

Следите за будущими уроками в нашей серии «Программирование с Yii2», поскольку мы продолжаем изучать различные аспекты фреймворка. Вы также можете ознакомиться с нашей серией «Построение стартапа с помощью PHP» , в которой используется расширенный шаблон Yii2 при создании реального приложения.

Если вы хотите узнать, когда появится следующий учебник по Yii2, следуйте за мной @reifman в Твиттере или зайдите на страницу моего инструктора . Моя страница инструктора будет включать все статьи из этой серии, как только они будут опубликованы.