
In this code lab, we will create a fully working Google Maps app using elements in Polymer's Google Web Components collection. The app will be responsive and will include driving directions and transit mode. Along the way, you'll also learn about Polymer's data-binding features and iron ajax element.
Fire up a code editor, start a new project and make sure your folder structure should look like this
IngAtmsCodeLab/
data/ <!-- will hold the data for our application -->
images/ <!-- will hold the images for our application -->
src/ <!-- will hold the elements for our application -->
bower.json <!-- Bower metadata files used for managing dependencies -->
index.html <!-- our application main entry point -->
Install Polymer and web components polyfill
$ bower install --save Polymer/polymer#1.11.3
$ bower install --save webcomponentsjs#0.7.24
Also install polymer-cli as a global Node package
$ npm install -g polymer-cli
Start with a minimal HTML5 document
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>ING ATMs</title>
</head>
<body>
</body>
</html>
then add the reference to the webcomponents-lite.min.js polyfill inside the head tag
ing-atms elementInside the src folder create an html file named ing-atms.html. Use the following element template:
<link rel="import" href="../bower_components/polymer/polymer.html">
<dom-module id="ing-atms">
<template>
<style>
</style>
<h1>Hello World from Polymer!</h1>
</template>
<script>
Polymer({
is: 'ing-atms'
});
</script>
</dom-module>
ing-atms elementTo employ <ing-atms>, you need to:
index.html.body.From command line run (insde the application folder
$ polymer serve -o
If everything went well you shall see a nice greeting
The Google Web Components collection provides the <google-map> element for declaratively rendering a Google Map. To use it, you first need to install it using Bower.
$ bower install --save GoogleWebComponents/google-map
The download may take a few seconds. You can verify that <google-map> (and any dependencies) was installed by checking that bower_components/google-map/ was created and populated.
To use the map element, you'll need to obtain an API key for the Google Maps API. Visit https://developers.google.com/maps/documentation/javascript/get-api-key and copy your key. We'll use it in the next step.
To employ <google-map>, you need to:
HTML Import to load it inside ing-atms.html.
<link rel="import" href="../bower_components/google-map/google-map.html">
ing-atms.html's template (replace the h1 tag).
<google-map api-key="YOUR_KEY" disable-default-ui zoom="15">
</google-map>
If you run the app right now, nothing will display. In order for the map to properly display itself, you need to set its container (in this case, <ing-atms>) to have a fixed height.
For this we are going to use a component iron-flex-layout
$ bower install --save PolymerElements/iron-flex-layout
HTML Import to load it inside ing-atms.html.
<link rel="import" href="../bower_components/iron-flex-layout/iron-flex-layout.html">
:host pseudoselector inside the style tag
:host {
@apply --layout-fit;
}
If you haven't already done so, refresh the page. At this point, you should see a map that takes up the entire viewport.
The application will be useful if the map will be centered to our location.
$ bower install --save ebidel/geo-location
To employ <geo-location>, you need to:
HTML Import to load it inside ing-atms.html.
<link rel="import" href="../bower_components/geo-location/geo-location.html">
ing-atms.html's template.
<geo-location watch-pos high-accuracy latitude="[[_twoWayBinding('lat')]]" longitude="[[_twoWayBinding('lng')]]"></geo-location>
The geo-location component will provide the latitude and longitude every time our position changes. We used two way binding to bind them to our lat and lng properties.
Update the google-map to use the new properties
<google-map
api-key="YOUR_KEY"
latitude="[[_oneWayBinding('lat')]]" longitude="[[_oneWayBinding('lng')]]"
disable-default-ui zoom="15">
</google-map>
Refreshing the page will center the map to our position (a permission to use the location will be asked).
Will be nice if our location will be clearly marked on the map by a pin
google-map-markerThe google-map-marker comes with the google-map component and it is used to display a marker on the map, but first we have to import the component.
<link rel="import" href="../bower_components/google-map/google-map-marker.html">
In order to use it we have to declare it inside the google-map component and use the same lat and lng properties for its position
<google-map
api-key="YOUR_KEY"
latitude="[[_oneWayBinding('lat')]]" longitude="[[_oneWayBinding('lng')]]"
disable-default-ui zoom="15">
<google-map-marker
latitude="[[_oneWayBinding('lat')]]"
longitude="[[_oneWayBinding('lng')]]">
</google-map-marker>
</google-map>
At this point we have a default marker showing our position but we can change the icon and even add some content that shows up when we click the marker.
<h2>You are here</h2>images folder
google-map-marker by adding the attribute icon="images/my-location.png"Download the locations.json inside the data folder of your project
locations.json fileTo read the file we need to do an AJAX request and in Polymer there is the iron-ajax component for that
iron-ajax element
$ bower bower install --save PolymerElements/iron-ajax
iron-ajax element
<iron-ajax auto url="../data/locations.json" on-response="handleResponse"></iron-ajax>
where
auto automatically performs the Ajax request.url the URL target of the request.on-response is the callback being called when the request finishes with successThe response will contain information in a format different than the one we need. It is an array of cities, each containing an array of locations
What we need is just an array of locations and in order to obtain that we will going to use the Array.reduce function.
Polymer({
is: 'ing-atms',
handleResponse: function (evt, xhr) {
this.locations = xhr.response.city.reduce(function (locations, city) {
return locations.concat(city.location);
}, []);
}
});
Now that we have the locations prepared let's show them on the map
For this we are going to use another Polymer construct, the dom-repeat template
Just after the location marker add
<template is="dom-repeat" items="[[_oneWayBinding('locations')]]" as="atm">
<google-map-marker
latitude="[[_oneWayBinding('atm.map.latitude')]]"
longitude="[[_oneWayBinding('atm.map.longitude')]]">
</google-map-marker>
</template>
filter function of the dom-repeat tempplateHint: make sure that atm type is not ATM non-ING
You can use:

The <google-map-directions> element was installed alongside <google-map>. It provides driving direction information using the Google Maps API.
To employ <google-map-directions>:
ing-atms.html.
<link rel="import" href="../bower_components/google-map/google-map-directions.html">
Declare <google-map-directions> as a sibling of <google-map>.
<google-map-directions
api-key="YOUR_KEY">
</google-map-directions>
<google-map
api-key="YOUR_KEY"
latitude="[[_oneWayBinding('lat')]]" longitude="[[_oneWayBinding('lng')]]"
disable-default-ui zoom="15">
...
</google-map>
<google-map-directions> fetches directions, but it isn't that useful by itself. You need to connect the results from <google-map-directions> to <google-map> so the directions render on the map.
Both elements expose a .map property that allow users to access/set an underlying Map object (used by the Google Maps JavaScript API). To get these two elements talking, set them to use the same Map object.
api-key a property of the ing-atms component and the bind it to both the google-map and google-map-directions componentgoogle-map and google-map-directions to the map propertygoogle-map-directions elementThe start address will be our current location. Create a computed property named startAddress
Polymer({
is: 'ing-atms',
properties: {
...
startAddress: {
type: Object,
computed: 'computeStartAddress(lat, lng)'
}
},
...
computeStartAddress: function (lat, lng) {
return {lat: lat, lng: lng};
}
});
The end address will be the ATM we want to go to location's
In order to compute the end address we need to add the following attributes on the ATM's google-map-marker, click-events on-google-map-marker-click="computeEndAddress"
where
click-events when present, marker *click events are automatically registered.on-google-map-marker-click callback function for the marker's click event.and
computeEndAddress: function (event) {
this.endAddress = {lat: parseFloat(event.target.latitude), lng: parseFloat(event.target.longitude)};
},
startAddress and endAddress properties to the google-map-directions elementgoogle-map-directions adds by creating a renderOptions property
renderOptions: {
type: Object,
value: function () {
return {
suppressMarkers: true
};
}
}
$ bower install --save PolymerElements/iron-icons
$ bower install --save PolymerElements/paper-tabs
In ing-atms.html, add the following HTML Imports:
<link rel="import" href="../bower_components/paper-tabs/paper-tabs.html">
<link rel="import" href="../bower_components/paper-tabs/paper-tab.html">
<link rel="import" href="../bower_components/iron-icons/maps-icons.html">
<link rel="import" href="../bower_components/iron-icon/iron-icon.html">
Note: maps-icons is required to load Polymer's map iconset. You'll use it later to render icons for the travel modes.
We'll use <paper-tabs> to allow users to select their travel type. Each transit mode will also use an icon from the maps icon set instead of the default one. To use one of the other icon sets, the icon attribute takes the form: <iconset>:<name>, where <iconset> is the name of the icon set (e.g. "maps") and <name> is the name of the icon from that set.
As an example, the following code places the directions-car icon from the maps icon set:
<iron-icon icon="maps:directions-car" item-icon></iron-icon>
The <google-map-directions> element contains a travelMode property for specifying the type of directions to render. It has four possible values: "DRIVING", "WALKING", "BICYCLING", and "TRANSIT".
<google-map> element, add a <paper-tabs> with the travel mode options.<iron-icon> and <span> containing helper text.
<!-- selected="0" selects the first item in the tab list.
Change it to another index if you want a different default. -->
<paper-tabs selected="0">
<paper-tab label="DRIVING">
<iron-icon icon="maps:directions-car"></iron-icon>
<span>DRIVING</span>
</paper-tab>
<paper-tab label="WALKING">
<iron-icon icon="maps:directions-walk"></iron-icon>
<span>WALKING</span>
</paper-tab>
<paper-tab label="BICYCLING">
<iron-icon icon="maps:directions-bike"></iron-icon>
<span>BICYCLING</span>
</paper-tab>
<paper-tab label="TRANSIT">
<iron-icon icon="maps:directions-transit"></iron-icon>
<span>TRANSIT</span>
</paper-tab>
</paper-tabs>
<paper-tabs> is styled by default. It has a yellow material design ink effect and selection bar indication the selected tab. We can make it nicer!
Polymer uses CSS custom properties to expose styling hooks for internal parts of a component's Shady DOM. For example, paper-tabs exposes --paper-tabs-selection-bar-color to colorize the tab's selection bar.
<dom-module id="ing-atms">
<template>
<style>
:host {
@apply --layout-fit;
}
paper-tabs {
@apply --layout-fixed-left;
--paper-tabs-selection-bar-color: #4271F4;
background: #ffffff;
}
paper-tab {
--paper-tab-ink: #BBDEFB;
}
paper-tab.iron-selected {
background: rgb(66, 113, 244);
color: #ffffff;
}
paper-tab iron-icon {
margin-right: 10px;
}
</style>
...
</dom-module>
This bit of code will colorize the selection bar and ink ripple effect a nice material design blue.
The last thing you need to do is data-bind the travelMode property of <google-map-directions> to <paper-tabs>'s .selected property. The tabs updates this property whenever a new child item is selected. By default, its set to the index of the selected item. We need to override this in order to bind to the string value of each direction type. The attr-for-selected property does just that.
Bind the direction's travelMode property to the tab's selected property:
<google-map-directions
map="[[_oneWayBinding('map')]]" api-key="[[_oneWayBinding('apiKey')]]"
start-address="[[_oneWayBinding('startAddress')]]" end-address="[[_oneWayBinding('endAddress')]]"
travel-mode="[[_oneWayBinding('travelMode')]]" renderer-options="[[_oneWayBinding('renderOptions')]]">
</google-map-directions>
...
<paper-tabs attr-for-selected="label" selected="[[_twoWayBinding('travelMode')]]">
<paper-tab label="DRIVING">
<iron-icon icon="maps:directions-car"></iron-icon>
<span>DRIVING</span>
</paper-tab>
<paper-tab label="WALKING">
<iron-icon icon="maps:directions-walk"></iron-icon>
<span>WALKING</span>
</paper-tab>
<paper-tab label="BICYCLING">
<iron-icon icon="maps:directions-bike"></iron-icon>
<span>BICYCLING</span>
</paper-tab>
<paper-tab label="TRANSIT">
<iron-icon icon="maps:directions-transit"></iron-icon>
<span>TRANSIT</span>
</paper-tab>
</paper-tabs>
The map should automatically update to show different forms of travel:

You built an entire maps application complete with driving directions, ajax call and showing custom pins, and kept it under 150 lines of code
Think about that for a second. This just goes to show you the power of web components. They're reusable and composable. Zest in Polymer's data-binding features and you can create an entire app using nothing but declarative markup.