
如果您问“什么是Yii?”,请查看 Yii Framework简介 ,其中 介绍了Yii 的优点,并包括2014年10月发布的Yii 2.0概述。
在本使用Yii2编程系列中 ,我指导读者使用PHP的Yii2框架。 在本教程中,我们将探索使用Ajax实现交互式页面的方法。 具体来说,我要强调在会议计划应用的两个领域,其中我写了使用Ajax 构建您启动一系列平行的。
首先,我们将回顾如何根据用户输入的特定位置在页面上加载Google地图。 如下所示,在我进入Plum Bistro并单击return之后,右边的地图会动态加载,而不会刷新页面。

其次,我将向您展示如何在计划阶段记录用户对会议所做的更改。 Meeting Planner使参与者可以轻松地确定自己的首选地点和日期时间,然后最终选择最终的地点和日期。

Ajax使此过程变得更加轻松和快捷,使人们可以滑动许多开关控件来指示自己的偏好,而无需刷新页面。
提醒一下,我确实参与了下面的评论主题。 如果您有不同的方法,其他想法或想为将来的教程提供建议,我特别感兴趣。 如果您有任何问题或建议,请在下面发布。 您也可以直接通过Twitter @reifman与我联系 。
在Yii中使用Ajax

如果您只是刚开始使用Ajax并想起步较慢,那么Yii Playground有两个简单的Ajax示例,它们可能对您有所帮助。 一个通过Ajax更改页面上的文本,另一个通过同一页面将响应加载到表单上,而无需刷新,每个都包含详细的代码示例。

让我们深入研究两个主要示例。 您可以在GitHub的Meeting Planner代码存储库中找到这些示例的所有源代码 。
交互式显示Google地图
建立输入表格
最初加载“ 创建地方”表单 (/frontend/views/place/create_place_google.php)时,该表单包含Google地方信息实时搜索窗口小部件:

集成Google Places JavaScript API
该表单会加载Google Maps JavaScript库并将其连接到place-searchbox输入字段:
$gpJsLink= 'https://maps.googleapis.com/maps/api/js?' . http_build_query(array(
'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显示地图。
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 -->
Meeting Planner Place表存储Google名称,place_id,位置,网站,附近和full_address,以在整个应用程序中使用。
上面包含的MapAsset加载了我们的create_place.js文件,该文件在Google和我们的表单之间运行; 它基本上通过Ajax管理数据的传输和响应。
我们的Ajax管理JavaScript
我将逐步指导您完成create_place.js。 首先,有setupListeners()
,由父窗体调用:
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 Bistro输入Plum
:

收集结果图及其数据
如果此人在下拉菜单中选择了Enter或单击了某个位置,则将调用populateResult()
;否则, populateResult()
调用populateResult()
。 如果没有,我们什么也不做。
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()
来显示地图:

loadMap()
函数专用于Google的Place API,并在右侧显示您在上方看到的地图:
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); // gps['k'], gps['D']);
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; 在Yii框架中更像是普通的AJAX。
当人们在会议计划中添加日期,时间和地点时,您会看到如下页面:

“ 您”和“ 他们”列显示每个参与者对地点和日期时间的偏好。 较大的“选择”滑块使人员可以对会议地点和时间做出最终决定。
人们需要收集大量数据,我们不想每次更改都需要刷新页面。 Ajax是解决此问题的理想解决方案。
我将遍历上面“会议场所”面板的代码。 上面的“会议时间”面板的工作原理类似。
遵守守则
由于MVC框架以及我希望重用代码部分的原因,此处的流程可能很难遵循。 PHP辅助函数和JavaScript有时必须放置在父文件中,而不是它们最密切相关的部分。 我将首先为您提供概述。 我鼓励您通过阅读以完全理解它。 同样,您可以通过GitHub浏览代码 。
提示:请记住,部分文件名通常以下划线开头。
- 会议计划程序页面位于/frontend/views/meeting/view.php。 此文件还包括帮助程序按钮的状态的辅助JavaScript函数,例如“ 发送”和“ 完成” (即,更改之后,用户现在可以发送此邀请吗?)使用Meeting Planner,通常必须先选择一个位置和一个时间,然后才能发送邀请),并显示视觉通知,通知用户完成后会将更改通过电子邮件发送给其他参与者。
- 显示场所的“位置”面板时,它将加载/frontend/views/meeting-place/_panel.php。 该文件包括辅助PHP函数
showOwnerStatus()
和showParticipantStatus()
,它们的子级_list.php将重用它们。 但是,最重要的是,_panel.php包含用于Bootstrap滑块switchChange
事件JavaScript方法。 - _panel.php文件使用_list.php来显示每个位置的每一行。 该文件将通过调用_panel.php函数
showOwnerStatus()
和showParticipantStatus()
来呈现Bootstrap滑块。 -
switchChange
函数将对MeetingPlaceChoiceController.php进行Ajax调用。 - 最后,MeetingPlaceChoiceController.php调用MeetingPlaceChoice.php模型以记录数据库中的更改。
抱歉,相关代码的放置很复杂且分散。
现在,我将逐步指导您完成关键组件。
Ajax代码逐步
这是Meeting / view.php渲染的Meeting-Place / _panel.php。 这将显示可能的地方行和参与者的选择的部分内容:
<?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,
]);
}
?>
下面是与响应Ajax结果但不是Ajax直接需要的操作有关JavaScript。 您无需了解这些功能如何理解此Ajax示例,但由于它们是为响应Ajax事件而调用的,因此我将它们包括在内。
<?php
$script = <<< JS
var notifierOkay; // meeting sent already and no page change session flash
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创建了显示位置和选择的表格:
<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调用。 选择器功能对应于较大的蓝色选择滑块,而选择功能对应于首选项滑块。
$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); // true | false
// 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); // true | false
// 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);
?>
上面的函数在MeetingPlaceChoiceController
调用actionSet()
以使用Ajax请求来响应切换更改:
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):
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
这是MeetingPlaceChoice::set()
方法,该方法在数据库中记录用户的操作,并创建一个MeetingLog条目,该条目监视计划期间的所有更改。
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 i.e. 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还允许我生成会议计划历史的文本摘要。 如果您想了解更多有关此的知识,我已经在《 建立您的初创企业:通知人们有关会议变更和传递通知》一书中对此进行了介绍。
下一步是什么?
我希望这些示例可以帮助您了解Yii中的Ajax基础。 如果您对更高级的Ajax感兴趣,我计划在Meeting Planner系列中包括Ajax加载的表单。 而且,诚然,Ajax是Yii社区尚未分享很多示例的领域。 通常,Ajax在Yii中的工作方式与在PHP和其他框架中的类似,因此您可以从其他框架社区的示例中学习。
在继续研究框架的不同方面时,请观看我们的《 使用Yii2编程》系列中即将发布的教程。 您可能还需要查看我们的“ 使用PHP构建启动”系列 ,该系列在构建实际应用程序时使用了Yii2的高级模板。
如果您想知道下一个Yii2教程何时到达, 请在Twitter上关注我@reifman或查看我的讲师页面 。 发布后,我的讲师页面将包含本系列中的所有文章。
相关链接
- 阿贾克斯(维基百科)
- 入门-Ajax(Mozilla开发人员网络)
- Yii2开发者交流 ,我的Yii2资源网站
翻译自: https://code.tutsplus.com/tutorials/programming-with-yii2-using-ajax--cms-26663