本教程是 Envato Tuts+ 上的“使用 PHP 构建您的初创公司”系列的一部分。在本系列中,我将使用我的会议规划器应用作为现实生活中的示例,引导您从概念到现实启动一家初创公司。在此过程中的每一步,我都会将 Meeting Planner 代码作为开源示例发布,您可以从中学习。我还将解决出现的与初创企业相关的业务问题。
深入了解 Ajax
上周,我深入研究了 Ajax,将会议安排体验转变为完全 ajax 化的模型,并消除了页面刷新的需要。我大约完成了一半,主要关注简单的元素。
在今天的教程中,我将引导您完成更复杂的内容面板,这些面板需要更多的故障排除、研究、调试、头脑风暴和重新编码。正如我所说,有时我想我可能不得不放弃这个功能,直到测试版发布之后。请跟随我带您经历那周我个人的一些噩梦(现在做得更好,不用担心)。
我还将向您展示如何使用 Google 的 Chrome 浏览器开发者控制台来帮助我识别损坏的区域,这在 PHP 和 JavaScript 之间使用 Ajax 时尤其困难。就像黑暗隧道尽头的光明。
您可能已经见过我们漂亮的 Bootstrap 开关,可以帮助组织者和参与者分享他们的偏好并做出日期时间和地点的最终选择。好吧,在 Ajax 更新后它们不应该看起来像这样,但解决这个问题却是此功能更新的一个重要部分:
如果您尚未试用会议规划师,请继续使用新的交互功能安排您的第一次会议。我确实参与了下面的评论主题,所以请告诉我你的想法!您也可以通过 Twitter @reifman 联系我。如果您想为未来的教程提出新功能或主题建议,我会特别感兴趣。
提醒一下,Meeting Planner 的所有代码都是用 PHP 的 Yii2 框架编写的。如果您想了解有关 Yii2 的更多信息,请查看我们的并行系列“使用 Yii2 进行编程”。
首先,让我们看看通过 Ajax 添加会议参与者。
添加会议参与者
添加参与者的代码与我们之前介绍的类似。但我确实想查看稍微不同的代码,这些代码更新参与者列表以及显示其身份的所有按钮。
以前,每次会议只有一名参与者。然后,我启用了群组会议并创建了一个按钮列表来指示每个参与者:
每当添加参与者时,我都会通过 Ajax 刷新整个列表。
以下是 jQuery 函数 addParticipant()
,它会在添加每个新按钮后调用 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 actionGetbuttons()
方法进行 Ajax 调用:
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 日期时间选择器小部件之一的日期和时间。
添加日期和时间
日期、时间和地点都是ajax化中最复杂的功能。表单上使用的并不是 Bootstrap 小部件或 Google 地图 API。结果是 Bootstrap Switch Controllers——这些控制器没有针对 Ajax 进行良好的设计或记录。
通过 Ajax 引导开关控制器的问题
以下是 Ajax 提交添加位置后损坏的开关的示例:
我将详细介绍如何最终解决此问题,但首先让我们看一下 meet-time/panel.php:
'<?php
use yiihelpersHtml;
use yiiwidgetsListView;
use yiibootstrapCollapse;
use kartikswitchinputSwitchInput;
?>
<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 = yiiwebResponse::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 bug 通常很难追踪。
在这种情况下,使用 Google Chrome 浏览器的开发者控制台帮助我突破了空白。
一般来说,使用 Ajax 时,您只是遇到了故障,没有任何迹象表明出了什么问题。
以下是我用来追踪错误的 Chrome 公开的逐步可见性。
通过使用控制台选项卡,我可以看到失败的 GET 请求。这是尝试请求添加会议地点时发生的服务器错误:
这帮助我确定了通过 ajax 请求的具体参数,在本例中为会议 id = 186。
查看网络选项卡还会显示这些调用及其参数:
当您单击特定查询时,您可以看到五个选项卡;这是标题选项卡:
在本例中,预览选项卡突出显示了 Ajax 遇到的 MeetingPlaceController 中的 PHP 错误请求:
您可以看到这变得多么有用 - 特别是考虑到我必须重建大量代码才能实现所有这些调度特点。
以下是网络标签请求会议 ID = 186 地点的另一个示例:
预览标签显示所请求的视图文件不存在或至少不存在不在它应该在的地方:
Google Chrome 的开发者控制台帮助我完成了 Ajax 工作。
谢谢你,谷歌! 今天我什至不会拿我的天才模因来取笑你。
下一步是什么?
我希望您喜欢关于 Ajax 的这两集以及向快速、高效的会议安排和消除页面刷新的转变。会议安排是 Meeting Planner 的核心和灵魂,因此使其发挥出色至关重要。
我个人通过这个过程学到了很多东西,这些变化对服务产生了巨大的积极影响。
请尝试更快速的安排并与您的朋友分享会议规划器。与往常一样,如果您在下面的评论中分享您的经验,我将不胜感激,并且我始终对您的建议感兴趣。您随时可以通过 Twitter @reifman 直接联系我。
我即将根据 SEC 新众筹规则的实施与 WeFunder 启动实验。如果您愿意,可以关注我们的个人资料。我还将在以后的教程中详细介绍这一点。
观看“使用 PHP 构建您的初创公司”系列中即将推出的教程。
相关链接
- 会议策划
-
构建您的初创公司:用于调度的动态 Ajax 表单(Envato Tuts+)
- 会议策划者的 WeFunder 页面
- 使用 Yii2 编程:入门
- Yii2 开发者交流会