German (Deutsch) translation by Wei Zhang (you can also view the original English article)



Wenn Sie nach "Was ist Yii?" Fragen, sehen Sie sich die Einführung in das Yii-Framework an, in der die Vorteile von Yii beschrieben werden und eine Übersicht über Yii 2.0 enthalten, die im Oktober 2014 veröffentlicht wurde.
In dieser Programmierserie mit Yii2 leite ich die Leser beim Einsatz des Yii2-Frameworks für PHP. In diesem Lernprogramm werden wir die Implementierung interaktiver Seiten mit Ajax untersuchen. Im Besonderen werde ich die Verwendung von Ajax in zwei Bereichen der Meeting Planner-Anwendung hervorheben, über die ich parallel die Reihe "Building Your Startup" schreibe.
Zuerst überprüfen wir, wie wir eine Google Map auf die Seite laden, wenn der Nutzer einen bestimmten Ort betritt. Wie unten gezeigt, wird die Karte nach rechts, nachdem ich in Plum Bistro eintrat und auf "Zurück" klicke, dynamisch ohne Seitenaktualisierung geladen.



Zweitens zeige ich Ihnen, wie wir die Änderungen aufzeichnen, die ein Benutzer an einem Meeting während der Planungsphase vornimmt. Der Meeting Planner macht es den Teilnehmern leicht, ihre bevorzugten Orte und Datumszeiten zu identifizieren und letztendlich den endgültigen auszuwählen.



Ajax macht den Prozess viel einfacher und schneller, sodass Benutzer eine Reihe von Schaltersteuerelementen verschieben können, um ihre Präferenzen ohne Seitenaktualisierung anzuzeigen.
Nur zur Erinnerung, ich beteilige mich an den Kommentarthreads unten. Ich bin besonders interessiert, wenn Sie unterschiedliche Ansätze, zusätzliche Ideen haben oder Themen für zukünftige Tutorials vorschlagen möchten. Wenn Sie eine Frage oder ein Thema haben, schreiben Sie bitte unten. Sie können mich auch direkt auf Twitter @reifman erreichen.
Ajax mit Yii verwenden



Wenn Sie gerade erst mit Ajax beginnen und langsam anfangen möchten, bietet das Yii Playground zwei einfache Beispiele für Ajax, die für Sie hilfreich sein könnten. Einer ändert den Text auf einer Seite über Ajax, und ein anderer lädt die Antwort in ein Formular auf derselben Seite, ohne beide zu aktualisieren, und beide enthalten detaillierte Codebeispiele.



Lassen Sie uns in unsere zwei Hauptbeispiele eintauchen. Sie finden alle Quellen für diese Beispiele im Code Repository von Meeting Planner unter GitHub.
Interaktive Anzeige von Google Maps
Erstellen des Eingabeformulars
Wenn das Formular "Ort erstellen" (/frontend/views/place/create_place_google.php) zum ersten Mal geladen wird, enthält es das Widget für die Google Places-Live-Suche:



Einbindung der Google Places-JavaScript-API
Das Formular lädt die Google Maps-JavaScript-Bibliothek und verbindet sie mit dem Eingabefeld für das Suchfeld "Ort":
$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 );
Das Teilformular _formPlaceGoogle.php enthält einige ausgeblendete Felder, in denen die Ergebnisse der Karte vor dem Senden der gesamten Seite gespeichert werden können, sowie ein verstecktes Div, um die Karte über Ajax anzuzeigen.
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 -->
Die Meeting Planner Place-Tabelle speichert den Google-Namen, die Platz-ID, den Standort, die Website, die Umgebung und die vollständige Adresse für die Verwendung in der gesamten Anwendung.
Das MapAsset, das oben enthalten ist, lädt unsere Datei create_place.js, die zwischen Google und unserem Formular arbeitet. Es verwaltet im Wesentlichen die Übertragung und Antwort von Daten über Ajax.
Unser Ajax Verwalten von JavaScript
Ich führe Sie durch create_place.js in Teilen. Zunächst gibt es setupListeners()
, das vom übergeordneten Formular aufgerufen wird:
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(); } }); }
Wenn der Benutzer mit der Eingabe beginnt, werden die Kopfzeilenoptionen für reale Orte vom Widget heruntergefahren und das Ereignis place_changed wird bei jedem Tastendruck verarbeitet. Der keydown
-Listener oben verhindert, dass der Return-Schlüssel (ASCII 13 oder 0xD für Sie Hex-Geeks) das Formular sendet.
So sieht es bei der Eingabe aus. Ich komme in Plum
für Plum Bistro:



Sammeln der resultierenden Karte und ihrer Daten
Wenn die Person einen Eintrag in der Dropdown-Liste ausgewählt oder angeklickt hat, wird populateResult()
aufgerufen. wenn nicht, machen wir nichts.
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']); }
Dadurch werden alle ausgeblendeten Felder mit Daten von Google gefüllt und loadMap()
aufgerufen, um die Karte anzuzeigen:



Die loadMap()
- Funktion ist sehr spezifisch für Google Place API und zeigt die Karte oben rechts an:
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 }); }
Die Benutzererfahrung ist schnell und beeindruckend. Versuch es!
Meeting-Änderungen dynamisch aufzeichnen
Als Nächstes wollen wir uns ansehen, wie wir Änderungen an Besprechungsplänen in Echtzeit aufzeichnen. Hier gibt es keine Google-API. Es ist mehr Vanille-AJAX im Yii-Framework.
Beim Hinzufügen von Terminen, Zeiten und Orten zu Ihren Meeting-Plänen wird eine Seite wie diese angezeigt:



Die Sie- und Themenspalten zeigen die Bevorzugung jedes Teilnehmers gegenüber Orten und Datumszeiten. Mit dem größeren Schieberegler "Auswählen" können Sie die endgültige Entscheidung über den Ort und die Uhrzeit des Treffens treffen.
Es gibt eine Menge Daten, die von Personen gesammelt werden müssen, und wir möchten bei jeder Änderung keine Seitenaktualisierung anfordern. Ajax ist die ideale Lösung für dieses Problem.
Ich gehe den Code für das Meeting-Place-Panel oben durch. Das Meeting-Time-Panel oben funktioniert ähnlich.
Dem Kodex folgen
Aufgrund des MVC-Frameworks und meines Wunsches, Codeteile wiederzuverwenden, kann der Fluss hier möglicherweise schwer zu folgen sein. PHP-Hilfsfunktionen und JavaScript mussten manchmal in übergeordneten Dateien platziert werden, nicht in den Teilbereichen, mit denen sie am engsten verwandt waren. Ich werde versuchen, Ihnen zuerst einen Überblick zu geben. Ich möchte Sie dazu ermutigen, einige Passagen vorzulesen, um es vollständig zu verstehen. Und wieder können Sie den Code über GitHub durchsuchen.
Hinweis: Beachten Sie, dass Dateinamen für Partials normalerweise mit einem Unterstrich beginnen.
- Die Seite Meetingplaner wird unter /frontend/views/meeting/view.php geladen. Diese Datei enthält auch hilfreiche JavaScript-Funktionen zur Verwaltung des Status von Schaltflächen wie Senden und Finalisieren. In Meeting Planner muss in der Regel ein Ort und eine Uhrzeit ausgewählt werden, bevor er gesendet werden kann. Außerdem werden visuelle Benachrichtigungen angezeigt, dass die Änderungen per E-Mail an andere Teilnehmer gesendet werden, wenn der Benutzer den Vorgang abgeschlossen hat.
- Beim Anzeigen des Bereichs Wo für Orte wird /frontend/views/meeting-place/_panel.php geladen. Diese Datei enthält die PHP-Hilfefunktionen
showOwnerStatus()
undshowParticipantStatus()
, die von ihrem untergeordneten Element _list.php wiederverwendet werden. Am wichtigsten ist jedoch, dass _panel.php JavaScript-Methoden für dasswitchChange
-Ereignis des Schiebereglers Bootstrap enthält. - Die Datei _panel.php verwendet _list.php, um jede einzelne Zeile für jeden Ort anzuzeigen. Diese Datei rendert die Bootstrap-Schieberegler durch Aufrufen der _panel.php-Funktionen
showOwnerStatus()
undshowParticipantStatus()
. - Die
switchChange
-Funktionen rufen Ajax-Aufrufe an MeetingPlaceChoiceController.php ab. - Schließlich ruft MeetingPlaceChoiceController.php das MeetingPlaceChoice.php-Modell auf, um die Änderungen in der Datenbank aufzuzeichnen.
Es tut mir leid, dass die Platzierung von relevantem Code kompliziert und verbreitet ist.
Nun werde ich Sie Schritt für Schritt durch die wichtigsten Komponenten führen.
Ajax-Code Schritt für Schritt
Hier ist Meeting / view.php, das Meeting-Place / _panel.php darstellt. Dies zeigt den Teil für die Reihen möglicher Plätze und die Auswahl der Teilnehmer an:
<?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, ]); } ?>
Darunter ist JavaScript im Zusammenhang mit Aktionen, die auf Ajax-Ergebnisse reagieren, aber nicht direkt für Ajax benötigt werden. Sie müssen nicht verstehen, was diese Funktionen tun, um dieses Ajax-Beispiel zu verstehen, aber ich habe sie hinzugefügt, da sie als Reaktion auf Ajax-Ereignisse aufgerufen werden.
<?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); ?>
Hier in Meeting-Place / _panel.php wird die Tabelle mit den Orten und Auswahlen erstellt, wobei _list.php aufgerufen wird:
<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; ?>
Noch wichtiger ist, es enthält auch das JavaScript, das wir verwenden, um Ajax-Aufrufe durchzuführen, wenn der Benutzer einen Schalter bewegt und seinen Status ändert. Die Auswahlfunktionen entsprechen dem größeren blauen Auswahlschieberegler, während die Auswahlfunktionen den Vorzugsschiebereglern entsprechen.
$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); ?>
Die obigen Funktionen rufen den Aufruf von actionSet()
in MeetingPlaceChoiceController
auf, um auf die Switchänderung mit Ajax-Anforderungen zu reagieren:
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; }
Controller-Aktionen, die über Ajax antworten, müssen ein JSON-Antwortformat haben (auf diese Weise weiß Yii, dass sie nicht für die Bereitstellung von HTML gedacht sind):
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
Hier ist die MeetingPlaceChoice::set()
- Methode, die die Aktionen des Benutzers in der Datenbank aufzeichnet und einen MeetingLog-Eintrag erstellt, der alle Änderungen während der Planung überwacht.
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; } }
Funktionen im Zusammenhang mit Besprechungsänderungen
In Meeting Planner protokolliere ich jede einzelne Änderung. Dadurch kann ich wissen, wann seit der letzten Änderung einer Person einige Minuten vergangen sind, und andere Meeting-Teilnehmer benachrichtigen. Es ist ein Experiment, das ich mit diesem Service versuche, anstatt zu verlangen, dass die Teilnehmer jedes Mal auf Submit klicken, wenn sie Änderungen vornehmen möchten.
Dies erfordert jedoch ein Training, um zu verstehen, dass es in Ordnung ist, es zu ändern und zu verlassen, d. Die displayNotifier()
- Funktionen zeigen daher einige Flash-Warnungen an, um dies zu unterstützen. Ich werde sie letztendlich im Laufe der Zeit verbessern und für erfahrene Benutzer entfernen.
Mit dem MeetingLog kann ich auch eine Textzusammenfassung des Planungsverlaufs der Besprechung erstellen. Wenn Sie daran interessiert sind, mehr darüber zu erfahren, habe ich darüber geschrieben in "Erstellen Sie Ihr Startup": Benachrichtigen von Personen über Besprechungsänderungen und Übermitteln von Benachrichtigungen
Was kommt als nächstes?
Ich hoffe, diese Beispiele helfen Ihnen, die Grundlagen von Ajax in Yii zu verstehen. Wenn Sie an fortgeschrittenem Ajax interessiert sind, plane ich, Ajax-geladene Formulare in die Meeting Planner-Serie aufzunehmen. Und zugegebenermaßen ist Ajax ein Gebiet, in dem die Yii-Community nicht viele Beispiele geteilt hat. Im Allgemeinen arbeitet Ajax in Yii ähnlich wie in PHP und anderen Frameworks, sodass Sie von Beispielen aus anderen Framework-Communities lernen können.
Achten Sie auf kommende Tutorials in unserer Programmierserie "Programmieren mit Yii2", während wir weiter in verschiedene Aspekte des Frameworks eintauchen. Vielleicht möchten Sie auch unsere Reihe für das Erstellen Ihres Startups mit PHP ausprobieren, die die erweiterte Vorlage von Yii2 verwendet, während wir eine reale Anwendung erstellen.
Wenn Sie wissen möchten, wann das nächste Yii2-Tutorial ankommt, folgen Sie mir @reifman auf Twitter oder besuchen Sie meine Instructor-Seite. Meine Ausbilderseite enthält alle Artikel dieser Serie, sobald sie veröffentlicht sind.
ähnliche Links
- Ajax (Wikipedia)
- Erste Schritte - Ajax (Mozilla Developers Network)
- Yii2 Developer Exchange, meine Website für Yii2-Ressourcen