Primeiros passos com o Angular Material

Veja nesse artigo, como explorar a biblioteca do Angular Material, desde instalação, configuração e uso, bem como conceitos em relação ao seu uso responsivo nas páginas web, principais componentes, etc. Confira agora.

Fique por dentro
Desenvolver aplicações web sempre requer um enorme esforço de design, estruturação e modularização dos templates que serão reaproveitados ao longo do ciclo de desenvolvimento da solução, principalmente nos dias de hoje, onde os requisitos de aparência de um site são cada vez mais exigentes. Em função disso, esse artigo se mostra útil ao abordar tal realidade através do framework Angular Material. Trata-se de uma união mais que bem-vinda dos já idealizados frameworks do Google: Material Design (especificação oficial da empresa para designs na web/mobile) e o AngularJS. Aqui, você aprenderá a usufruir ao máximo dos recursos de ambos através da construção de exemplos simples e funcionais.

Na última conferência do Google I/O em 2014, o Google anunciou o Material Design, a sua nova linguagem de design, tanto para a web quanto para dispositivos móveis. Eles já haviam convertido grande parte de suas aplicações populares para aderir a essa nova especificação, se esforçando para fornecer uma experiência consistente a seus usuários.

Depois de conhecer o Material Design, você irá obter imediatamente um sentimento minimalista ultramoderno em relação a suas aplicações. O objetivo é criar uma linguagem visual que sintetize princípios clássicos de um bom design juntamente à inovação e tecnologia. Também será possível desenvolver um único sistema subjacente que permite uma experiência unificada em várias plataformas e em diversos tamanhos de dispositivos. O Material Design é fundado em três princípios fundamentais:


Veja também se isso pode de interessar:


Como o AngularJS cabe no Material Design?

O AngularJS, o framework JavaScript MVW (um acrônimo alternativo ao MVC, que significa Model-View-Whatever) do Google, aborda muitos dos desafios encontrados no desenvolvimento de aplicações single-page. Ele fornece o framework necessário para a criação de aplicações web modernas que se conectam a APIs e nunca precisam de uma página para serem atualizadas.

O Angular seria a HTML se tivesse sido projetada para aplicações. A HTML é uma grande linguagem declarativa para documentos estáticos, porém nem tanto para a criação de aplicações dinâmicas. A criação de aplicações dinâmicas com a HTML tem sido sempre um exercício de enganar o navegador para fazer coisas que não deveria fazer. Existe um par de abordagens para fazer isso:

  1. Biblioteca – uma coleção de funções. (Por exemplo: o jQuery)
  2. Framework – o código preenche dinamicamente os elementos estáticos, quando necessário.

O Angular tem uma abordagem diferente para resolver esse problema. Ao invés de lutar com o HTML dado, ele cria novas construções HTML. O Angular ensina ao navegador uma nova sintaxe HTML através de uma construção chamada “diretivas”. Além de vir com um conjunto delas internamente, também permite que criemos diretivas personalizadas, podendo escrever nossos próprios elementos HTML.

Introdução ao Angular Material

O Google está desenvolvendo ativamente o Angular Material, uma implementação do Material Design no AngularJS. O Angular Material é composto por várias peças, possui uma biblioteca de CSS para a tipografia e outros elementos, fornece uma abordagem JavaScript interessante para theming (definição de temas) e seu layout responsivo usa uma grade do tipo flex. Mas a característica mais atraente do Angular Material é a sua incrível coleção de diretivas. Existem duas maneiras básicas que podemos seguir para usar o Angular Material:

Neste artigo trataremos de explorar alguns dos principais recursos do framework do Angular Material, explorando desde a sua instalação até a importação dos arquivos nas páginas HTML em conjunto com os recursos nativos do AngularJS. O leitor terá, ao final do mesmo, insumos suficientes para entender como ambos os frameworks se comunicam, bem como adaptá-los aos seus projetos facilmente. Além disso, trataremos de expor a integração entre a estrutura HTML com os códigos de estilo do CSS que a biblioteca do Angular Material disponibiliza por padrão para incutir o design característico do framework e seus componentes.

Instalação local

Para executar os comandos desse artigo, você precisará ter o Node.js instalado na máquina, assim poderemos fazer uso do seu gerenciador de pacotes, o npm. Use o seguinte comando npm para instalar as bibliotecas:

npm install angular-material

O npm irá baixar os arquivos do Angular Material no diretório node_modules > angular-material. Por isso, é importante selecionar um diretório para guardar seus arquivos no projeto final. Vejamos o exemplo demonstrado na Listagem 1, um Alô Mundo usando os módulos iniciais do Angular Material. Veja que ele traz apenas uma série de importações dos arquivos de CSS e JavaScript que o framework faz uso. Como de praxe, toda aplicação AngularJS deve conter no body da página o ng-app com o nome do módulo da aplicação. Esse será usado no JavaScript que inicializa o módulo do ngMaterial.

<div class="pre_xml"> <html lang="pt"> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="node_modules/angular-material/angular-material.css"> </head> <body ng-app="aloMundo" ng-cloak> <md-toolbar class="md-warn"> <div class="md-toolbar-tools"> <h2 class="md-flex">Alô Mundo Angular Material</h2> </div> </md-toolbar> <md-content flex layout-padding> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. </p> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. </p> <p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> </md-content> </body> <script src="node_modules/angular/angular.js"></script> <script src="node_modules/angular-animate/angular-animate.js"></script> <script src="node_modules/angular-aria/angular-aria.js"></script> <script src="node_modules/angular-messages/angular-messages.js"></script> <script src="node_modules/angular-material/angular-material.js"></script> <script type="text/javascript"> angular.module('aloMundo', ['ngMaterial']); </script> </html>
Listagem 1. Alô Mundo Angular Material

Obteremos o resultado produzido conforme mostra a Figura 1.

Figura 1. Resultado exibido do Alô Mundo

Versão baseada no CDN

Você também pode implementar o mesmo exemplo usando os arquivos angular-material.min.css e angular-material.min.js do Google CDN, como mostra a Listagem 2. O resultado, por sua vez, continuará o mesmo.

<html lang="pt"> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/ angular-material.min.css"> </head> <body ng-app="aloMundo" ng-cloak> <md-toolbar class="md-warn"> <div class="md-toolbar-tools"> <h2 class="md-flex">Alô Mundo Angular Material</h2> </div> </md-toolbar> <md-content flex layout-padding> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. </p> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. </p> <p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> </md-content> </body> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular .min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular -animate.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular -aria.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular -messages.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/ angular-material.min.js"></script> <script type="text/javascript"> angular.module('aloMundo', ['ngMaterial']); </script> </html>
Listagem 2. Instalando o Angular Material CDN

Diretivas

As diretivas são uma característica do núcleo do AngularJS. Como já falamos anteriormente, o Angular vem com várias diretivas que usamos todo o tempo, como ng-model ou ng-repeat. Elas são uma parte muito importante do framework, pois disponibilizam ações customizadas e reaproveitamento de código.

Essa biblioteca possui um conjunto de diretivas que inspiram o design Material. Diretivas do Angular Material são tags HTML que começam com md (abreviação de material design), e são bem fáceis de usar. Por exemplo, vamos dar uma olhada em um exemplo de botão simples. Um botão HTML padrão poderia ser algo como:

<button>Clique aqui!</button>

Um botão do Angular Material se parece mais ou menos com isso:

<md-button>Clique aqui!</md-button>

E isso é tudo o que precisamos para fazer um botão Material. Agora, existem várias outras opções que estão disponíveis para essa diretiva, como theming e elevação a partir da superfície, para aumentar a sua importância:

<md-button class="md-raised md-primary md-hue-1">Clique aqui!</md-button>

A classe md-raised se encarrega de incutir o efeito elevado ao botão, com uma sombra no plano de fundo, tal como vemos na Figura 2.

Figura 2. Exemplo do botão com classe md-raised

Serviços

Os serviços são de igual modo fundamentais para a funcionalidade do Angular, utilizados para dividir o código através da aplicação. Um serviço comum de núcleo como o $http é usado e reutilizado para chamadas de dados em aplicações do Angular. Os serviços Angular são:

O Angular Material vem embalado com alguns serviços que fornecem funcionalidades extras para nossas aplicações. Eles também contribuem para o desempenho de algumas das diretivas. Um grande exemplo de um desses serviços é o toast. Um toast é uma pequena notificação que desliza a partir da parte inferior da tela e desaparece após alguns segundos. Veja na Listagem 3 como implementar esse tipo de serviço no JavaScript. Para o recurso funcionar, precisamos incorporar o módulo do $mdToast ao controller do AngularJS, assim ele estará disponível para instanciar a função show() e exibir a mensagem. Para o exemplo funcionar, você precisa adicionar também um ng-controller à tag body da página, uma vez que ele será mapeado pela função controller() na declaração do módulo do Angular:

<body ng-app="aloMundo" ng-controller="ToastEx" ng-cloak>
angular.module('aloMundo', ['ngMaterial']).controller('ToastEx', function($scope, $mdToast) { $mdToast.show( $mdToast.simple('Alô Mundo Toast!') .position('left bottom') .hideDelay(3000) ); });
Listagem 3. Criando um toast simples

O resultado pode ser visualizado na Figura 3. Este exemplo mostra apenas um toast simples que aparece no canto inferior esquerdo da tela e é removido após três segundos.

Figura 3. Mensagem sendo exibida num Toast

Theming

O Material Design é uma linguagem visual onde os temas transmitem significados através de cores, tons e contraste. Esses temas são expressos ao longo dos componentes do aplicativo para proporcionar uma sensação mais unificada.

De acordo com as diretrizes do Material Design, você deve limitar a seleção de cores, escolhendo três tonalidades de cores da paleta primária e uma cor de destaque da paleta secundária. Ele segue essa simples diretriz usando o JavaScript para configurar o tema. Mas qual a diferença entre uma paleta e uma tonalidade? A tonalidade é uma única cor em uma paleta, já a paleta é um conjunto de tonalidades. Por exemplo uma paleta seria verde e uma tonalidade é um determinado tom de verde.

Configurando o tema

Tematizar o seu projeto é uma opção muito útil para impor um design profissional sem grandes esforços no Angular Material. No arquivo app.js (crie-o no diretório /js para organizar melhor a distribuição dos arquivos do projeto), é preciso definir suas paletas e tonalidades usando o serviço de provedor de theming, conforme podemos ver na Listagem 4. Nela, temos algumas funções importantes, a saber:

angular.module('myApp', ['ngMaterial']) .config(function($mdThemingProvider) { $mdThemingProvider.theme('default') .primaryPalette('blue', { 'default': '400', 'hue-1': '100', 'hue-2': '600', 'hue-3': 'A100' }) .accentPalette('orange') .warnPalette('yellow') .backgroundPalette('grey'); });
Listagem 4. Definindo paletas e tonalidade em um theming

Na Listagem 5 aplicamos o tema nos componentes definindo a classe do elemento para a paleta e tonalidade desejada. Veja que precisamos referenciar cada uma das paletas criadas exatamente pelo seu nome (warn, accent, etc.). Veja o resultado de tal ação na Figura 4.

<md-button class="md-primary">Clique aqui!</md-button> <md-button class="md-primary md-hue-1">Clique aqui!</md-button> <md-button class="md-primary md-hue-2">Clique aqui!</md-button> <md-button class="md-accent">ou talvez aqui</md-button> <md-button class="md-warn">Cuidado</md-button>
Listagem 5. Exemplos de botões Material com tema que criamos
Figura 4. Resultado dos botões com as classes da paleta

Layouts

O flexbox é a maior e mais recente adição de design ao Angular Material. Trata-se do sistema nativo do CSS3 que lida com a organização automática dos elementos de uma página quando exibida em dispositivos de diferentes tamanhos, dispensando a propriedade float, antes largamente usada como solução para impor responsividade. Se você estiver familiarizado com o sistema de grids do Bootstrap, terá mais facilidade ao utilizá-lo, pois na verdade o Bootstrap já está migrando para o flexbox em seu próximo lançamento. Ele é baseado no sistema de layout de linhas e colunas (geralmente em máximo de 12), porém com muito mais recursos.

A diretiva de layout é um elemento de container usado para especificar a direção do layout para os elementos filhos. A seguir estão os valores atribuíveis à mesma:

De acordo com as diretivas do design responsivo, com a forma como o layout será alterado e dependendo do tamanho da tela do dispositivo, as seguintes definições de layout definidas na Tabela 1 podem ser usadas para definir a direção nos dispositivos com larguras de diferentes dimensões.

Layout Dimensões
layout-xs width < 600px
layout-gt-xs width >= 600px
layout-sm 600px <= width < 960px
layout-gt-sm width >= 960px
layout-md 960px <= width < 1280px
layout-gt-md width >= 1280px
layout-lg 1280px <= width < 1920px
layout-gt-lg width >= 1920px
layout-xl width >= 1920px
Tabela 1. Lista de dimensões para diferentes layouts

A Listagem 6, por sua vez, mostra como usar essa diretiva para exibir o layout padrão.

<html lang="pt"> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/angular_material/ 1.0.0/angular-material.min.css"> <style> .box { color:white; padding:10px; text-align:center; border-style: inset; } .red { background:red; } .orange { background:orange; } </style> </head> <body ng-app="firstApplication"> <div id="layoutContainer" ng-controller="layoutController as ctrl" ng-cloak> <div layout="row" layout-xs="column"> <div flex class="red box">Linha 1: Item 1</div> <div flex="20" class="orange box">Linha 1: Item 2</div> </div> <div layout="column" layout-xs="column"> <div flex="33" class="red box">Coluna 1: item 1</div> <div flex="66" class="orange box">Coluna 1: item 2</div> </div> </div> </body> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/ angular.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/ angular-animate.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/ angular-aria.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/ angular-messages.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/ angular-material.min.js"></script> <script type="text/javascript"> angular.module('firstApplication', ['ngMaterial']) .controller('layoutController', layoutController); function layoutController ($scope) { } </script> </html>
Listagem 6. Exemplo de uso da diretiva com o layout padrão

Veja na Figura 5 o resultado. Observe que na listagem estamos fazendo uso dos atributos layout definidos nas tags para determinar se as mesmas adotarão o estilo row ou column, conforme mencionamos. A função layoutController() está vazia já que não configuraremos nada para o controller ainda e o Angular não permite inicializar um controle sem uma função associada. Veja como o Angular Material trabalha o resultado final em detrimento das concepções de agrupamento que configurarmos.

Figura 5. Diretiva de layout padrão

A diretiva flex em um elemento de container é usada para personalizar o tamanho e a posição dos elementos, ela define a maneira como o elemento deve ajustar o seu tamanho em relação ao seu recipiente pai e aos outros elementos dentro do container. A seguir estão os valores atribuíveis:

A Listagem 7 mostra o uso dessa diretiva demonstrando como atribuir o flex às nossas páginas.

<html lang="pt"> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/ angular-material.min.css"> <style> .box { color:white; padding:10px; text-align:center; border-style: inset; } .red { background:red; } .orange { background:orange; } </style> </head> <body ng-app="firstApplication"> <div id="layoutContainer" ng-controller="layoutController as ctrl" layout="row" ng-cloak layout-wrap> <div flex="30" class="red box"> [flex="30"] </div> <div flex="45" class="orange box"> [flex="45"] </div> <div flex="25" class="red box"> [flex="25"] </div> <div flex="33" class="red box"> [flex="33"] </div> <div flex="66" class="orange box"> [flex="66"] </div> <div flex="50" class="orange box"> [flex="50"] </div> <div flex class="red box"> [flex] </div> </div> </body> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/ angular.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/ angular-animate.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/ angular-aria.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/ angular-messages.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/ angular-material.min.js"></script> <script type="text/javascript"> angular .module('firstApplication', ['ngMaterial']) .controller('layoutController', layoutController); function layoutController ($scope) { } </script> </html>
Listagem 7. Atribuindo atributo flex à nossa página

Veja na Figura 6 o resultado. Observe que as definições se mantiveram em relação à configuração do controller e das diretivas padrão do Angular, porém apenas acrescentamos o atributo flex aos nossos elementos de

definindo seus valores divididos tal como demonstramos. Bem simples, não acha?
Figura 6. Usando a diretiva de layout flex

A seguir vamos conhecer algumas das melhores diretivas do Angular Material, onde teremos alguns exemplos práticos de como aplicá-las em nosso código.

Autocomplete

O Autocomplete fornece uma experiência agradável ao usuário, o auxiliando na escolha de uma opção. É o que faz o motor de busca do Google, o melhor do mercado. A diretiva Autocomplete adiciona essa funcionalidade ao seu aplicativo através da exibição do preenchimento das palavras via sugestões à medida em que o usuário as escreve. Mas a melhor parte dessa diretiva é a personalização. Ao preencher o seu autocomplete com o atributo md-item-template você pode dar mais sentido às sugestões. Por exemplo, se um usuário estava à procura de nomes em uma empresa, o autocomplete poderia mostrar os nomes correspondentes com a imagem da sua empresa (assim como vemos ao buscar alguém no Facebook ou no LinkedIn), dando ao usuário uma experiência mais robusta. A tag > pode ser usada para fornecer resultados de pesquisa a partir de dados locais, remotos ou em cache através do código sources.md-autocomplete. Após a primeira chamada, ele usa os resultados em cache para eliminar solicitações desnecessárias do servidor bem como lógicas de pesquisa que podem ser desativadas.

Vejamos uma série de atributos e suas descrições que usaremos no nosso exemplo de autocomplete:

O exemplo da Listagem 8 (retirado da seção “Demos” da documentação oficial do Angular Material – vide seção Links) mostra como usar a diretiva md-autocomplete. Nele, veremos como criar um exemplo de caixa de pesquisa que buscará por um dos estados brasileiros (configurados em uma lista estática dentro do código JavaScript, criado mais adiante), com opções para habilitar um efeito de carregamento com uma barra de progresso, usando o recurso nativo de cache do próprio Angular, além habilitar ou desabilitar o campo de busca em si. Vejamos algumas de suas principais partes:

<html lang="pt"> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/ angular_material/1.0.0/angular-material.min.css"> </head> <body ng-app="estadosApp" ng-cloak> <div ng-controller="autoCompleteController as estApp" layout="column" ng-cloak> <md-content class="md-padding"> <form ng-submit="$event.preventDefault()"> <md-autocomplete ng-disabled="estApp.isDisabled" md-no-cache="estApp.noCache" md-selected-item="estApp.selectedItem" md-search-text-change="estApp.searchTextChange(estApp.searchText)" md-search-text="estApp.searchText" md-selected-item-change="estApp.selectedItemChange(item)" md-items="item in estApp.queryBusca(estApp.searchText)" md-item-text="item.display" md-min-length="0" placeholder="Digite o nome do estado..."> <md-item-template> <span md-highlight-text="estApp.searchText" md-highlight- flags="^i">{{item.display}}</span> </md-item-template> <md-not-found> Nenhum resultado encontrado para "{{estApp.searchText}}". <a ng-click="estApp.novoEstado(estApp.searchText)">Criar um novo!</a> </md-not-found> </md-autocomplete> <br/> <md-checkbox ng-model="estApp.simularQuery">Exibir barra de progresso? </md-checkbox> <md-checkbox ng-model="estApp.noCache">Desabilitar cache?</md-checkbox> <md-checkbox ng-model="estApp.isDisabled">Desabilitar campo de busca? </md-checkbox> </form> </md-content> </div> </body> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/ angular.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/ angular-animate.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/ angular-aria.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/ angular-messages.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/ angular-material.min.js"></script> <script type="text/javascript"> // Código do autocomplete </script> </html>
Listagem 8. Implementado a diretiva autocomplete – Parte 1

A segunda parte do código encontra-se na Listagem 9. O mesmo trecho deve ser inserido entre as tags das linhas 44 e 46 da listagem anterior, pois trata-se do código que dinamizará a busca via Angular Material.

Na linha 4 temos a função que mapeia os principais fluxos de execução. Nas primeiras linhas declaramos as variáveis a serem usadas ao longo do código (note que a função novoEstado não tem implementação, é apenas uma referência para que possamos implementar essa adição dinâmica em um exemplo mais formal). A função queryBusca (linha 17) se encarrega de receber o objeto de query e fazer uma filtragem inicial para saber se já temos no nosso próprio cache (variável self). Veja que o código faz uso também de promises (linha 24) para lidar com o processamento assíncrono da listagem de itens. As promises disponibilizam meios de processamento assíncrono dentro do JavaScript que, por padrão, não suporta tal recurso. O Angular faz uso delas para tornar o componente de autocomplete assíncrono, uma vez que não é possível saber quanto tempo pode demorar uma requisição a uma lista de valores remota a ser exibida pelo mesmo. A função loadEstados (linha 36), por sua vez, se encarrega de criar uma string com todos os valores separados por vírgula para que possamos quebrá-la (split) e mapeá-la (map) com chaves-valores. A função createFilterFor (linha 46) mascara todos os valores para minúsculo para que possamos assim não ter diferenças entre o que está na lista de estados e o que está de fato na tela.

angular.module('estadosApp', ['ngMaterial']) .controller('autoCompleteController', autoCompleteController); function autoCompleteController ($timeout, $q, $log) { var self = this; self.simularQuery = false; self.isDisabled = false; // lista de estados a serem exibidos self.estados = loadEstados(); self.queryBusca = queryBusca; self.selectedItemChange = selectedItemChange; self.searchTextChange = searchTextChange; self.novoEstado = novoEstado; function novoEstado(state) { alert("A implementar..."); } function queryBusca (query) { var results = query ? self.estados.filter( createFilterFor(query) ) : self.estados, deferred; if (self.simularQuery) { deferred = $q.defer(); $timeout(function () { deferred.resolve( results ); }, Math.random() * 1000, false); return deferred.promise; } else { return results; } } function searchTextChange(text) { $log.info('Texto modificado para: ' + text); } function selectedItemChange(item) { $log.info('Item modificado para: ' + JSON.stringify(item)); } // constrói uma lista de estados como map de pares de chave-valor function loadEstados() { var allEstados = 'Acre (AC), Alagoas (AL), Amapá (AP), Amazonas (AM), Bahia (BA), Ceará (CE), Distrito Federal (DF), Espírito Santo (ES), Goiás (GO), Maranhão (MA), Mato Grosso (MT), Mato Grosso do Sul (MS), Minas Gerais (MG), Pará (PA) , Paraíba (PB), Paraná (PR), Pernambuco (PE), Piauí (PI), Rio de Janeiro (RJ), Rio Grande do Norte (RN), Rio Grande do Sul (RS), Rondônia (RO), Roraima (RR), Santa Catarina (SC), São Paulo (SP), Sergipe (SE), Tocantins (TO)'; return allEstados.split(/, +/g).map( function (state) { return { value: state.toLowerCase(), display: state }; }); } // filtra pela query de busca function createFilterFor(query) { var lowercaseQuery = angular.lowercase(query); return function filterFn(state) { return (state.value.indexOf(lowercaseQuery) === 0); }; } }
Listagem 9. Implementado a diretiva autocomplete – Parte 2

Veja na Figura 7 como ficará nosso código final na tela.

Figura 7. Página com a diretiva autocomplete

O fato de o md-autocomplete exibir resultados em cache ao executar uma consulta permite que, após a primeira chamada, ele use os resultados em cache para eliminar solicitações ou lógicas de servidor desnecessárias, trazendo os resultados de imediato, como podemos ver na Figura 8.

Figura 8. Digitando um texto no box que contém a diretiva autocomplete e valor em cache

Ele também pode ser desativado como mostra a Figura 9, onde o box fica bloqueado para uma nova digitação.

Figura 9. Desabilitando o box de busca

Botom Sheet

O botom sheet é um pequeno menu que desliza para cima a partir da parte inferior da tela, cobrindo o conteúdo e mantendo o foco. Originalmente destinado a ser utilizado exclusivamente para dispositivos móveis, o botom sheet vem ganhando popularidade em telas maiores. Para usá-lo, temos que criar um template com a diretiva md-bottom-sheet, que contém tanto uma classe md-grid como uma md-list, as quais são usadas para criar elementos de grade e lista no layout do Material, respectivamente. Em seguida, chamá-lo com o serviço do botom sheet: o $mdBottomSheet.show(). A Listagem 10 traz um exemplo prático de uso para essa diretiva. Veja que a nível de HTML nenhuma mudança significativa foi efetuada, apenas no JavaScript. A função abrirBottomSheet() se encarregou de receber o módulo do $mdBottomSheet e chamar a sua função show(), passando o template com uma string em HTML a ser exibida (veja que é nela onde devem estar inseridas as tags ).

<html lang="pt"> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/angular_material/ 1.0.0/angular-material.min.css"> </head> <body ng-app="bottomSheetApp"> <div ng-controller="bottomSheetController as ctrl" layout="column"> <md-content class="md-padding"> <form ng-submit="$event.preventDefault()"> <md-button class="md-raised md-primary" ng-click="abrirBottomSheet()"> Abrir Bottom Sheet! </md-button> </form> </md-content> </div> </body> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/ angular.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/ angular-animate.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/ angular-aria.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/ angular-messages.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/ angular-material.min.js"></script> <script type="text/javascript"> angular .module('bottomSheetApp', ['ngMaterial']) .controller('bottomSheetController', bottomSheetController); function bottomSheetController ($scope, $mdBottomSheet) { $scope.abrirBottomSheet = function() { $mdBottomSheet.show({ template: '<md-bottom-sheet>Aprender <b> Angular Material</b>!!</md-bottom-sheet>' }); }; } </script> </html>
Listagem 10. Implementando a diretiva botom sheet

A Figura 10 mostra como ficará o nosso botão criado através dessa diretiva, já na Figura 11 podemos ver como ficará a tela após o click no botão. Veja que a nova tela cobre o antigo conteúdo, mas ainda o exibe em plano de fundo.

Figura 10. Exemplo do botão
Figura 11. Exibição da tela usando a diretiva bottom sheet

Input

O md-input-container é um componente recipiente para conter qualquer elemento ou como um filho. O md-input-container também suporta a manipulação de erros usando o padrão do AngularJS, via diretivas ng-messages, e anima as mensagens usando os eventos de ngEnter/ngLeave ou os eventos de ngShow/ngHide.

Insira em alguns dos seus campos de texto a diretiva de input. Envolva sua tag de input com a diretiva md-input-container e você verá isso acontecer. A diretiva de input lida com um tipo de elemento no HTML que sempre foi tido como entediante, mas no fim proporciona uma agradável surpresa pela capacidade que o Angular Material fornece.

Como em várias diretivas apresentadas, o input também possui alguns atributos, a seguir iremos conhecer alguns e, logo mais na Listagem 11, iremos criar um formulário com essa diretiva.

<html lang="pt"> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/angular_material /1.0.0/angular-material.min.css"> </head> <body ng-app="inputApp"> <div id="inputContainer" ng-controller="inputController as ctrl" ng-cloak> <md-content layout-padding> <form name="formProjeto"> <md-input-container class="md-block"> <label>Usuário</label> <input required name="usuario" ng-model="project.usuario"> <div ng-messages="formProjeto.usuario.$error"> <div ng-message="required">Obrigatório.</div> </div> </md-input-container> <md-input-container class="md-block"> <label>Email</label> <input required type="email" name="emailUsuario" ng-model=" .emailUsuario" minlength="10" maxlength="100" ng-pattern="/^.+@.+\..+$/" /> <div ng-messages="formProjeto.emailUsuario.$error" role="alert"> <div ng-message-exp="['required', 'minlength', 'maxlength', 'pattern']"> Seu email deve conter entre 10 e 100 caracteres de tamanho e deve ser um endereço válido. </div> </div> </md-input-container> <md-input-container class="md-block"> <label>Comentários</label> <input md-maxlength="300" required name="comentarios" ng-model="project.comentarios"> <div ng-messages="formProjeto.comentarios.$error"> <div ng-message="required">Obrigatório.</div> <div ng-message="md-maxlength">Os comentários devem ter menos de 300 caracteres.</div> </div> </md-input-container> </form> </md-content> </div> </body> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular .min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular -animate.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular -aria.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular -messages.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/ angular-material.min.js"></script> <script type="text/javascript"> angular .module('inputApp', ['ngMaterial']) .controller('inputController', inputController); function inputController($scope) { $scope.project = { comentarios: 'Comentários', }; } </script> </html>
Listagem 11. Implementação de formulário com a diretiva input

Note que o próprio AngularJS já se encarrega de validar os atributos base da HTML5, como o required (para campo obrigatório) e o maxlength, por exemplo. Para cada campo devemos definir uma div com a diretiva ng-messages, que irá receber também um role (um papel específico dentro do Angular) referente ao tipo de mensagem que ali será exibida (o valor alert confere a cor amarela bem como configurações específicas do Angular Material). Na tag interna de diretiva ng-message-exp definimos quais validações deverão ser consideradas por padrão naquele campo em si e dentro dela a mensagem que deverá ser exibida caso alguma retorne falso (linhas 21 a 24). Caso seja necessário exibir uma mensagem para cada regra de validação, podemos definir várias divs com uma ng-message para cada uma (linhas 31 e 32).

No JavaScript da linha 51, contudo, apenas definimos o texto que já deve vir preenchido no campo por padrão.

As Figuras 12 a 14 mostram como ficará o formulário que acabamos de implementar.

Figura 12. Formulário com diretiva input
Figura 13. Formulário com mensagem de campo obrigatório exibida
Figura 14. Aqui podemos ver o funcionamento dos métodos ng-message-exp e md-maxlenght

Grid Lists

São uma alternativa às listas padrão. Uma grid list é melhor para a apresentação de imagens e é otimizada para uma melhor compreensão visual dos elementos. Ela funciona através da fixação de diferentes tiles (blocos de divs HTML) feitos sob medida em uma grid, dando uma sensação eclética à implementação. Vejamos na Listagem 12 um exemplo simples fornecido pela documentação do Angular Material de tilesets (conjunto de blocos tiles) que geram vários quadros dinâmicos na página, com cores aleatórias. As propriedades:

No JavaScript apenas criamos um vetor com várias cores em formato hexadecimal e as randomizamos via função randomColor (linha 42), da mesma forma que definimos rowspan’s e colspan’s distintos e aleatórios via função randomSpan (linha 46). Veja também que a função colorTiles da linha 29 faz uso dessas funções randômicas para gerar e atribuir as cores/dimensões referentes a cada tile. Se o leitor desejar fazer testes com mais tiles basta incrementar o valor que está sendo iterado no loop for.

O tamanho do tile e layout, então, corresponde ao tamanho da tela conforme ilustrado na Figura 15.

<html lang="pt"> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/ angular-material.min.css"> </head> <body ng-app="gridApp"> <div ng-controller="GridListCtrl as appCtrl" ng-cloak="" class="gridListdemoResponsiveUsage"> <md-content layout-padding=""> <md-grid-list md-cols-gt-md="12" md-cols-sm="3" md-cols-md="8" md-row-height-gt-md="1:1" md-row-height="4:3" md-gutter-gt-md="16px" md-gutter-gt-sm="8px" md-gutter="4px"> <md-grid-tile ng-repeat="tile in appCtrl.colorTiles" ng-style="{ 'background': tile.color }" md-colspan-gt-sm="{{tile.colspan}}" md-rowspan-gt-sm="{{tile.rowspan}}"> </md-grid-tile> </md-grid-list> </md-content> </div> </body> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/ angular.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/ angular-animate.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/ angular-aria.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/ angular-messages.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/ angular-material.min.js"></script> <script type="text/javascript">angular .module('gridApp', ['ngMaterial']) .controller('GridListCtrl', function ($scope) { var COLORS = ['#ffebee', '#ffcdd2', '#ef9a9a', '#e57373', '#ef5350', '#f44336', '#e53935', '#d32f2f', '#c62828', '#b71c1c', '#ff8a80', '#ff5252', '#ff1744', '#d50000', '#f8bbd0', '#f48fb1', '#f06292', '#ec407a', '#e91e63', '#d81b60', '#c2185b', '#ad1457', '#880e4f', '#ff80ab', '#ff4081', '#f50057', '#c51162', '#e1bee7', '#ce93d8', '#ba68c8', '#ab47bc', '#9c27b0', '#8e24aa', '#7b1fa2', '#4a148c', '#ea80fc', '#e040fb', '#d500f9', '#aa00ff', '#ede7f6', '#d1c4e9', '#b39ddb', '#9575cd', '#7e57c2', '#673ab7', '#5e35b1', '#4527a0', '#311b92', '#b388ff', '#7c4dff', '#651fff', '#6200ea', '#c5cae9', '#9fa8da', '#7986cb', '#5c6bc0', '#3f51b5', '#3949ab', '#303f9f', '#283593', '#1a237e', '#8c9eff', '#536dfe', '#3d5afe', '#304ffe', '#e3f2fd', '#bbdefb', '#90caf9', '#64b5f6', '#42a5f5', '#2196f3', '#1e88e5', '#1976d2', '#1565c0', '#0d47a1', '#82b1ff', '#448aff', '#2979ff', '#2962ff', '#b3e5fc', '#81d4fa', '#4fc3f7', '#29b6f6', '#03a9f4', '#039be5', '#0288d1', '#0277bd', '#01579b', '#80d8ff', '#40c4ff', '#00b0ff', '#0091ea', '#e0f7fa', '#b2ebf2', '#80deea', '#4dd0e1', '#26c6da', '#00bcd4', '#00acc1', '#0097a7', '#00838f', '#006064', '#84ffff', '#18ffff', '#00e5ff', '#00b8d4', '#e0f2f1', '#b2dfdb', '#80cbc4', '#4db6ac', '#26a69a', '#009688', '#00897b', '#00796b', '#00695c', '#a7ffeb', '#64ffda', '#1de9b6', '#00bfa5', '#e8f5e9', '#c8e6c9', '#a5d6a7', '#81c784', '#66bb6a', '#4caf50', '#43a047', '#388e3c', '#2e7d32', '#1b5e20', '#b9f6ca', '#69f0ae', '#00e676', '#00c853', '#f1f8e9', '#dcedc8', '#c5e1a5', '#aed581', '#9ccc65', '#8bc34a', '#7cb342', '#689f38', '#558b2f', '#33691e', '#ccff90', '#b2ff59', '#76ff03', '#64dd17', '#f9fbe7', '#f0f4c3', '#e6ee9c', '#dce775', '#d4e157', '#cddc39', '#c0ca33', '#afb42b', '#9e9d24', '#827717', '#f4ff81', '#eeff41', '#c6ff00', '#aeea00', '#fffde7', '#fff9c4', '#fff59d', '#fff176', '#ffee58', '#ffeb3b', '#fdd835', '#fbc02d', '#f9a825', '#f57f17', '#ffff8d', '#ffff00', '#ffea00', '#ffd600', '#fff8e1', '#ffecb3', '#ffe082', '#ffd54f', '#ffca28', '#ffc107', '#ffb300', '#ffa000', '#ff8f00', '#ff6f00', '#ffe57f', '#ffd740', '#ffc400', '#ffab00', '#fff3e0', '#ffe0b2', '#ffcc80', '#ffb74d', '#ffa726', '#ff9800', '#fb8c00', '#f57c00', '#ef6c00', '#e65100', '#ffd180', '#ffab40', '#ff9100', '#ff6d00', '#fbe9e7', '#ffccbc', '#ffab91', '#ff8a65', '#ff7043', '#ff5722', '#f4511e', '#e64a19', '#d84315', '#bf360c', '#ff9e80', '#ff6e40', '#ff3d00', '#dd2c00', '#d7ccc8', '#bcaaa4', '#795548', '#d7ccc8', '#bcaaa4', '#8d6e63', '#eceff1', '#cfd8dc', '#b0bec5', '#90a4ae', '#78909c', '#607d8b', '#546e7a', '#cfd8dc', '#b0bec5', '#78909c']; this.colorTiles = (function () { var tiles = []; for (var i = 0; i < 46; i++) { tiles.push({ color: randomColor(), colspan: randomSpan(), rowspan: randomSpan() }); } return tiles; })(); function randomColor() { return COLORS[Math.floor(Math.random() * COLORS.length)]; } function randomSpan() { var r = Math.random(); if (r < 0.8) { return 1; } else if (r < 0.9) { return 2; } else { return 3; } } }); </script> </html>
Listagem 12. Exemplo com tilesets randômicos no Angular Material
Figura 15. Exemplo final da grid list

O Google está convertendo suas aplicações mais populares para o Material Design. Agora eles estão trabalhando no desenvolvimento do Angular Material, uma implementação do Material Design escrito em AngularJS acrescentando sempre novos recursos e funcionalidades. Dessa forma você terá sempre em mãos o poder desses dois poderosos frameworks: um totalmente focado nos componentes/estilos da página e outro na dinamização desses de modo a não necessitarmos de todo um aparato de servidor como era feito antigamente.

Podemos notar nesse artigo que o Material Design e o Angular Material são uma fantástica maneira de aplicar as especificações de design para suas aplicações single-page. Se você quiser criar seu próprio aplicativo com o Angular Material, não perca tempo começando do zero. Em vez disso, comece com um aplicativo totalmente funcional conforme as demonstrações das diretivas configuradas. E claro, você pode aprender tudo sobre Angular Material visitando a documentação oficial na seção Links.

Artigos relacionados