如果您要问“什么是 Yii?”,请查看 Yii 框架简介,它回顾了 Yii 的优点并包括 2014 年 10 月发布的 Yii 2.0 概述。
在这个 使用 Yii2 编程系列中,我将指导读者使用 Yii2 框架 for php。在本教程中,我们将探讨使用 ajax 实现交互式页面。具体来说,我将重点介绍 Ajax 在 Meeting Planner 应用程序的两个领域中的使用,我将同时编写关于这两个领域的Building Your Startup 系列。
首先,我们将回顾如何在页面上加载 Google 地图以响应用户输入特定地点。如下图,我进入 Plum Bistro点击return后,右边的地图是动态加载的,没有刷新页面。
其次,我将向您展示我们如何记录用户在计划阶段对会议所做的更改。Meeting Planner 使参与者可以轻松地确定他们喜欢的地点和日期时间,然后最终选择最后一个。
Ajax 使该过程变得更加容易和快速,允许人们滑动多个开关控件来指示他们的偏好,而无需任何页面刷新。
提醒一下,我确实参与了下面的评论线程。如果您有不同的方法、其他想法或想为未来的教程建议主题,我会特别感兴趣。如果您有问题或主题建议,请在下面发布。您也可以直接在 Twitter @reifman上与我联系 。
在 Yii 中使用 Ajax
作者 DanielSHaischt,通过维基共享资源,CC BY-SA 3.0
如果您刚开始使用 Ajax 并且想慢慢开始,Yii Playground 有两个简单的 Ajax 示例 ,可能对您有所帮助。一个通过 Ajax 更改页面上的文本,另一个将响应加载到同一页面上的表单,两者都无需刷新,并且每个都包含详细的代码示例。
让我们深入研究两个主要示例。您可以在 GitHub的 Meeting Planner 代码存储库中找到这些示例的所有源代码。
交互式显示谷歌地图
构建输入表单
当Create a Place 表单(/frontend/views/place/create_place_google.php) 最初加载时,它包括 Google Places 实时搜索小部件:
集成 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 部分表单包括一些隐藏字段,在提交整个页面之前可以在其中存储地图的结果,以及通过 Ajax 显示地图的隐藏 div。
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 为您的十六进制极客)提交表单。
这是您键入时的样子。我要进 PlumPlum Bistro:
收集生成的地图及其数据
如果此人已选择进入或单击下拉列表中的某个位置,则 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 是这个问题的理想解决方案。
我将介绍上面的 Meeting-Place 面板的代码。上面的会议时间面板的工作方式类似。
遵守守则
由于 MVC 框架和我重用代码部分的愿望,这里的流程可能很难遵循。PHP 帮助函数和 JavaScript 有时必须放在父文件中,而不是它们最密切相关的部分。我会先给你一个概述。我鼓励您通过阅读它来完全理解它。同样,您可以通过 GitHub 浏览代码。
提示:请记住,部分文件名通常以下划线开头。
会议计划页面加载在 /frontend/views/meeting/view.php。该文件还包括帮助 JavaScript 函数来管理按钮的状态,例如Send和Finalize(即在此更改之后,用户现在可以发送此邀请吗?使用 Meeting Planner,通常必须选择一个地点和一个时间才能发送) 并在用户完成时显示更改将通过电子邮件发送给其他参与者的视觉通知。
在显示地点的Where面板时,它会加载 /frontend/views/meeting-place/_panel.php。该文件包括辅助 PHP 函数showOwnerStatus()和showParticipantStatus(),将被其子 _list.php 重用。但是,最重要的是,_panel.php 包含用于 Bootstrap 滑块switchChange事件的 JavaScript 方法。
_panel.php 文件使用 _list.php 来显示每个地点的每一行。该文件将通过调用 _panel.php 函数showOwnerStatus()和showParticipantStatus().
这些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); ?>
上面的函数使用 Ajax 请求调用actionSet()in MeetingPlaceChoiceController以响应开关更改:
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 的高级模板构建真实世界的应用程序。
- 构建输入表单
- 集成 Google Places javascript api
- 我们的 Ajax 管理 javaScript
- 收集生成的地图及其数据
- 遵守守则
- Ajax 代码一步一步
- 与会议更改相关的功能