To drugi wpis dotyczący wyszukiwania miejsc za pomocą API Google Maps. Dzisiaj opiszę w jaki sposób wyniki otrzymywane w podpowiedziach wyszukiwarki przeniosłem na konkretną lokalizację na mapie. Nie jest to działanie idealne ale spełnia poniekąd swoją rolę. Na końcu opiszę problemy jakie obecnie występują, które postaram się poprawić podczas wykonywania refaktoryzacji. Jest ostatnim ważnym etapem, ponieważ wyszukiwarka będzie funkcjonalnością dającą dostęp do reszty funkcji aplikacji. Z czego wynika, że wyszukiwarka wraz z mapą jest najważniejszą częścią programu.
Moment przenoszenia wskazań mapy do wyszukiwanej lokalizacji był dla mnie najtrudniejszy. Spędziłem nad uzyskaniem zadowalającego działającego rozwiązania 3 długie wieczory. Mając pole tekstowe, do którego ładowały się lokalizacje z automatycznego podpowiadania, zostało tylko tę lokalizację przechwycić. Proste, prawda? Nic bardziej mylnego.
Od początku wiedziałem, że potrzebuję Usługi (ang. Service) udostępniającej dane w różnych kontrolerach. Powód jest prosty: wprowadzanie i przechwytywanie danych z pola tekstowego odbywa się w innym kontrolerze (AppController) aniżeli wyświetlanie mapy (HomeController).
Usługa
Wygląda ona następująco i umieściłem ją w module app:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class SearchService { | |
$position; | |
setPosition: (position) => void; | |
constructor() { | |
'ngInject'; | |
return this.$position; | |
} | |
public setPosition(position) { | |
console.log('set_pos'+position); | |
this.$position = position; | |
} | |
} |
Myślę, że zawartości tej klasy nie trzeba nikomu tłumaczyć. Publiczna właściwość i setter (przydałby się getter?).
I w tym momencie zaczynają się schody. Mimo że będzie to wyglądać dziecinnie proste, bez większej wiedzy na ten temat, dojście do rozwiązania problemów zajęło mi wiele czasu.
W pliku ./lib/app/index.ts wstrzyknąłem głównemu kontrolerowi moją usługę:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ... | |
import {SearchService} from './searchService'; | |
import {AppController} from './controller'; | |
// ... | |
// ROUTER CONFIG | |
mod.config(modConfigRouter); | |
mod.controller('AppController', AppController); | |
mod.service('$searchService', SearchService); | |
// ... |
A następnie jej w tym kontrolerze użyłem:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class AppController { | |
// ... | |
$searchService: SearchService; | |
constructor( | |
private $injector:ng.auto.IInjectorService, | |
public $scope:ng.IScope, | |
private $rootScope: ng.IRootScopeService, | |
public $state: angular.ui.IState, | |
public $searchService: SearchService | |
) { | |
'ngInject'; | |
// ... | |
this.$scope.$searchService = $searchService; | |
this.$rootScope.$state = $state; | |
} | |
onEnter() { | |
// ... | |
this.$autocomplete.addListener('place_changed', this.setPosition.bind(this)); | |
} | |
setPosition() { | |
let place = this.$autocomplete.getPlace(); | |
if (place !== undefined) | |
{ | |
let position = place.geometry.location; | |
this.$scope.$searchService.setPosition(position); | |
} | |
} | |
geolocate() { | |
// ... | |
} | |
} |
W ten zgrabny sposób w konstruktorze wstrzykujemy jako parametr usługę. Następnie przekazujemy ją do głównej przestrzeni danych, która jest dostępna dla każdego kontrolera aplikacji, a nawet widoków. W metodzie onEnter() dodałem eventListener reagujący na wybranie lokalizacji z podpowiadanych w wyszukiwarce miejsc. Metoda reagująca na te zdarzenie pobiera dane o pozycji wyszukanego miejsca i przekazuje je do usługi. Proste prawda? Aż mi głupio, że zajęło mi to tyle czasu.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class HomeController { | |
// ... | |
constructor(private $injector:ng.auto.IInjectorService, public $scope:ng.IScope) { | |
'ngInject'; | |
// ... | |
this.$searchService = this.$scope.$searchService; | |
} | |
onEnter() { | |
let searchBtn = document.getElementById('searchButton'); | |
searchBtn.addEventListener('click', () => { | |
let latLng = this.$searchService.$position; | |
this.maps.panTo(latLng); | |
}); | |
// ... | |
} | |
} |
Jak widać nie musiałem tutaj wstrzykiwać usługi. Jest ona dostępna z poziomu wspólnej przestrzeni danych $scope. Nie jestem pewien co do poprawności takiej praktyki ale póki co działa bez zarzutów.
W metodzie onEnter() natomiast dodałem nasłuchiwanie zdarzenia kliknięcia w button lupy koło pola wyszukiwania. Naciśnięcie tego przycisku ma za zadanie zmianę położenia centrum wyświetlanego na mapie. Jest to bardzo proste, pobieram pozycję z usługi i przekazuję do metody panTo centrujących mapę.
Wyszukiwanie!
Jak widać nie było to takie trudne. Po prostu kilka niewielkich zmian i gotowe :) Efekt końcowy prezentuje się następująco:Problemy
Niedoskonałości i mankamenty zaproponowanego rozwiązania to głównie:- Brzydki kod i nieciekawe rozwiązania
- Pole tekstowe w nagłówku działa tylko na jednej z zakładek
- Pole tekstowe nie zapamiętuje wpisanej wartości po przejściu do innej zakładki
- Wyszukiwarka jest wyświetlana w niecekawy sposób
- Wyniki autopodpowiedzi są prezentowane w sposób niedopuszczalny dla aplikacji mobilnej
Wszystkie te i inne problemy postaram się rozwiązać w następnych wpisach dotyczących wyszukiwania.
Brak komentarzy:
Prześlij komentarz