Neste codelab você irá aprender como começar a desenvolver aplicações Polymer com o Polymer Starter Kit e código JavaScript com sintaxe ES2015 - formalmente conhecida como ES6 - mantendo-o compatível com todos os browsers suportados pelo Polymer.

O que você irá aprender

Conhecimentos e recursos necessários

Como você irá usar este codelab?

Somente para leitura Leitura e resolução dos exercícios

Qual seu atual nível de experiência trabalhando com o Polymer?

Iniciante Intermediário Avançado

Qual seu atual nível de experiência com o ES2015?

Iniciante Intermediário Avançado

Para fazer este codelab você precisará de um editor de texto, caso você ainda não possua um editor favorito, você pode baixar o Chrome Dev Editor. Você também irá precisar executar alguns comandos no terminal, neste caso use o de sua preferência ou o padrão do seu sistema operacional.

Download e instalação do Polymer Starter Kit

Download Polymer Starter Kit v1.1.0

Faça o download do Polymer Start Kit v1.1.0 através do botão acima, porém tenha em mente que neste momento você irá baixar a versão completa (existe também uma versão "light"), pois ela contém um sistema de build completo e funcionalidades adicionais que iremos utilizar no decorrer do codelab.

Após o download descompacte o arquivo baixado em um local de sua preferência, mas lembre-se que será neste local que você irá trabalhar daqui pra frente.

Para começar, acesse o local onde você descompactou o Polymer Starter Kit v.1.1.0 via linha de comando e execute o comando bower install para instalar uma lista de dependências (incluindo o Polymer) dentro do diretório bower_components/

Você também precisará executar o comando npm install para instalar as ferramentas e dependências necessárias, como o Gulp e seus plugins, dentro do diretório node_modules/

A estrutura do seu projeto deverá estar assim agora:

polymer-starter-kit-1.1.0/
  .jscsrc  <!-- Configurações do JSCS -->
  .jshintrc  <!-- Configurações do JSHint -->
  app/  <!-- Arquivos de app (html/js/imagens) -->
  bower_components/ <!-- Dependências instaladas via Bower -->
  bower.json  <!-- Metadados para o Bower gerenciar dependências -->
  gulpfile.js  <!-- Regras de build da aplicação -->
  node_modules/ <!-- Dependências instaladas via npm -->
  package.json  <!-- Metadados para o npm gerenciar dependências -->
  ...



O Polymer Starter Kit disponibiliza uma estrutura de diretórios e arquivos para iniciarmos nosso projeto. Os arquivos da aplicação podem ser encontrados no diretório app/. Nas próximas seções veremos mais informações sobre as funcionalidades desta estrutura.

Pré-visualizando a aplicação

Utilizamos o Gulp como nossa ferramenta para build. Ao executarmos npm install instalamos o Gulp e todos os plugins e ferramentas listados no package.json. O Gulp utiliza um arquivo chamado gulpfile.js onde são definidos todas as tarefas de build da aplicação.

Para pré-visualizar a aplicação execute gulp serve, este comando irá montar a aplicação e iniciar um servidor para desenvolvimento que poderá ser acessado no endereço http://localhost:5000

Qualquer alteração nos arquivos de sua aplicação automaticamente deve invocar um build parcial e o navegador em execução irá recarregar a página, deste modo você sempre irá acompanhar suas alterações em tempo real.

Por que existe uma etapa para compilação/build da aplicação?

Isto não é geralmente obrigatório para construir aplicações client-side, então você deve estar se perguntando, porque o Polymer Starter Kit vem com esse sistema de build próprio? E para responder, basta saber o que acontece ao executar o comando gulp no seu terminal:

Resumindo, o uso do sistema entregue pelo Polymer Starter Kit para build simplificará seu desenvolvimento e aplicará boas práticas deixando assim suas aplicações prontas para produção.

Durante este codelab usaremos a sintaxe ES2015 através de classes, arrow functions entre outros, e embora o suporte do ES2015 esteja bem contemplado nos browsers modernos, nenhum implementou até o momento todas as features propostas pelo padrão, sendo assim, para usufruir de todos os benefícios da nova sintaxe proposta pelo ES2015, você precisará transpilar seu código ES2015 para ES5 usando uma ferramenta chamada BabelJS.

Ao chegar até aqui, teremos uma boa noção de ambiente para iniciarmos o uso do Polymer Start Kit, apenas teremos que adicionar mais uma etapa antes de começar a escrever código, e esta última etapa é a do transpilador.

Criando uma tarefa de transpilação no gulp

Instale os plugins do gulp: Babel, Sourcemap e Crisper utilizando o comando:

npm install --save-dev gulp-babel gulp-sourcemaps gulp-crisper babel-preset-es2015

Adicione a seguinte task no arquivo gulpfile.js:

gulpfile.js

// Transpila todos os arquivos JavaScript para ES5
gulp.task('js', function () {
 return gulp.src(['app/**/*.{js,html}'])
   .pipe($.sourcemaps.init())
   .pipe($.if('*.html', $.crisper())) // Extrai JavaScript de arquivos html
   .pipe($.if('*.js', $.babel({
     presets: ['es2015']
   })))
   .pipe($.sourcemaps.write('.'))
   .pipe(gulp.dest('.tmp/'))
   .pipe(gulp.dest('dist/'));
});

Esta tarefa irá transpilar todos os arquivos JavaScript e códigos JavaScript inline dentro de arquivos HTML, e também irá gerar sourcemaps para ES5 dentro dos diretórios .tmp e dist.

Integrando a tarefa de transpilação

Você precisará ter certeza que a tarefa js recém criada é disparada e executada pelas tarefas de construção, para isso siga os passos abaixo.


Primeiramente, na tarefa serve do gulp, tenha certeza que a tarefa js está sendo chamada ao iniciar serve e também em futuras modificações de arquivos HTML e JavaScript:

gulpfile.js

gulp.task('serve', ['styles', 'elements', 'images', 'js'], function () { // Adicione 'js' aqui!

  ...

  gulp.watch(['app/**/*.html'], ['js', reload]); // Adicione 'js' dentro do array aqui!
  gulp.watch(['app/styles/**/*.css'], ['styles', reload]);
  gulp.watch(['app/elements/**/*.css'], ['elements', reload]);
  gulp.watch(['app/{scripts,elements}/**/*.js'], ['jshint', 'js']); // Adicione 'js' desta forma aqui!
  gulp.watch(['app/images/**/*'], reload);
});

Seguindo o modelo acima, você irá ter certeza que a tarefa js está funcionando perfeitamente com todo o workflow fornecido previamente pelo Polymer Starter Kit, sendo assim, toda vez que você modificar um arquivo HTML ou JavaScript que estiverem sendo observados pela tarefa gulp serve aquele código JavaScript será transpilado em tempo de desenvolvimento e também recarregará a página da aplicação.

Agora, na tarefa default tenha certeza que a tarefa js está rodando em paralelo com a tarefa elements:

gulpfile.js

gulp.task('default', ['clean'], function (cb) {
  // Descomente o 'cache-config' depois do 'rename-index' se você quiser usar Service Workers
  runSequence(
    ['copy', 'styles'],
    ['elements', 'js'],  // Adicione 'js' no array!
    ['jshint', 'images', 'fonts', 'html'],
    'vulcanize','rename-index', // 'cache-config',
    cb);
});

Assim você irá garantir que a tarefa js está sendo executada toda vez que você compila sua aplicação.

A tarefa html deixa os códigos e arquivos HTML, CSS e JavaScript prontos para produção através de minificação e concatenação dos arquivos separados em arquivos únicos, sendo assim, precisaremos nos certificar de que não serão utilizados os arquivos JavaScript originais nesta etapa, pois os mesmos não irão funcionar em todos os browsers, e para isso, basta remover o diretório app da declaração useref na tarefa html.

gulpfile.js

// Busca no HTML por Assets e os otimiza
gulp.task('html', function () {
  var assets = $.useref.assets({searchPath: ['.tmp', 'dist']}); // O 'app' foi removido daqui!

  ...

});

Configurando linters para ES2015

O Polymer Starter Kit vem com um arquivo de configuração para JSCS e o JSHint. JSCS é um linter que garante ao desenvolver seguir um único guia de estilo de código e o JSHint por sua vez irá analisar seu código para localizar problemas comuns de mal uso da linguagem JavaScript. Neste caso, para habilitar suporte do JSCS e do JSHint ao ES2015, adicione "esnext": true ao arquivo .jscsrc:

.jscsrc

{
  "esnext": true, // Adicione esta linha!
  "preset": "google",
  "disallowSpacesInAnonymousFunctionExpression": null,
  "excludeFiles": ["node_modules/**"]
}

e no arquivo .jshintrc:

.jshintrc

{
  "esnext": true, // Adicione esta linha!
  "node": true,
  "browser": true,

  ...

}

Checando se tudo está funcionando corretamente!

Agora, vamos ver se tudo está funcionando corretamente até aqui, e para isso, execute o comando gulp para iniciar um build.

Ao finalizar o build confira se um diretório .tmp foi criado e se ele contém os arquivos abaixo dentro de elements/my-list/ e scripts/:

polymer-starter-kit-1.1.0/
  .tmp/  <!-- Diretório temporário usado para build -->
    elements/  <!-- Custom elements do Polymer -->
      my-list/  <!-- Exemplo de diretório de custom element do Polymer -->
        my-list.html  <!-- Template do custom element 'my-list' -->
        my-list.js  <!-- Código JavaScript separado do HTML pelo Crisper -->
    ...
    scripts/  <!-- Diretório contendo código JavaScript que não são elementos Polymer -->
      app.js  <!-- Um arquivo JavaScript -->
      app.js.map  <!-- Source map do arquivo app.js -->
  ...

O exemplo acima, nos mostra que nossa nova tarefa está funcionando exclusivamente para separar código JavaScript de arquivos HTML dos elementos criados com Polymer e também gerando os source maps de arquivos transformados gerados.

E é isso! Agora você será capaz de escrever código JavaScript utilizando a sintaxe ES2015 na sua aplicação. E é isto que iremos fazer a partir de agora.

Agora veremos como criar um custom element com Polymer e a sintaxe ES2015. Vamos fazer um simples Gerador de Memes e adicioná-lo em uma página de nossa aplicação:

Criando seu custom element

Crie o seguinte diretório: app/elements/awesome-meme/

Neste diretório, crie um arquivo chamado awesome-meme.html, este arquivo irá conter o código de seu custom element.

Agora você precisa de um meme para colocar no plano de fundo, para isso, basta salvar esta imagem no seu diretório app/images/ com o nome awesome.png.

Para criar o elemento Polymer "awesome-meme" definimos um <dom-module> que irá conter <style>, <template> e <script>, e tudo isso, vamos adicionar no arquivo awesome-meme.html:

awesome-meme.html

<link rel="import" href="../../bower_components/polymer/polymer.html">

<dom-module id="awesome-meme">
  <template>
    <style>
      /* TODO: Adicionar CSS */
    </style>

    <!-- TODO: Adicionar o conteúdo HTML do template-->
  </template>

  <script>
    /* TODO: Criar definição do elemento Polymer usando ES2015 */
  </script>
</dom-module>

O gerador irá ter apenas dois blocos de texto configuráveis sobre a imagem de fundo, sendo assim, adicione isto dentro da tag <template>:

awesome-meme.html

<div class="top">{{top}}</div>
<div class="bottom">{{bottom}}</div>

Os dois blocos de texto irão ser posicionados de forma absoluta, e para configurar isso, adicione os seguintes estilos dentro da tag <style>:

awesome-meme.html

:host {
  display: block;
  background-image: url("/images/awesome.png");
  width: 300px;
  height: 300px;
  position: relative;
}
.top, .bottom{
  position: absolute;
  font-size: 30px;
  font-weight: bold;
  width: 100%;
  text-align: center;
}
.top {
  top: 20px;
}
.bottom {
  bottom: 20px;
}

Agora, você irá escrever o elemento Polymer utilizando a definição de classe proposta pelo ES2015, que no caso, será bem simples. Para começar, defina as propriedades top e bottom e capitalize qualquer texto que entrar nelas. Para isso, adicione o seguinte código dentro da tag <script> no arquivo awesome-meme.html:

awesome-meme.html

class AwesomeMeme {
  beforeRegister() {
    this.is = 'awesome-meme';
    this.properties = {
      top: {
        type: String,
        value: ''
      },
      bottom: {
        type: String,
        value: ''
      }
    };
  }
  created() {}
  ready() {
    this.top = this.top.toUpperCase();
    this.bottom = this.bottom.toUpperCase();
  }
  attached() {}
  detached() {}
  attributeChanged() {}
}

Polymer(AwesomeMeme);

No nosso componente, estão listadas todas as funções de callback (beforeRegister, created, ready, attached, detached, attributeChanged) de um elemento Polymer apenas para fins educacionais, sendo assim, sinta-se livre em remover as funções created() {}, attached() {}, detached() {} e attributeChanged() {}.

Usar classes para definir elementos com Polymer permitirá uma melhor leitura e manutenção de código, assim como facilitará trabalhar com heranças.

Opcionalmente, você também pode atualizar o código JavaScript dos outros dois Polymer custom elements para ES2015 na nossa aplicação. Você pode encontrá-los em:

Aplicando seu custom element na aplicação

Agora você pode usar e configurar seu novo elemento Polymer em sua aplicação. Para fazer isso você precisa importar o elemento na página principal de sua app. Adicione a linha de código abaixo no arquivo app/elements/elements.html:

elements.html

...

<link rel="import" href="../styles/app-theme.html">
<link rel="import" href="my-greeting/my-greeting.html">
<link rel="import" href="my-list/my-list.html">
<!-- Adicione a linha abaixo -->
<link rel="import" href="awesome-meme/awesome-meme.html">

Agora você pode declarar seu elemento Meme na aplicação, para fins de linearidade do codelab, coloque o mesmo dentro do segundo Card declarado no arquivo app/index.html:

index.html

...

<paper-material elevation="1">
  <p class="paper-font-body2">This is another card.</p>
  <!-- Adicione a linha abaixo -->
  <awesome-meme top="ES2015" bottom="is awesome!"></awesome-meme>
</paper-material>

...

E para finalizar, execute o comando gulp serve para ver o resultado:

Vamos agora dar uma olhada em outros arquivos JavaScript que vem junto com o Polymer Starter Kit e "atualizá-los" usando a sintaxe do ES2015.

Regras de Routing

Para começar, verifique o conteúdo do arquivo app/elements/routing.html e troque todas as funções anônimas para arrow functions:

routing.html

<script src="../../bower_components/page/page.js"></script>
<script>
  window.addEventListener('WebComponentsReady', () => { // Arrow function!

    // Utilizamos o Page.js para rotas, que é um Micro
    // client-side router inspirado no Express router
    // Mais informações em: https://visionmedia.github.io/page.js/
    page('/', () => { // Arrow function!
      app.route = 'home';
      app.scrollPageToTop();
    });

    page('/users', () => { // Arrow function!
      app.route = 'users';
      app.scrollPageToTop();
    });

    page('/users/:name', data => { // Arrow function!
      app.route = 'user-info';
      app.params = data.params;
      app.scrollPageToTop();
    });

    page('/contact', () => { // Arrow function!
      app.route = 'contact';
      app.scrollPageToTop();
    });

    // add #! before urls
    page({
      hashbang: true
    });
  });
</script>

Arrow functions oferecem uma forma rápida e legível de escrever funções anônimas. Além disso, arrow functions fazem automaticamente o bind do this.

Teste o código recém alterado clicando em Users e Contact no menu da aplicação. As páginas correspondentes deverão ser exibidas.

Código da Aplicação

Agora no arquivo app/scripts/app.js vamos iniciar trocando as funções anônimas por arrow functions como feito anteriormente e em seguida trocaremos todos os var por let:

app.js

...

let app = document.querySelector('#app');

...

  let appName = document.querySelector('.app-name');
  let middleContainer = document.querySelector('.middle-container');
  let bottomContainer = document.querySelector('.bottom-container');
  let detail = e.detail;
  let heightDiff = detail.height - detail.condensedHeight;
  let yRatio = Math.min(1, detail.y / heightDiff);
  let maxMiddleScale = ...
  let scaleMiddle = ...
  let scaleBottom = 1 - yRatio;

...

  let drawerPanel = document.querySelector('#paperDrawerPanel');

...

Após a alteração, você estará utilizando somente variavéis com escopo de bloco. Neste caso em particular essa alteração não faz muita diferença pois todas as variáveis são declaradas no topo de uma função (logo escopo de bloco === escopo da função), no entanto o uso do let é uma boa prática pois geralmente é o que será necessário e torna seu código mais claro e robusto.

E para finalizar, o ES2015 também tem uma funcionalidade incrível chamada template Strings que basicamente permite interpolar strings. Vamos modificar as 3 concatenações para o uso de template Strings deixando o código mais elegante da seguinte forma:

app.js

...

// Move/translate middleContainer
Polymer.Base.transform(`translate3d(0,${yRatio * 100}%,0)`, middleContainer);

// Scale bottomContainer and bottom sub title to nothing and back
Polymer.Base.transform(`scale(${scaleBottom}) translateZ(0)`, bottomContainer);

// Scale middleContainer appName
Polymer.Base.transform(`scale(${scaleMiddle}) translateZ(0)`, appName);

...

Para verificar se tudo está funcionando após as alterações, veja se o comando gulp serve ainda está sendo executado, volte para seu browser para ter certeza que tudo está funcionando como esperado, ou seja, verifique se o título encolhe após o scroll.

Parabéns! Você acabou! Sinta se livre para comparar o que você fez com o código disponível na seguinte url: https://github.com/googlecodelabs/psk-es2015

Você acabou de criar um custom element desenvolvido com Polymer utilizando a sintaxe do ES2015 e com garantia de compatibilidade com todos os browsers suportados pelo Polymer!

O que você aprendeu

Outras referências

Mas não se deixe enganar, há mais para se aprender sobre as configurações que o Polymer Starter Kit oferece como suporte à Service Workers, Testes automatizados, etc.

Polymer

Outros