





















































This article by Adnan Jaswal, the author of the book, KnockoutJS by Example, will render a map of the application and allow the users to place markers on it. The users will also be able to get directions between two addresses, both as description and route on the map.
(For more resources related to this topic, see here.)
This feature is about placing markers on the map for the selected addresses. To implement this feature, we will:
Let's get started by updating the address model. Open the MapsApplication module and locate the AddressModel variable. Add an observable to this model to hold the marker like this:
/* generic model for address */
var AddressModel = function() {
this.marker = ko.observable();
this.location = ko.observable();
this.streetNumber = ko.observable();
this.streetName = ko.observable();
this.city = ko.observable();
this.state = ko.observable();
this.postCode = ko.observable();
this.country = ko.observable();
};
Next, we create a method that will create and place the marker on the map. This method should take location and address model as parameters. The method will also store the marker in the address model. Use the google.maps.Marker class to create and place the marker. Our implementation of this method looks similar to this:
/* method to place a marker on the map */
var placeMarker = function (location, value) {
// create and place marker on the map
var marker = new google.maps.Marker({
position: location,
map: map
});
//store the newly created marker in the address model
value().marker(marker);
};
Now, create a method that checks for an existing marker in the address model and removes it from the map. Name this method removeMarker. It should look similar to this:
/* method to remove old marker from the map */
var removeMarker = function(address) {
if(address != null) {
address.marker().setMap(null);
}
};
The next step is to register subscribers that will trigger when an address changes. We will use these subscribers to trigger the removal of the existing markers. We will use the beforeChange event of the subscribers so that we have access to the existing markers in the model. Add subscribers to the fromAddress and toAddress observables to trigger on the beforeChange event. Remove the existing markers on the trigger. To achieve this, I created a method called registerSubscribers. This method is called from the init method of the module. The method registers the two subscribers that triggers calls to removeMarker. Our implementation looks similar to this:
/* method to register subscriber */
var registerSubscribers = function () {
//fire before from address is changed
mapsModel.fromAddress.subscribe(function(oldValue) {
removeMarker(oldValue);
}, null, "beforeChange");
//fire before to address is changed
mapsModel.toAddress.subscribe(function(oldValue) {
removeMarker(oldValue);
}, null, "beforeChange");
};
We are now ready to bring the methods we created together and place a marker on the map. Create a map called updateAddress. This method should take two parameters: the place object and the value binding. The method should call populateAddress to extract and populate the address model, and placeMarker to place a new marker on the map. Our implementation looks similar to this:
/* method to update the address model */
var updateAddress = function(place, value) {
populateAddress(place, value);
placeMarker(place.geometry.location, value);
};
Call the updateAddress method from the event listener in the addressAutoComplete custom binding:
google.maps.event.addListener(autocomplete, 'place_changed',
function() {
var place = autocomplete.getPlace();
console.log(place);
updateAddress(place, value);
});
Open the application in your browser. Select from and to addresses. You should now see markers appear for the two selected addresses. In our browser, the application looks similar to the following screenshot:
The last feature of the application is to draw a route between the two address markers. To implement this feature, we will:
Let's get started by creating and initializing the direction service. We will use the google.maps.DirectionsService class to get the routing information and the google.maps.DirectionsRenderer to draw the route on the map. Create two attributes in the MapsApplication module: one for directions service and the other for directions renderer:
/* the directions service */
var directionsService;
/* the directions renderer */
var directionsRenderer;
Next, create a method to create and initialize the preceding attributes:
/* initialise the direction service and display */
var initDirectionService = function () {
directionsService = new google.maps.DirectionsService();
directionsRenderer = new google.maps.DirectionsRenderer({suppressMarkers: true});
directionsRenderer.setMap(map);
};
Call this method from the mapPanel custom binding handler after the map has been created and cantered. The updated mapPanel custom binding should look similar to this:
/* custom binding handler for maps panel */
ko.bindingHandlers.mapPanel = {
init: function(element, valueAccessor){
map = new google.maps.Map(element, {
zoom: 10
});
centerMap(localLocation);
initDirectionService();
}
};
The next step is to create a method that will build and fire a request to the direction service to fetch the direction information. The direction information will then be used by the direction renderer to draw the route on the map. Our implementation of this method looks similar to this:
/* method to get directions and display route */
var getDirections = function () {
//create request for directions
var routeRequest = {
origin: mapsModel.fromAddress().location(),
destination: mapsModel.toAddress().location(),
travelMode: google.maps.TravelMode.DRIVING
};
//fire request to route based on request
directionsService.route(routeRequest, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsRenderer.setDirections(response);
} else {
console.log("No directions returned ...");
}
});
};
We create a routing request in the first part of the method. The request object consists of origin, destination, and travelMode. The origin and destination values are set to the locations for from and to addresses. The travelMode is set to google.maps.TravelMode.DRIVING, which, as the name suggests, specifies that we require driving route. Add the getDirections method to the return statement of the module as we will bind it to a button in the view.
One last step before we can work on the view is to clear the route on the map when the user selects a new address. This can be achieved by adding an instruction to clear the route information in the subscribers we registerd earlier. Update the subscribers in the registerSubscribers method to clear the routes on the map:
/* method to register subscriber */
var registerSubscribers = function () {
//fire before from address is changed
mapsModel.fromAddress.subscribe(function(oldValue) {
removeMarker(oldValue);
directionsRenderer.set('directions', null);
}, null, "beforeChange");
//fire before to address is changed
mapsModel.toAddress.subscribe(function(oldValue) {
removeMarker(oldValue);
directionsRenderer.set('directions', null);
}, null, "beforeChange");
};
The last step is to update the view. Open the view and add a button under the address input components. Add click binding to the button and bind it to the getDirections method of the module. Add enable binding to make the button clickable only after the user has selected the two addresses. The button should look similar to this:
<button type="button" class="btn btn-default" data-bind="enable:
MapsApplication.mapsModel.fromAddress &&
MapsApplication.mapsModel.toAddress, click:
MapsApplication.getDirections">
Get Directions
</button>
Open the application in your browser and select the From address and To address option. The address details and markers should appear for the two selected addresses. Click on the Get Directions button. You should see the route drawn on the map between the two markers. In our browser, the application looks similar to the following screenshot:
In this article, we walked through placing markers on the map and displaying the route between the markers.
Further resources on this subject: