Статьи

Создание вашего стартапа: Ajax для встреч и мест

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

Это руководство является частью серии « Создай свой стартап с помощью PHP» на Envato Tuts +. В этой серии я проведу вас через запуск стартапа от концепции до реальности, используя мое приложение Meeting Planner в качестве примера из реальной жизни. На каждом этапе я буду публиковать код Планировщика собраний в качестве примеров с открытым исходным кодом, из которых вы можете извлечь уроки. Я также буду решать вопросы, связанные с бизнесом по мере их возникновения.

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

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

Я также собираюсь показать вам, как я использовал консоль разработчика браузера Chrome от Google, чтобы помочь мне определить поврежденные области, что может быть особенно сложно при работе с Ajax между PHP и JavaScript. Это как свет в конце туннеля тьмы.

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

Стартапы Ajax - Сломанные контроллеры начальной загрузки после Ajax

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

Напоминаем, что весь код для Meeting Planner написан на Yii2 Framework для PHP. Если вы хотите узнать больше о Yii2, ознакомьтесь с нашей параллельной серией Программирование с Yii2 .

Для начала давайте рассмотрим добавление участников собрания через Ajax.

Стартапы Ajax - Панель участников, загруженная через Ajax

Код для добавления участников аналогичен тому, что мы рассмотрели ранее. Но я хочу рассмотреть немного другой код, который обновляет список участников и все кнопки, представляющие их личности.

Раньше на совещании был только один участник. Затем я включил групповые собрания и создал список кнопок для обозначения каждого участника:

Стартапы Ajax - Обновление списка кнопок участников через Ajax

Каждый раз, когда добавляется участник, я обновляю весь список через Ajax.

Вот функция jQuery addParticipant() которая вызывает getParticipantButtons() после добавления каждого нового:

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
function addParticipant(id) {
  // ajax add participant
  // adding someone from new_email
  new_email = $(‘#new_email’).val();
  friend_id = $(‘#participant-email’).val();
  friend_email = $(‘#participant-email :selected’).text();
  // adding from friends
  if (new_email!=» && (friend_id !== undefined && friend_id!=»)) {
      displayAlert(‘participantMessage’,’participantMessageOnlyOne’);
      return false;
  } else if (new_email!=» && new_email!==undefined) {
    add_email = new_email;
  } else if (friend_id!=») {
    add_email = friend_email;
  } else {
    displayAlert(‘participantMessage’,’participantMessageNoEmail’);
    return false;
  }
    $.ajax({
     url: $(‘#url_prefix’).val()+’/participant/add’,
     data: {
       id: id,
       add_email:add_email,
      },
     success: function(data) {
       // see remove below
       // to do — display acknowledgement
       // update participant buttons — id = meeting_id
       // hide panel
       $(‘#addParticipantPanel’).addClass(«hidden»);
       if (data === false) {
         // show error, hide tell
         displayAlert(‘participantMessage’,’participantMessageError’);
         return false;
       } else {
         // clear form
         $(‘#new_email’).val(»);
         // odd issue with resetting the combo box
         $(«#participant-email:selected»).removeAttr(«selected»);
         $(«#participant-email»).val(»);
         $(«#participant-emailundefined»).val(»);
        // show tell, hide error
         getParticipantButtons(id);
         displayAlert(‘participantMessage’,’participantMessageTell’);
         refreshSend();
         refreshFinalize();
         return true;
      }
     }
  });
}

Вот getParticipantButtons() :

01
02
03
04
05
06
07
08
09
10
11
12
function getParticipantButtons(id) {
  $.ajax({
   url: $(‘#url_prefix’).val()+’/participant/getbuttons’,
   data: {
     id: id,
    },
    type: ‘GET’,
   success: function(data) {
     $(‘#participantButtons’).html(data);
   },
 });
}

Он выполняет Ajax-вызов метода actionGetbuttons() :

01
02
03
04
05
06
07
08
09
10
11
12
public function actionGetbuttons($id) {
     $m=Meeting::findOne($id);
     $participantProvider = new ActiveDataProvider([
         ‘query’ => Participant::find()->where([‘meeting_id’=>$id]),
         ‘sort’=> [‘defaultOrder’ => [‘participant_type’=>SORT_DESC,’status’=>SORT_ASC]],
     ]);
     $result = $this->renderPartial(‘_buttons’, [
         ‘model’=>$m,
         ‘participantProvider’ => $participantProvider,
     ]);
     return $result;
   }

Примечание: вместо ParticipantController мне нравится кратко говорить «ParCon», потому что это звучит как удаленная командная база Star Trek — или указывает, что я слишком долго работал над этим запуском. Я определенно провел слишком много поздних ночей, работая над функцией Ajax.

В любом случае, вышеперечисленные функции снова заполняют панель всеми обновленными участниками.

Теперь давайте перейдем к датам и времени, которые зависят от одного из часто используемых виджетов Bootstrap Date Time .

Стартапы Ajax - панель даты и времени, загруженная через Ajax

И даты, и время, и места оказались самыми сложными особенностями для аяксификации. Это был не столько виджет Bootstrap или Google Maps API, который использовался в формах. Это оказались контроллеры Bootstrap Switch — они не были хорошо спроектированы или документированы для Ajax.

Вот пример сломанных переключателей после отправки Ajax при добавлении места:

Стартапы Ajax - переключатели начальной загрузки для сломанных мест

Я расскажу, как в конечном итоге это исправить, но сначала давайте посмотрим на meeting-time / panel.php:

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<?php
use yii\helpers\Html;
use yii\widgets\ListView;
use yii\bootstrap\Collapse;
use \kartik\switchinput\SwitchInput;
?>
<div id=»notifierTime» class=»alert-info alert fade in» style=»display:none;»>
<button type=»button» class=»close» data-dismiss=»alert» aria-hidden=»true»>&times;</button>
<?php echo Yii::t(‘frontend’,»We’ll automatically notify the organizer when you’re done making changes.»);
</div>
<div class=»panel panel-default»>
  <!— Default panel contents —>
  <div class=»panel-heading» role=»tab» id=»headingWhen»>
    <div class=»row»><div class=»col-lg-10 col-md-10 col-xs-10″><h4 class=»meeting-view»>
      <a role=»button» data-toggle=»collapse» data-parent=»#accordion» href=»#collapseWhen» aria-expanded=»true» aria-controls=»collapseWhen»><?= Yii::t(‘frontend’,’When’) ?></a>
    </h4>
    <span class=»hint-text»>
      <?php if ($timeProvider->count<=1) { ?>
        <?= Yii::t(‘frontend’,’add one or more dates and times for participants to choose from’) ?>
    <?php } elseif ($timeProvider->count>1) { ?>
      <?= Yii::t(‘frontend’,’are listed times okay?’);
    <?php
      }
    ?>
    <?php if ($timeProvider->count>1 && ($model->isOrganizer() || $model->meetingSettings[‘participant_choose_date_time’])) { ?>
      <?= Yii::t(‘frontend’,’you can also choose the time’) ?>
    <?php }?>
  
    <?php
      if ($model->isOrganizer() || $model->meetingSettings->participant_add_date_time) { ?>
        <?= Html::a(», ‘javascript:void(0);’, [‘class’ => ‘btn btn-primary glyphicon glyphicon-plus’,’title’=>’Add possible times’,’id’=>’buttonTime’,’onclick’=>’showTime();’]);
        <?php
      }
    ?>
      </div>
    </div>
  </div> <!— end row —>
</div> <!— end heading —>
  <div id=»collapseWhen» class=»panel-collapse collapse in» role=»tabpanel» aria-labelledby=»headingWhen»>
    <div class=»panel-when»>
      <div class=»when-form hidden»>
        <div id=»timeMessage» class=»alert-info alert fade in hidden»>
          <button type=»button» class=»close» data-dismiss=»alert» aria-hidden=»true»>&times;</button>
          <span id=»timeMsg1″><?= Yii::t(‘frontend’,’We\’ll automatically notify others when you\’re done making changes.’)?>
          <span id=»timeMsg2″><?= Yii::t(‘frontend’,’Please pick a date and time.’)?>
        </div>
        <div id=»addTime» class=»hidden»>
          <!— hidden add time form —>
          <?= $this->render(‘_form’, [
              ‘model’ => $meetingTime,
          ]) ?>
        </div>
      </div>
    <table class=»table» id=»meeting-time-list» class=»hidden»>
  <?php
   if ($timeProvider->count>0):
  ?>
  <!— Table —>
    <?= ListView::widget([
           ‘dataProvider’ => $timeProvider,
           ‘itemOptions’ => [‘class’ => ‘item’],
           ‘layout’ => ‘{items}’,
           ‘itemView’ => ‘_list’,
           ‘viewParams’ => [‘timezone’=>$timezone,’timeCount’=>$timeProvider->count,’isOwner’=>$isOwner,’participant_choose_date_time’=>$model->meetingSettings[‘participant_choose_date_time’],’whenStatus’=>$whenStatus],
       ]) ?>
  <?php endif;
  </table>
  </div>
</div>
</div>

Вы заметите timeMessage которое предоставляет предварительно загруженные оповещения для отображения при определенных условиях Ajax, как я рассмотрел в прошлом эпизоде. И, в основном, код начинает следовать тем же форматам, которые я использовал на других панелях.

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

Вот панель переключения JavaScript в Meeting.js:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
//show the panel
function showTime() {
  if ($(‘#addTime’).hasClass( «hidden»)) {
    $(‘#addTime’).removeClass(«hidden»);
    $(‘.when-form’).removeClass(«hidden»);
  }else {
    $(‘#addTime’).addClass(«hidden»);
    $(‘.when-form’).addClass(«hidden»);
  }
};
 
function cancelTime() {
  $(‘#addTime’).addClass(«hidden»);
  $(‘.when-form’).addClass(«hidden»);
}

Когда появляется панель и пользователь отправляет новую дату, он вызывает addTime() :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function addTime(id) {
   start_time = $(‘#meetingtime-start_time’).val();
   start = $(‘#meetingtime-start’).val();
   if (start_time ==» || start==») {
     displayAlert(‘timeMessage’,’timeMsg2′);
     return false;
   }
   // ajax submit subject and message
   $.ajax({
      url: $(‘#url_prefix’).val()+’/meeting-time/add’,
      data: {
        id: id,
       start_time: encodeURIComponent(start_time),
       start:encodeURIComponent(start),
     },
      success: function(data) {
        //$(‘#meeting-note’).val(»);
        insertTime(id);
        displayAlert(‘timeMessage’,’timeMsg1′);
        return true;
      }
   });
   $(‘#addTime’).addClass(‘hidden’);
 }

Это вызывает функцию actionAdd() MeetingTimeController.php actionAdd() :

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
public function actionAdd($id,$start,$start_time) {
     Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
     $timezone = MiscHelpers::fetchUserTimezone(Yii::$app->user->getId());
     date_default_timezone_set($timezone);
     $model = new MeetingTime();
     $model->start = urldecode($start);
     $model->start_time = urldecode($start_time);
     if (empty($model->start)) {
       $model->start = Date(‘M d, Y’,time()+3*24*3600);
     }
     $model->tz_current = $timezone;
     $model->duration = 1;
     $model->meeting_id= $id;
     $model->suggested_by= Yii::$app->user->getId();
     $model->status = MeetingTime::STATUS_SUGGESTED;
     $selected_time = date_parse($model->start_time);
     if ($selected_time[‘hour’] === false) {
       $selected_time[‘hour’] =9;
       $selected_time[‘minute’] =0;
     }
     // convert date time to timestamp
     $model->start = strtotime($model->start) + $selected_time[‘hour’]*3600+ $selected_time[‘minute’]*60;
     $model->end = $model->start + (3600*$model->duration);
     $model->save();
     return true;
   }

Когда я начал добавлять новые даты и места через Ajax, я столкнулся со стеной, пытаясь повторно инициализировать контроллер Bootstrap Switch, который я начал использовать в более раннем эпизоде ​​«Доступность и выбор планирования» .

Я бы перезагрузил строки даты и времени, и все контроллеры переключателей на странице сломались.

Частично проблема заключалась в том, что повторная реализация Ajax для Bootstrap Switch не была хорошо документирована. Перепробовав несколько вещей в темноте и обратившись за помощью в сети, я наконец-то нашел свой путь к этому.

$("input[name='meeting-time-choice']").map(function(){} проходит по каждому $(this).bootstrapSwitch(property,value) управления переключателем, а набор команд $(this).bootstrapSwitch(property,value) сбрасывает Настройки управления. Потребовалось некоторое время, чтобы найти соответствующий API управления.

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
function insertTime(id) {
   $.ajax({
    url: $(‘#url_prefix’).val()+’/meeting-time/inserttime’,
    data: {
      id: id,
     },
     type: ‘GET’,
    success: function(data) {
     $(«#meeting-time-list»).html(data).removeClass(‘hidden’);
      $(«input[name=’time-chooser’]»).map(function(){
         //$(this).bootstrapSwitch();
         $(this).bootstrapSwitch(‘onText’,'<i class=»glyphicon glyphicon-ok»></i>&nbsp;choose’);
         $(this).bootstrapSwitch(‘offText’,'<i class=»glyphicon glyphicon-remove»></i>’);
         $(this).bootstrapSwitch(‘onColor’,’success’);
         $(this).bootstrapSwitch(‘handleWidth’,70);
         $(this).bootstrapSwitch(‘labelWidth’,10);
         $(this).bootstrapSwitch(‘size’,’small’);
       });
       $(«input[name=’meeting-time-choice’]»).map(function(){
         //$(this).bootstrapSwitch();
         $(this).bootstrapSwitch(‘onText’,'<i class=»glyphicon glyphicon-thumbs-up»></i>&nbsp;yes’);
         $(this).bootstrapSwitch(‘offText’,'<i class=»glyphicon glyphicon-thumbs-down»></i>&nbsp;no’);
         $(this).bootstrapSwitch(‘onColor’,’success’);
         $(this).bootstrapSwitch(‘offColor’,’danger’);
         $(this).bootstrapSwitch(‘handleWidth’,50);
         $(this).bootstrapSwitch(‘labelWidth’,10);
         $(this).bootstrapSwitch(‘size’,’small’);
       });
    },
  });
  refreshSend();
  refreshFinalize();
 }

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

Прежде чем я достиг этой точки, я потратил много времени на другие обходные пути. Bootstrap Switch — удивительный контроллер и ключевая часть простоты использования Meeting Planner, но его адаптация почти поразила меня.

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

Стартапы Ajax - Панель мест встречи, загруженная через Ajax

Как я уже говорил ранее, это может привести к путанице и разочарованию в отладке Ajax между JavaScript и PHP. Ошибки Ajax часто трудно отследить.

В этом случае использование консоли разработчика браузера Google Chrome помогло мне преодолеть пустоту.

Как правило, с Ajax у вас просто проблемы с отсутствием признаков того, что пошло не так.

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

Используя вкладку Консоль , я мог видеть неудачные запросы GET. Это ошибка сервера при попытке добавить место к собранию:

Стартапы Ajax - вкладка консоли браузера Google Chrome

Это помогло мне определить конкретные аргументы, запрашиваемые через ajax, в данном случае для встречи id = 186.

Просмотр вкладки Сеть также показывает эти вызовы и их аргументы:

Стартапы Ajax - вкладка «Сеть» в браузере Google Chrome

Когда вы нажимаете на конкретный запрос, вы видите пять вкладок; Вот вкладка Заголовки :

Стартапы Ajax - вкладка «Заголовки браузера Google Chrome»

В этом случае на вкладке « Предварительный просмотр » была выделена моя ошибка PHP в MeetingPlaceController, с которой столкнулся запрос Ajax:

Стартапы Ajax - вкладка предварительного просмотра браузера Google Chrome

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

Вот еще один пример вкладки « Сеть », запрашивающей места для идентификатора собрания = 186:

Стартапы Ajax - вкладка «Сеть» в браузере Google Chrome

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

Стартапы Ajax - вкладка предварительного просмотра браузера Google Chrome

Консоль разработчика Google Chrome мне очень помогла в завершении работы с Ajax.

Спасибо, Google! Я даже не буду дразнить вас о моем гениальном меме сегодня.

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

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

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

Я все ближе к запуску эксперимента с WeFunder, основанного на реализации новых правил краудфандинга SEC . Вы можете следить за нашим профилем там, если хотите. Я также напишу больше об этом в следующем уроке.

Следите за будущими уроками в серии « Построение стартапа с помощью PHP» .