Planner 应用程序作为现实生活中的示例,指导您从概念到现实启动一家初创公司 。在此过程中的每一步,我都会将 Meeting Planner 代码作为开源示例发布,您可以从中学习。我还将解决与启动相关的业务问题。
深入了解 ajax
上周,我深入研究了 Ajax,将会议安排体验转变为完全 ajax 化的模型,并消除了页面刷新的需要。我大约完成了一半,主要关注简单的元素。
在今天的教程中,我将指导您完成更复杂的内容面板,这些面板需要更多的故障排除、研究、调试、头脑风暴和重新编码。正如我所说,有时我认为我可能不得不放弃这个功能,直到测试版发布之后。跟随我从那周开始我的一些个人噩梦(现在做得更好,别担心)。
我还将向您展示我如何使用 Google 的 chrome 浏览器开发人员控制台来帮助我识别损坏的区域 — 当在 php 和javascript之间使用 Ajax 时,这尤其困难。这就像黑暗隧道尽头的光。
您可能已经看过我们漂亮的 Bootstrap 开关,可帮助组织者和参与者分享他们的偏好,并为日期时间和地点做出最终选择。好吧,在 Ajax 更新后它们不应该是这样的——但是解决这个问题是这个特性更新的重要组成部分:
如果您尚未试用 Meeting Planner,请继续 使用新的交互式功能安排您的第一次会议。我确实参与了下面的评论线程,所以告诉我你的想法!你也可以在 Twitter @reifman 上联系我。如果您想为未来的教程推荐新功能或主题,我特别感兴趣。
提醒一下,Meeting Planner 的所有代码都是用 PHP 的 Yii2 F ram ework 编写的。如果您想了解更多关于 Yii2 的信息,请查看我们的并行系列 Programming With Yii2。
首先,让我们看一下通过 Ajax 添加会议参与者。
添加会议参与者
添加参与者的代码与我们之前介绍的类似。但我确实想查看稍微不同的代码,这些代码会更新参与者列表和所有显示其身份的按钮。
以前,每次会议只有一名参与者。然后,我 启用了小组会议并创建了一个按钮列表来指示每个参与者:
每当添加参与者时,我都会通过 Ajax 刷新整个列表。
这是在添加每个新函数后addParticipant() 调用的 jquery 函数:getParticipantButtons()
function addParticipant(id) { // ajax add participant // adding someone from new_email new_email = $('#new_email').val(); friend_id = $('#participant-email').val(); // also an email. blank before selection friend_email = $('#participant-email :selected').text(); // placeholder text before select // 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()功能:
function getParticipantButtons(id) { $.ajax({ url: $('#url_prefix').val()+'/participant/getbuttons', data: { id: id, }, type: 'GET', success: function(data) { $('#participantButtons').html(data); }, }); }
它对 ParticipantController.php 方法进行 Ajax 调用actionGetbuttons():
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; }
注意:我喜欢简称为“ParCon”,而不是 ParticipantController,因为它听起来像是一个远程星际迷航指挥基地——或者表明我在这个初创公司上独自工作的时间太长了。 我确实在 Ajax 功能上花费了太多的深夜。
无论如何,上述功能会使用所有更新的参与者重新填充面板。
现在,让我们继续讨论依赖于常用Bootstrap 日期时间选择器小部件之一的日期和时间。
添加日期和时间
日期、时间和地点都被证明是 ajaxify 最复杂的特征。与其说是表单上使用的 Bootstrap 小部件或 Google Maps api 。结果证明是Bootstrap Switch Controllers ——这些都不是为 Ajax 设计或记录的。
通过 Ajax 的引导开关控制器的问题
以下是 Ajax 提交添加地点后的损坏开关示例:
我将介绍我最终是如何解决这个问题的,但首先,让我们看一下 meeting-time/panel.php:
<?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">×</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 }?> </span></div><div class="col-lg-2 col-md-2 col-xs-2"><div style="float:right;"> <?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">×</button> <span id="timeMsg1"><?= Yii::t('frontend','We\'ll automatically notify others when you\'re done making changes.')?></span> <span id="timeMsg2"><?= Yii::t('frontend','Please pick a date and time.')?></span> </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 条件下显示的预加载警报。而且,在很大程度上,代码开始遵循我在其他面板上使用的相同格式。
在对每个内容面板进行 ajax 化时,我尽可能地尝试构建先前的方法并重用代码结构。
这是 Meeting.js 中的切换面板 javaScript:
//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():
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'); }
这会调用 MeetingTimeController.php actionAdd() Ajax 函数:
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控制器时遇到了困难,我在前面的调度可用性和选择集中开始使用该控制器。
我会重新加载日期时间行,页面上的所有开关控制器都坏了。
部分问题在于 Bootstrap Switch 的 Ajax 重新实例化没有得到很好的记录。在黑暗中尝试了一些事情并在互联网上寻求帮助之后,我终于找到了解决办法。
循环通过 $("input[name='meeting-time-choice']").map(function(){} 每个开关控制,并且该组 $(this).bootstrapSwitch(property,value) 命令重置控制设置。发现合适的控制 API 需要一些时间。
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> 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> yes'); $(this).bootstrapSwitch('offText','<i class="glyphicon glyphicon-thumbs-down"></i> 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 易用性的关键部分——但 ajaxifying 几乎让我感到震惊。
继续前进,添加会议地点类似于添加日期和时间,但我想使用此内容面板深入了解使用 Google Chrome 浏览器开发工具对 Ajax 进行故障排除。
添加会议地点
正如我之前所说,在 JavaScript 和 PHP 之间调试 Ajax 会变得非常混乱和令人沮丧。Ajax 错误通常很难追踪。
在这种情况下,使用谷歌 Chrome 浏览器的开发者控制台帮助我突破了空白。
一般来说,使用 Ajax,您只会出现功能障碍,而没有任何迹象表明出了什么问题。
以下是我用来追踪错误的 Chrome 公开的一些可见性的逐步说明。
通过使用控制台选项卡,我可以看到失败的 GET 请求。这是尝试请求将地点添加到会议的服务器错误:
这帮助我确定了通过 ajax 请求的特定参数,在本例中为会议 id = 186。
查看Network选项卡还会显示这些调用及其参数:
当您点击具体查询时,您可以看到五个选项卡;这是标题选项卡:
在这种情况下, 预览选项卡突出显示了 Ajax 请求遇到的 MeetingPlaceController 中的 PHP 错误:
您可以看到这变得多么有用——尤其是考虑到我必须重新构建大量代码以 ajaxify 所有这些调度功能。
这是请求会议 id = 186 的地点的网络选项卡的另一个示例 :
Preview 选项卡显示请求的 视图文件不存在或至少不在应有的位置:
Google Chrome 的开发者控制台帮助我完成了 Ajax 工作。
谢谢你,谷歌!今天我什至不会拿我的天才模因取笑你。
下一步是什么?
我希望您喜欢这些关于 Ajax 的章节,以及向快速、高效的会议安排和消除页面刷新的转变。会议安排是 Meeting Planner 的核心和灵魂,因此让它发挥作用至关重要。
- 添加日期和时间
- 通过 Ajax 的引导开关控制器的问题
- 添加会议地点