Repository navigation
Frontend View Aktuelle Route
Modul: happyHour.views.currentRoute
Controllerdatei: views/current-route/current-route.js
Templatedatei: views/current-route/current-route.html
Route: /currentRoute
Autor: Markus Thömmes
- AppStatusPersistenceService
- Zum Speichern der generierten Route im AppStatus
- $location
- Zum Weiterleiten auf andere Ansichten
- BackendService
- Zum Laden der Bars aus dem Backend
- RouteGeneratorService
- Zum Generieren der Route
- RoutesPersistenceService
- Zum lokalen Speichern der Routen
- rating
- Direktive zum Anzeigen von Bewertungen auf Foursquare
- input-day
- Direktive zum Auswählen des Wochentags
- input-slider
- Direktive zum Auswählen von Werten mithilfe eines Sliders
- input-places
- Direktive zum Auswählen von Orten mit einer Autovervollständigung
- bar-slider
- Direktive zum Auswählen von alternativen Bars
- formatTime
- Dient dem Formatieren von Zeiträumen
Beim Öffnen der Ansicht wird zunächst die aktuelle Route aus dem AppStatus geladen.
$scope.route = AppStatusPersistenceService.getRoute();Bei jeder Änderung der Route wird sie erneut lokal persistiert, um immer eine aktuelle Version im lokalen Speicher zu haben. Es wird auf die String-Version des Objekts gewatched um deep-watching zu vermeiden und Performanceeinbrüche zu vermeiden.
$scope.$watch(function() {
return JSON.stringify($scope.route);
}, function() {
AppStatusPersistenceService.setRoute($scope.route);
});Initial werden die zur Auswahl stehenden Bars aus dem Backend geladen und pro Timeframe in das bars-Array geschrieben. Werden keine gefunden, wird ein Popup angezeigt. Diese Abfrage wird jedesmal wiederholt, wenn die Sidebar geschlossen wird und sich relevante Optionen (route.options.location, route.options.radius, route.options.weekday) geändert haben.
$scope.bars = [];
BackendService.getBars($scope.route.options.location, $scope.route.options.radius, $scope.route.options.weekday).then(function(bars) {
possibleBars = bars;
if(possibleBars.length > 0) {
_.forEach($scope.route.timeframes, function() {
$scope.bars.push(possibleBars);
});
}
else {
$scope.noBarPopupOpen = true;
}
});Sobald sich etwas an den Start- und Endzeiten und/oder der Verweildauer ändern, wird versucht, die Bars der alten Timeframes auf die neuen Timeframes zu übernehmen, um die entsprechende Route nicht vollständig zu verlieren.
Dazu werden zunächst die neuen Timeframes aus den Optionen generiert.
while(isBeforeOverNight(startTime, endTime)) {
newTimeframes.push({
startTime: startTime.toString(),
endTime: startTime.add($scope.route.options.stayTime, 'hours').toString(),
bar: null
});
}Nun wird über die neuen und alten Timeframes iteriert und verglichen, ob sich neuer und alter Timeframe überschneiden. Wenn ja wird die Bar aus dem alten Timeframe in den neuen übernommen.
if(
newStartTime.isBefore(oldStartTime) && newEndTime.isAfter(oldStartTime) ||
newEndTime.isAfter(oldStartTime) && newStartTime.isBefore(oldEndTime) ||
newStartTime.isSame(oldStartTime) || newEndTime.isSame(oldEndTime)
) {
timeframe.bar = oldTimeframe.bar;
}Um die Route komplett neuzuberechnen, wird über den Reload-Button die reloadRoute-Methode aufgerufen, die sich die Bars nach aktuellsten Optionen aus dem Backend zieht und anschließend an den Algorithmus übergibt, um eine neue Route zu generieren. Werden keine Bars gefunden, wird eine entsprechende Fehlermeldung ausgegeben.
BackendService.getBars($scope.route.options.location, $scope.route.options.radius, $scope.route.options.weekday).then(function(bars) {
if(bars.length > 0) {
var route = RouteGeneratorService.createRoute(bars, $scope.route.options);
$scope.route = route;
}
else {
$scope.noBarPopupOpen = true;
}
});Wird eine Bar angeklickt wird die frameClicked-Methode ausgeführt und es werden Details der Bar angezeigt. Dies geschieht über das Setzen der is__expanded-Klasse auf dem HTML-Listenelement.
$scope.frameClicked = function(index) {
if(index === $scope.openFrameIndex) {
$scope.openFrameIndex = -1;
}
else {
$scope.openFrameIndex = index;
}
};ng-class="{is__expanded : $parent.openFrameIndex === $index}"Wird über eine Bar nach links geswiped, oder wird im erweiterten Zustand der Löschen-Knopf betätigt wird die Bar durch die removeBar-Methode aus dem Timeframe entfernt. Des Weiteren wird diese Bar mittels der toDelete-Variable zum Löschen vorgemerkt, was bedeutet, dass der Bar austauschen-Button angezeigt wird.
$scope.removeBar = function(index) {
$scope.route.timeframes[index].bar = null;
};ng-class="{is__backside : toDelete}"Das Austauschen der Bar läuft über die BarPickerDirective. Diese schreibt die Auswahl in den entsprechenden Timeframe.
<bar-slider bars="bars[$index]" result="timeframe.bar">Wird der Melden-Button geklickt erscheint ein Popup, das es ermöglicht einen Meldetext einzugeben und an das Backend abzusenden. Es wird zusätzlich die ID der Bar gespeichert, um sie im globalen Kontext identifizieren zu können und die Meldung entsprechend absetzen zu können.
ng-click="$parent.reportPopupOpen = true; $parent.reportBarId = timeframe.bar.id;Das Absenden des Popups führt zu einem Aufruf der reportBar-Methode, die die Meldung an das Backend sendet und das Popup wieder schließt, falls eine Meldung eingegeben wurde.
$scope.reportBar = function(barId, reportBarText) {
if(reportBarText.length > 0) {
BackendService.reportData(barId, reportBarText);
$scope.reportPopupNotification = true;
}
};Das Auswählen einer Lokation erfolgt über die PlacesAutocompleteDirective. Sie speichert ihr Ergebnis direkt im Routenobjekt.
<input-places result="route.options.location" />Klickt der Nutzer auf das Lokationsicon neben dem Eingabefeld, so wird seine aktuelle Position mittels GPS abgefragt und in der Route gespeichert. Dies geschieht mit der setLocationToDevice-Methode.
$scope.setLocationToDevice = function() {
navigator.geolocation.getCurrentPosition(function(position) {
$scope.$apply(function() {
$scope.route.options.location.longitude = position.coords.longitude;
$scope.route.options.location.latitude = position.coords.latitude;
});
});
};Die Auswahl eines Wochentags erfolgt durch die DayPickerDirective, die ihr Ergebnis direkt im Routenobjekt hinterlegt.
<input-day result="route.options.weekday" /Radius und Verweilzeit werden durch die SliderDirective ausgewählt und das Ergebnis sofort in das Routenobjekt überführt.
<input-slider options="{min:0.1, max:10, step:0.1, from:1, hide_min_max:true}" result="route.options.radius" />
<input-slider options="{min:0.5, max:6, step:0.5, from:1, hide_min_max:true}" result="route.options.stayTime" />Auch Start- und Endzeit werden über die SliderDirective gewählt, das Ergebnis (ein Array aus Strings) wird jedoch zunächst in das notwendige Format gebracht, um es dann in das Routenobjekt zu schreiben.
<input-slider options="{type:'double', values: ['18:00','19:00','20:00','21:00','22:00','23:00','24:00','01:00','02:00','03:00','04:00'], from:0, to:6, min_interval:1, max_interval: 10, hide_min_max:true}" result="routeTime" />$scope.$watch('routeTime', function(routeTime) {
$scope.route.options.startTime = routeTime[0];
$scope.route.options.endTime = routeTime[1];
});Beim Klick auf Sharen wird die Route durch die shareRoute-Methode an das Backend gesendet, der entsprechende Link generiert und dieser dann in einem Popup für den Nutzer angezeigt.
$scope.shareRoute = function() {
BackendService.saveRoute($scope.route).then(function(hash) {
$scope.shareLink = $location.protocol() + '://' + $location.host() + ':' + $location.port() + '/#/openRoute/' + hash;
$scope.sharePopupOpen = true;
});
};Beim Klick auf Offline speichern wird zunächst ein Popup geöffnet, das es ermöglicht der Route einen Namen zu geben. Anschließend wird die saveRoute-Methode aufgerufen, die die Route speichert, sofern der Name den Anforderungen genügt und der lokale Speicher noch nicht ausgeschöpft ist. Ist dies der Fall wird eine entsprechende Warnung angezeigt.
$scope.saveRoute = function() {
if(RoutesPersistenceService.isFull()) {
$scope.saveErrorPopupOpen = true;
$scope.namePopupOpen = false;
}
else {
if($scope.routeName !== '' && $scope.routeName.length <= 50) {
$scope.route.name = $scope.routeName;
RoutesPersistenceService.add($scope.route);
$scope.namePopupOpen = false;
}
}
};Beim Klick auf Impressum wird ein entsprechendes bildschirmfüllendes Popup geöffnet.
ng-click="showImpressum = true"