Neste codelab, você criará uma aplicação com o Google Maps utilizando elementos fornecidos pela coleção Google Web Components do Polymer. A aplicação será responsiva e incluirá instruções para direção e modo de trânsito. No decorrer do codelab, você também irá aprender mais sobre a característica de data-binding do Polymer e também sobre a coleção de elementos iron.

O que você aprenderá

Conhecimentos e recursos desejados

Como você avalia sua experiência com Polymer?

Iniciante Intermediário Avançado

Criando um novo projeto

A primeira vez que você executar o Chrome Dev Editor, ele irá pedir para que você configure seu ambiente de trabalho.

Abra o Chrome Dev Editor, inicialize um novo projeto e siga os passos a seguir:

  1. Clique no ícone para iniciar um novo projeto.
  2. Escreva "PolymerMapsCodelab" como Project name (Nome do projeto).
  3. No dropdown Project type (Tipo do projeto), escolha "JavaScript web app (using Polymer paper elements)".
  4. Clique no botão Create (Criar).

Após estas etapas, o Chrome Dev Editor irá criar a estrutura básica de sua aplicação Polymer. Incluindo já o Bower em background para download e instalação da lista de dependências do projeto (que inclui a biblioteca principal do Polymer) dentro do diretório bower_components/, mas não se preocupe, você irá aprender mais sobre isso e também sobre o que é o Bower a seguir.

Lembre-se que requisitar os componentes pode levar algum tempo dependendo da sua velocidade de conexão com a internet.

Sua estrutura de projeto deve ser semelhante a isto:

PolymerMapsCodelab/
  bower_components/ <!-- Dependências instaladas via Bower -->
  bower.json  <!-- Metadados para o Bower gerenciar dependências -->
  index.html  <!-- sua aplicação -->
  main.js
  styles.css

Pré-visualizando a aplicação

A qualquer momento neste codelab você poderá pré-visualizar mudanças na aplicação, e para isso, basta selecionar o arquivo index.html e apertar o botão e pronto, com esta simples ação, um servidor web será inicializado possibilitando a você navegar pelo conteúdo da página index.html.

Próximo passo

Neste momento, como observado acima, após a execução da nossa aplicação vemos que ela ainda não se parece muito com um mapa, sendo assim, vamos adicionar um mapa.

O conjunto Google Web Components do Polymer contém o elemento <google-map> que possibilita a renderização de um mapa através da API do Google Maps. Para utilizá-lo, você precisará instalar este elemento utilizando o Bower.

Instalando o elemento <google-map>

O Chrome Dev Editor não possui uma interface para adicionar comandos do Bower, sendo assim, você precisará editar manualmente o arquivo bower.json e incluir a dependência google-map, e então executar utilizando a funcionalidade Bower Update do Chrome Dev Editor, que basicamente irá checar as dependências dentro do bower.json e instalar as que não foram instaladas ainda.

  1. Altere o arquivo bower.json adicionando o elemento google-map como dependência:
"dependencies": {
  "iron-elements": "PolymerElements/iron-elements#^1.0.0",
  "paper-elements": "PolymerElements/paper-elements#^1.0.1",
  "google-map": "GoogleWebComponents/google-map#^1.1.0"
}
  1. Clique com o botão direito no arquivo bower.json no editor.
  2. E no menu de contexto escolha a opção Bower Update (Atualizar Bower).

O download será feito em alguns segundos, e após finalizá-lo, você poderá verificar o elemento <google-map> (e suas dependências) no diretório bower_components/google-map/.

Aplicando o elemento <google-map>

Para utilizar o elemento <google-map> você precisa:

  1. Usar um HTML Import para carregá-lo no arquivo index.html.
  2. Declarar uma instância do elemento na página.

No arquivo index.html, remova todos os outros HTML imports do <head> (deixe apenas o stylesheet,pois iremos utilizá-lo posteriormente), após a remoção aplique um único import apontando para google-map.html:

index.html

<head>
  ....
  <script src="bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
  <link rel="import" href="bower_components/google-map/google-map.html">

  <link rel="stylesheet" href="styles.css">
</head>

Em seguida, substitua o conteúdo do <body> com uma instância do <google-map>:

index.html

<body unresolved>
  <google-map latitude="37.779" longitude="-122.3892" zoom="13"></google-map>
</body>

Como você pode ver na declaração, o <google-map> é completamente declarativo. O mapa aqui será centralizado utilizando os atributos latitude e longitude e também o nível de zoom através do atributo zoom.

Adicionando estilo ao mapa

Se você executar o aplicativo agora, nada será exibido, pois para criar um container de exibição para o mapa (neste caso, <body>) é necessário aplicar uma altura fixa ao mesmo.

Para isso, abra o arquivo styles.css e substitua o conteúdo original pelo seguinte conteúdo:

styles.css

body, html {
  font-family: 'Roboto', Arial, sans-serif;
  height: 100%;
  margin: 0;
}

Adicionando um marcador

O elemento <google-map> suporta marcadores no mapa, para isso, basta declarar o elemento filho <google-map-marker>. E como feito para centralizar o mapa, para configurar a posição do marcador, utilize os atributos latitude e longitude.

Ainda no arquivo index.html, adicione ao elemento filho <google-map-marker> com o atributo draggable ao mapa da seguinte forma:

index.html

<google-map latitude="37.779" longitude="-122.3892" zoom="13" disable-default-ui>
  <google-map-marker latitude="37.779" longitude="-122.3892"
      title="Go Giants!" draggable="true"></google-map-marker>
</google-map>

Note que também desabilitamos os controles do mapa com o atributo disableDefaultUi e por ser uma propriedade booleana apenas a presença desse atributo transforma o mesmo em true.

Executando a aplicação

Neste momento, se você ainda não fez, pressione o botão e após tudo que já realizamos até aqui, você deverá ver um mapa preenchendo toda a área da janela com um pin (marcador).

Perguntas frequentes

Próximo passo

Vamos adicionar instruções de direção!

O elemento <google-map-directions> foi adicionado no momento em que instalamos o elemento <google-map>, e o primeiro por sua vez, provê informações para direção diretamente da API do Google Maps.

Aplicando o elemento <google-map-directions>

Para aplicar o elemento <google-map-directions>:

  1. Use um HTML Import para carregá-lo no arquivo index.html.
  2. Declare uma instância do elemento na página
  3. E por fim, precisamos "conectá-lo" ao nosso mapa.

No index.html, adicione um HTML Import apontando para google-map-directions.html:

index.html

<head>
  ....
  <script src="bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
  <link rel="import" href="bower_components/google-map/google-map.html">
  <link rel="import" href="bower_components/google-map/google-map-directions.html">
</head>

Declare o elemento <google-map-directions> como um elemento sibling (irmão) do elemento <google-map> e adicione o atributo start-address com o valor "San Francisco" e o atributo end-address com o valor "Mountain View".

index.html

<body unresolved>
  <google-map latitude="37.779" longitude="-122.3892" zoom="13" disable-default-ui>
    ...
  </google-map>
  <google-map-directions
      start-address="San Francisco"
      end-address="Mountain View"></google-map-directions>
</body>

Desenhando as direções no mapa

O elemento <google-map-directions> requisita as direções, mas nada além disso. Você precisa conectar os resultados do <google-map-directions> ao <google-map>, dessa forma as direções serão renderizadas no mapa.

Ambos os elementos expõem uma propriedade .map, que por sua vez, permite acessar/configurar um objeto Map (usado pela API JavaScript do Google Maps), sendo assim, para fazer seus dois elementos conversarem, eles deverão utilizar o mesmo objeto Map.

E para realizar tal operação, basta adicionar o seguinte código no rodapé do arquivo index.html:

index.html

...

<script>
  var gMap = document.querySelector('google-map');
  gMap.addEventListener('api-load', function(e) {
    document.querySelector('google-map-directions').map = this.map;
  });
</script>
</body>

Nota: É preciso esperar até que o mapa emita o evento api-load, para assegurar que o mapa estará pronto para uso.

Executando a aplicação

Ao pressionar o botão você verá o mapa, um marcador e as direções desenhadas no mapa entre San Francisco (A) e Mountain View (B).

Próximo passo

Você criará um meio para que o usuário possa adicionar endereços de início e fim de trajeto através da funcionalidade de data-binding do Polymer.

Para começar, adicionaremos inputs para permitir que o usuário adicione endereços de inicio e fim de trajeto, e para isso, vamos utilizar os seguintes elementos do Polymer: paper-card, paper-item, paper-input, iron-icon e iron-icons.

Para declarar nossos inputs, abra o arquivo index.html e adicione os seguintes componentes via HTML Import:

index.html

<head>
  ....
  <link rel="import" href="bower_components/iron-icon/iron-icon.html">
  <link rel="import" href="bower_components/iron-icons/iron-icons.html">
  <link rel="import" href="bower_components/paper-item/paper-item.html">
  <link rel="import" href="bower_components/paper-item/paper-icon-item.html">
  <link rel="import" href="bower_components/paper-input/paper-input.html">
  <link rel="import" href="bower_components/paper-card/paper-card.html">
</head>

No final da página, adicione dois elementos <paper-icon-item> e dois <paper-input> para possibilitar a entrada dos endereçoes de ínicio e fim de trajeto. Lembre-se que o elemento <paper-icon-item> é um helper que facilita a criação de um item com ícone.

<paper-icon-item>
  <paper-input label="Start address" value="San Francisco">
</paper-icon-item>
<paper-icon-item>
  <paper-input label="End address" value="Mountain View">
</paper-icon-item>

Adicionando ícones de busca

O elemento <iron-icon> é bastante útil para exibição de ícones, basta adicionar o atributo icon com referência a algum valor existente no conjunto de ícones fornecidos pelo Polymer e tudo funcionará.

  1. Dentro de cada item, adicione um elemento <iron-icon>.
  2. E por fim, adicione o atributo/valor icon="search".
<paper-icon-item>
  <iron-icon icon="search" item-icon></iron-icon>
  <paper-input label="Start address" value="San Francisco">
</paper-icon-item>
<paper-icon-item>
  <iron-icon icon="search" item-icon></iron-icon>
  <paper-input label="End address" value="Mountain View">
</paper-icon-item>

Adicionando um card do Material Design

Neste momento, envolva seus inputs com o elemento <paper-card>,que por sua vez, é uma abstração do componente card encontrado no Material Design. Utilizar este tipo de componente é sempre uma ótima abordagem quando a intenção é destacar elementos de sua aplicação.

<paper-card elevation="2"> <!-- nível de elevação de sombra do card -->
  <paper-icon-item>
    ...
  </paper-icon-item>
  <paper-icon-item>
    ...
  </paper-icon-item>
</paper-card>

Agora, no arquivo styles.css, adicione um estilo para o card para que ele seja exibido na parte inferior esquerda da tela e por cima do mapa.

styles.css

paper-card {
  position: absolute;
  bottom: 25px;
  left: 25px;
  z-index: 1;
}

Próximo passo

Em seguida, você aprenderá mais sobre as funcionalidades relacionadas à data-binding entregues pelo Polymer utilizando os valores inseridos aos campos recém criados.

In the last step, we created search inputs for the driving directions. In this step of the codelab, we'll hook them up to power <google-map-directions>.

Na última etapa, você criou os inputs para adicionar início e fim de trajeto, certo? Nesta etapa do codelab, iremos então integrá-los ao nosso elemento <google-map-directions>.

Aplicando data-binding fora do Polymer

As funcionalidades de data-binding estarão disponíveis somente quando criamos um componente utilizando o <dom-module>, porém o Polymer fornece uma versão extendida do elemento <template> chamada "dom-bind", que basicamente irá de forma implícita fornecer funcionalidades do Polymer fora do contexto do mesmo, ou seja, isto irá por exemplo permitir o uso direto de {{}} para data-binding na sua página principal.

Sendo assim, com essa propriedade definida em um elemento <template is="dom-bind">, será possível atrelar dados facilmente da seguinte forma:

<template is="dom-bind" id="t">
  <!-- Hello, Eric. Como você está hoje? -->
  Hello, <span>{{name}}</span>. <span>{{greeting}}</span>
</template>

<script>
  var t = document.querySelector('#t');
  t.name = 'Eric';
  t.greeting = 'Como você está hoje??';
</script>

Percebeu como funciona? Ao usar a técnica acima, você simplesmente está adicionando valores nas propriedades name e greeting do template.

Atribuindo dados no mapa ↔ elemento de direções

No último passo, você escreveu o seguinte código JavaScript para atribuir direções a propriedade map:

document.querySelector('google-map-directions').map = this.map;


Os elementos de mapa e direção declaram uma propriedade map em seu objeto properties. Isso quer dizer que você pode usar um atributo de mesmo nome para aplicar data-bind em duas propriedades juntas.

  1. No arquivo index.html, remova a tag <script> que foi adicionada no 4º passo.
  2. Envolva o markup (marcação) existente da tag <body> dentro de um elemento <template is="dom-bind">.
  3. Adicione o atributo map nos elementos <google-map> e <google-map-directions>, certificando-se de que tudo está sempre dentro do elemento <template is="dom-bind">, visto que ele será o responsável por atribuir o valor do atributo map em ambos, ou seja, quando um for modificado, isso será refletido no outro.
<template is="dom-bind">
  <google-map map="{{map}}" ...></google-map>
  <google-map-directions map="{{map}}"...></google-map-directions>
</template>

Nota: Usamos o valor {{map}}como nome da propriedade para o binding, mas você pode usar qualquer nome que desejar (por exemplo: map="{{foo}}").

Atribuindo dados dos campos de endereço ↔ elemento de direções

Nesta etapa, as propriedades startAddress e endAddress estão forçando respectivamente o uso dos valores "San Francisco" e "Mountain View".

<google-map-directions
    start-address="San Francisco" end-address="Mountain View"
    libraries="places">
</google-map-directions>


Assim como os campos de endereço:

<paper-input label="Start address" value="San Francisco">
<paper-input label="End address" value="Mountain View">

Podemos e vamos deixar isto dinâmico através da atribuição dos valores inseridos nos campos de endereços aos atributos do elemento <google-map-directions>.

index.html

<template is="dom-bind">
    
<google-map map="{{map}}" latitude="37.779" longitude="-122.3892"
    disable-default-ui zoom="13">
  <google-map-marker latitude="37.779" longitude="-122.3892"
      title="Go Giants!" draggable="true"></google-map-marker>
</google-map>

<google-map-directions map="{{map}}"
    start-address="{{start}}"
    end-address="{{end}}"></google-map-directions>

<paper-card elevation="2">
  <paper-icon-item>
    <iron-icon icon="search" item-icon></iron-icon>
    <paper-input label="Start address" value="{{start}}">
  </paper-icon-item>
  <paper-icon-item>
    <iron-icon icon="search" item-icon></iron-icon>
    <paper-input label="End address" value="{{end}}">
  </paper-icon-item>
</paper-card>

</template>

O nome utilizado em cada declaração de {{}} não importa, porém cada um deve coincidir com as propriedades que você quer atribuir juntas.

Executando a aplicação

  1. Primeiramente, pressione o botão como de costume.
  2. Coloque o valor CA no campo Start address.
  3. Insira o valor NYC no campo End address.

Você verá o mapa atualizar sozinho a rota da California para New York:

Teste outros destinos! Você verá o mapa atualizando o desenho das direções sozinho utilizando estas outras coordenadas de teste.

index.html

<body>
  <template is="dom-bind">
    
    <google-map map="{{map}}" latitude="37.779" longitude="-122.3892"
        disable-default-ui zoom="13">
      <google-map-marker latitude="37.779" longitude="-122.3892"
          title="Go Giants!" draggable="true"></google-map-marker>
    </google-map>

    <google-map-directions map="{{map}}"
        start-address="{{start}}"
        end-address="{{end}}"></google-map-directions>

    <paper-card elevation="2">
      <paper-icon-item>
        <iron-icon icon="search" item-icon></iron-icon>
        <paper-input label="Start address" value="{{start}}">
      </paper-icon-item>
      <paper-icon-item>
        <iron-icon icon="search" item-icon></iron-icon>
        <paper-input label="End address" value="{{end}}">
      </paper-icon-item>
    </paper-card>

  </template>

</body>

Próximo passo

Permitir que o usuário selecione os tipos de direção (andando, trânsito e dirigindo).

Importando componentes para esta etapa do codelab

No arquivo index.html, adicione os seguintes HTML Imports dentro da tag <head>:

index.html

<head>
  ....
  <link rel="import" href="bower_components/iron-icons/maps-icons.html">
  <link rel="import" href="bower_components/paper-tabs/paper-tabs.html">
</head>

Nota: O componente maps-icons é obrigatório para carregar um mapeamento específico do conjunto de ícones maps fornecido pelo Polymer, que se fará necessário no momento da renderização dos ícones que simbolizarão os modos de viagem.

Criando o seletor de modo de viagem

Para criar o seletor de modo de viagem utilizaremos o elemento <paper-tabs>, ele será a interface responsável em deixar que o usuário escolha o modo de viagem preferido. E para cada modo de viagem iremos usar um ícone fornecido pelo conjunto maps carregado previamente, sendo assim, para utilizarmos um outro ícone de outro conjunto de ícones em nossas aplicações, basta colocar <iconset>:<name> como valor do atributo icon, onde <iconset> é o nome do conjunto de ícones (por exemplo: "maps") e <name> é o nome do ícone dentro daquele conjunto de ícones.

Como exemplo, o seguinte código utiliza o ícone directions-car do conjunto de ícones maps:

<iron-icon icon="maps:directions-car" item-icon></iron-icon>

O elemento <google-map-directions> possui uma propriedade chamada travelMode, que por sua vez permite que seja especificado o tipo de direção que queremos utilizar para cálculo e desenho no mapa. Temos quatro valores possíveis: "DRIVING", "WALKING", "BICYCLING", e "TRANSIT".

  1. No arquivo index.html dentro do elemento <paper-card> adicione um elemento <paper-tabs> com as opções que desejamos colocar para o modo de viagem.
  2. Para cada modo de viagem adicione um elemento <iron-icon> e um <span> contendo um texto de ajuda.

index.html

<paper-card elevation="2">
...

<!-- selected="0" escolhe qual será o primeiro item de uma lista de tabs.
     Você pode alterar para outra tab caso queira usar. -->
<paper-tabs selected="0">
  <paper-tab>
    <iron-icon icon="maps:directions-car"></iron-icon>
    <span>DRIVING</span>
  </paper-tab>
  <paper-tab>
    <iron-icon icon="maps:directions-walk"></iron-icon>
    <span>WALKING</span>
  </paper-tab>
  <paper-tab>
    <iron-icon icon="maps:directions-bike"></iron-icon>
    <span>BICYCLING</span>
  </paper-tab>
  <paper-tab>
    <iron-icon icon="maps:directions-transit"></iron-icon>
    <span>TRANSIT</span>
  </paper-tab>
</paper-tabs>

</paper-card>

Estilizando as tabs

O elemento <paper-tabs> possui um estilo padrão, um efeito ink do Material Design e uma barra que indica qual tab está selecionada, porém deixaremos ele melhor ainda.

O Polymer fornece uma série de propriedades CSS customizadas que ajudam o desenvolvedor estilizar elementos encapsulados via Shady DOM de um componente. Para nosso exemplo, vamos utilizar a propriedade CSS --paper-tabs-selection-bar-color que é exposta pelo elemento paper-tabs para que possamos colorir facilmente a barra da tab selecionada.

Propriedades CSS customizadas serão padronizadas nos browsers em breve, mas enquanto elas não vem, o Polymer provê a extensão <style is="custom-style">, que como pode ser notado, é uma extensão da tag <style> e que permite encapsular e processar todos os estilos customizados fornecidos pelo Polymer. Para utilizar este elemento, basta definí-lo dentro do elemento <head> da sua página principal, como no exemplo abaixo:

index.html

<head>
  ...
  <style is="custom-style">
    paper-tabs {
      --paper-tabs-selection-bar-color: #0D47A1;
      margin-top: 16px;
    }
    paper-tab {
      --paper-tab-ink: #BBDEFB;
    }

    /* Outros estilos que dão poder para realizar coisas incríveis, porém estes podem ser adicionados no arquivo styles.css, já que não utilizam nenhuma funcionalidade do Polymer */
    paper-tab iron-icon {
      margin-right: 10px;
    }
    paper-tab.iron-selected {
      background: rgb(66, 133, 244);
      color: white;
    }
  </style>
</head>

Este pequeno trecho de código irá colorir a barra de seleção e aplicará o efeito ink ripple com a cor material design azul.

Atribuindo o valor do seletor modo de viagem ↔ elemento de direções

Para finalizar nossa aplicação a última coisa que precisamos fazer é atribuir via data-binding o valor da propriedade travelMode do elemento <google-map-directions> utilizando a propriedade .selected do elemento <paper-tabs>. O elemento já atualiza a propriedade automaticamente toda vez que é selecionada uma nova tab. Por padrão o valor retornado é o index de cada tab, sendo assim, precisamos sobrescrever isso para que seja possível atribuir um valor do tipo string para cada um dos tipos de direção, e nesta tarefa vamos contar com a propriedade attr-for-selected, visto que ela existe justamente para isto.

Abaixo segue o código de como vamos atribuir um valor a propriedade selected e também o valor da tab selecionada no travelMode:

index.html

<google-map-directions map="{{map}}"
      start-address="{{start}}"
      end-address="{{end}}"
      travel-mode="[[travelMode]]"></google-map-directions>
...

<paper-card>

<paper-tabs selected="{{travelMode}}" attr-for-selected="label">
  <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-card>

Executando a aplicação

  1. Pressione o botão .
  2. Coloque SF no campo Start address.
  3. Coloque Oakland, CA no campo End address.
  4. Teste os diferentes modos de viagem.

O mapa deve atualizar automaticamente para mostrar diferentes formas de viajar:

Você acabou de construir uma aplicação com mapa e com instruções de direção e tudo isso sem escrever uma linha de código JavaScript.

Pense sobre isso por um segundo. Isto é apenas a ponta do iceberg relativa ao poder promovido pelos web components, visto que abordamos apenas sua reusabilidade e composição, e isso, sem contar o poder do data-binding do Polymer e também toda a sua forma declarativa de trabalhar tornando tudo mais simples.

O que você aprendeu

Outras referências

Polymer

Outros