Desenvolvimento - HTML

Carregando DropDownLists em cadeia com AngularJS

Aprenda a carregar elementos SELECT aninhados entre si com a carga em cascata.

por Fabrício Rocha e Edison Oliveira



O AngularJS é um framework JavaScript construído e mantido pelo grupo de engenheiros da Google, com o objetivo de facilitar o desenvolvimento de Single-Page Applications(SPA), que é um tipo de aplicação web que se encaixa em uma única página, com o objetivo de oferecer uma experiência de uso mais rica ao usuário, semelhante à uma aplicação desktop. Em uma SPA, todo o código necessário, tais como HTML,JavaScripte CSS, é baixado uma única vez, quando a aplicação é inicialmente carregada. A página não precisa ser recarregada em nenhum outro momento.

O objetivo do AngularJS é simplificar o desenvolvimento e os testes das aplicações SPA, fornecendo um framework para a arquitetura client-side model-view-controller (MVC), em conjunto com componentes utilizados em aplicações ricas para internet (RIA).

Neste artigo criaremos uma aplicação simples onde teremos dois elementos HTML Select,em que a opção selecionada no primeiro elemento irá popular as opções do segundo elemento em cascata.

Arquitetura

O AngularJS, de maneira sutil, conduz à adesão do design pattern MVC no desenvolvimento de aplicações. O MVC é um padrão que reforça a separação de conceitos em três partes: Model, View e Controller. No contexto da uma aplicação AngularJS, essas partes são associadas às seguintes camadas da aplicação:

  • Model: são os objetos JSON, que tanto podem ser retornados por RestServices, quanto declarados e preenchidos na própria aplicação.
  • View: são as páginas HTML da aplicação
  • Controller: são os arquivos JavaScript contendo o código que faz a interação entre o HTML e os dados a serem exibidos.

A Figura 1 ilustra o modelo de interação entre as camadas, sempre disparados por um evento do usuário.

Arquitetura da aplicação AngularJS

Figura 1. Arquitetura da aplicação AngularJS

Diretivas

As diretivas dizem ao compilador do AngularJS para incluir um comportamento específico ao elemento DOM. O AngularJS traz diversas diretivas nativas, mas diretivas customizadas podem ser criadas pelo desenvolvedor. As diretivas nativas geralmente começam com “ng-“ (por exemplo ng-controller) e são incluídas no código HTML como se fossem atributos dos respectivos elementos DOM.

Criando a aplicação

Para o nosso exemplo iremos criar apenas dois arquivos: uma página HTML que chamaremos de index.html, e um arquivo JavaScript que chamaremos de app.js.

No arquivo app.js iniciamos fazendo a declaração da aplicação, propriamente dita, como mostra a Listagem 1.

Listagem 1. Definição do app.

  (function () {
      var app = angular.module('artigo', []);
  (...)
  })()

O método module cria a aplicação e recebe dois parâmetros: o nome do app e os módulos auxiliares (por exemplo, componentes de terceiros) que serão utilizados na aplicação. Um detalhe importante é que mesmo que nenhum módulo auxiliar seja utilizado, é necessário passar um array vazio como parâmetro.

O segundo passo é definir a Controller, como mostra a Listagem 2.

Listagem 2. Definição da controller

  app.controller('ArtigoController', function ($scope) {
  (...)
  )}HHT;

O objeto $scope encapsula o Model da aplicação. É esse objeto que mantém o estado da aplicação e faz a interface junto à View. Outro detalhe importante do $scope é que ele é hierárquico.

Na página HTML é necessário, primeiramente, adicionar a referência entre a página e a aplicação. Isso é feito através da diretiva ng-app, como mostra o comando a seguir:

<html xmlns="http://www.w3.org/1999/xhtml" ng-app="artigo">

Em seguida, incluiremos uma referência ao Framework do AngularJS, como mostra a Listagem 3.

Listagem 3. Referência ao Framework AngularJS

  <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.min.js"></script>

Finalmente, adicionamos a referência do app.js usando o comando a seguir:

<script type="text/javascript" src="app.js"></script>

Agora já podemos fazer uso de nossa controller usando o comando da Listagem 4.

Listagem 4. Associação da controller com o HTML

  <div ng-controller="ArtigoController">
  (...)
  </div>

O escopo da controller Artigo abrange todo o conteúdo da tag DIV em que ele está inserido. Na Listagem 5 criaremos dentro da controller, no arquivo app.js, dois objetos JSON que definirão a estrutura de dados contendo seis registros de funcionários divididos em dois setores de uma empresa fictícia: o setor comercial e o setor de TI. Normalmente, esses dados seriam obtidos de um banco de dados, através de um serviço RESTful, mas para simplificar o exemplo definiremos os objetos hard-coded.

Listagem 5. Definição dos objetos JSON

  var setor1 = {
              codigo: 1,
              nome: 'TI',
              funcionarios:
                  [
                      { codigo: 1, nome: 'Valério Gualdim Ginjeira' },
                      { codigo: 2, nome: 'Márcio Hermesinda Santiago' },
                      { codigo: 3, nome: 'João Mem Valgueiro' }
                  ]
          };
   
          var setor2 = {
              codigo: 2,
              nome: 'Comercial',
              funcionarios:
                  [
                      { codigo: 4, nome: 'Débora Eduardo Salgado' },
                      { codigo: 5, nome: 'Feliciano Parcidio Moura' },
                      { codigo: 6, nome: 'Ana Fulvio Fortunato' }
                  ]
          };

Da forma acima, os objetos não poderão ser acessados pela View porque não foram atribuídas ao escopo. Atribuiremos os objetos ao escopo criando um array contendo os dois objetos, como mostra o código a seguir:

$scope.setores = [setor1, setor2];

Nossa View terá dois DropDownLists aninhados: um para que o usuário selecione o setor, e uma vez selecionado o setor, o segundo DropDownList deverá carregar os funcionários desse setor. Acompanhe a Figura 2.

View contendo dois DropDownLists aninhados

Figura 2. View contendo dois DropDownLists aninhados

É necessário definir os objetos que armazenarão tanto o Setor quanto o Profissional que o usuário irá selecionar. Isso é feito na Controller com os comandos a seguir:

   $scope.setorSelecionado = {};
          $scope.funcionarioSelecionado = {};

A aplicação iniciará sem que nenhum registro esteja selecionado. Então por que definir os objetos vazios? Porque se os objetos não forem definidos, no momento em que a View for carregada, e o elemento HTML fizer referência aos objetos, o compilador interpretará como erro, já que os objetos serão interpretados como Undefined.

Dentro da View, é necessário incluir os elementos HTML da Listagem 6.

Listagem 6. Definição dos elementos Select

  <select>
  <option value="">Selecione</option>
  </select>

Mas isso irá apenas criar elementos vazios. Para que os elementos sejam atachados aos dados do escopo, contidos na propriedade setores, utilizamos a diretiva ng-options, como mostra a Listagem 7.

Listagem 7. Elemento select recebe a diretiva ng-options, que irá popular o elemento

  <select ng-options="s.nome for s in setores track by s.codigo">
  <option value="">Selecione</option>
  </select>

A diretiva acima equivale a fazer um laço de repetição na variável setores, ordenada pelo código, e para cada item do array setores, uma linha é adicionada no elemento Select, contendo o conteúdo da propriedade nome definida no jSon dentro da controller. Falta ainda atachar a propriedade setorSelecionado ao registro selecionado no elemento Select. Isso é feito através da diretiva ng-model, como mostra a Listagem 8.

Listagem 8. Elemento Select recebe a diretiva ng-Model, que armazenará a opção selecionada

  <select ng-options="s.nome for s in setores track by s.codigo" ng-model="setorSelecionado">
  <option value="">Selecione</option>
  </select>

O aninhamento entre os DropDownLists, também conhecido como Cascade, se dá na diretiva ng-options do segundo elemento Select. As opções serão lidas do objeto setorSelecionado, que será alimentado no primeiro Select (Listagem 8), como mostra a Listagem 9.

Listagem 9. Elemento Select aninhado, que será carregado em função da seleção do primeiro elemento

  <select ng-options="p.nome for p in setorSelecionado.funcionarios track by p.codigo" ng-model="funcionarioSelecionado">
  <option value="">Selecione</option>
  </select>

Cada vez que o usuário selecionar um registro no primeiro elemento Select, as opções do segundo serão alteradas. Isso sem que ocorra o recarregamento da página (refresh). Isso é o que traz a experiência rica de uso ao usuário.

Fabrício Rocha e Edison Oliveira

Fabrício Rocha e Edison Oliveira - Fabrício é Arquiteto de Sistemas, especializado em aplicações críticas. Atuou como arquiteto no 4º maior sistema de e-Commerce do mundo. Atualmente atua como arquiteto e desenvolvedor sênior na startup BeXs, do ramo de sistemas web de terceira geração. Edison é MCSA – MCSD – MCP – MCDBA – PMP – ITIL – COBIT, Bacharel em Sistemas de Informação (UCDB). MBA em Governança de TI (Mauá). Atualmente atua como desenvolvedor sênior na startup BeXs, do ramo de sistemas web de terceira geração.