Angular 2 - Vinculando uma lista em um Select - Seleção em cascata - II


 Neste artigo vou mostrar como vincular uma lista de dados em um Select Dropdown em uma aplicação Angular 2 usando o Visual Studio Code.

Continuando a primeira parte do artigo veremos agora como implementar seleção em cascata onde teremos agora dois componentes Select onde em um serão exibidos os países e conforme o país selecionado no outro serão exibidas as cidades do respectivo país.

Nosso ponto de partida será o projeto criado no artigo anterior.

Definindo um Serviço Angular e a classe para cidades

Para poder exibir uma segunda lista com as cidades de um pais selecionado vamos criar uma classe onde vamos definir a lista de cidades para cada país.

E vamos fazer isso usando um serviço e não definir o código no componente como haviamos feito no artigo anterior.

Então vamos criar um serviço Angular e depois usar este serviço no nosso componente. Esta abordagem é mais recomendada e aderente às boas práticas, pois se no futuro mudarmos a forma de como obter os dados não teremos que alterar o componente.

Um serviço Angular pode obter dados de um arquivo JSON, de uma API da Web, ou de Serviço da Web ou de qualquer outro armazenamento local.

Quem vai consumir o serviço não está se importando como o serviço está obtendo os dados. Portanto, para o nosso serviço, vamos criar uma classe chamada "DataserviceService" com dois métodos que retornam um array de países e estados.

Criando a classe Cidade

No VS Code crie o arquivo cidade.ts na pasta app com o código abaixo:

Definimos acima a classe Cidade com 3 propriedades : id , paisid e nome.

Criando o serviço Angular: DataService

Agora vamos criar o serviço angular e para isso vamos usar o Angular CLI.

Na janela do prompt de comandos posicione-se na pasta do projeto e digite o comando : ng g service dataservice

Foram criados dois arquivos seguindo o style guide do Angular:

  1. dataservice.service.ts - o arquivo onde foi definido a classe DataserviceService;
  2. dataservice.service.spec.ts - arquivo usado para testes unitários;

Observe que temos uma alerta informando que o serviço foi gerado mas precisa ser fornecido para ser usado.

Outro detalhe importante a ressaltar é que quanto criamos o serviço usando o Angular CLI ele já incluir o import do serviço na classe app.component.ts.

Abrindo o projeto no VS Code veremos os arquivos criados e o código do arquivo dataservice.service.ts conforme abaixo:

As declarações no Angular são definidas assim : @Nome ,  e isso é um decorator.

Os decorators ou decoradores são uma extensão proposta para o JavaScript e permitem modificar e/ou marcar métodos, classes, propriedades e parâmetros.

Existem muitos decoradores, e, no código acima estamos usando dois decoradores muito importantes para a injeção de dependência (DI) :  @Inject e @ Injectable

  1. @inject - É um mecanismo manual que faz com que o Angular saiba que um parâmetro deve ser injetado.
  2. @Injectable() - Faz com que o Angular saiba que uma classe pode ser usada com o injetor de dependência. Ele não é estritamente necessário se a classe tiver outros decoradores nele ou não tiver dependências. O que é importante é que qualquer classe que seja injetada com Angular seja decorada. No entanto, a melhor prática é decorar injetáveis com @Injectable().

Nossa classe esta usando o decorator @Injectable() e por isso tivemos que importar a função Injectable do Angular para aplicarmos o decorador a essa função ( @Injectable() )

Vamos alterar o código do arquivo conforme abaixo:

import { Injectable } from '@angular/core';
import { Pais } from './pais';
import { Cidade } from './cidade'
@Injectable()
export class DataserviceService {
  getPaises() {
    return [
     new Pais(1, 'Brasil' ),
     new Pais(2, 'USA' ),
     new Pais(3, 'Itália' )
    ];
  }
   getCidades() {
   return [
     new Cidade(1, 1, 'São Paulo' ),
     new Cidade(2, 1, 'Brasília' ),
     new Cidade(3, 1, 'Rio de Janeiro'),
     new Cidade(4, 1, 'Santos'),
     new Cidade(5, 2, 'New Yord' ),
     new Cidade(6, 2, 'Chicago'),
     new Cidade(7, 2, 'Los Angeles' ),
     new Cidade(8, 3, 'Roma' ),
     new Cidade(9, 3, 'Florença' ),
     new Cidade(10, 3, 'Veneza')
    ];
  }
  constructor() { }
}

Definimos dois métodos :

  1. getPaises() : que retorna um array de paises;
  2. getCidades() : que retorna um array de cidades por paises;

Agora precisamos alterar o código do nosso componente definido no arquivo app.component.ts.

Abra este arquivo e altere o seu código conforme abaixo:

import { DataserviceService } from './dataservice.service';
import {Component, NgModule} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
import { Pais } from './pais';
import { Cidade } from './cidade';
@Component({
  selector: 'minha-lista-paises',
  templateUrl : './app.component.html',
  providers : [DataserviceService]
})
export class AppComponent {
   paises : Pais[];
   cidades : Cidade[];
   constructor(private _dataService: DataserviceService) {
     this.paises = this._dataService.getPaises();
   }
}

Destaques do código acima:

1 - O import do serviço feito - DataserviceService - pelo o Angular CLI
2 - O import das classes Pais e Cidade

A seguir vamos injetar o nosso serviço no componente e para isso precisamos adicionar dois recursos ao código:

1 - Adicionar um construtor que defina uma propriedade privada;

   constructor(private _dataService: DataserviceService) {
     this.paises = this._dataService.getPaises();
   }


2 - Adicionar o metada providers - O provider array informa ao Angular para criar uma nova instância do DataserviceService quando ele criar um AppComponent. O AppComponent, bem como seus componentes filho, podem usar esse serviço para obter dados das cidades e paises.-

  providers : [DataserviceService]

Agora podemos ajustar o arquivo app.component.html que vai exibir as informações ao usuário.

Abra o arquivo app.component.html na pasta app e altere o seu código conforme abaixo:

 <!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<!-- jQuery library -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!-- Latest compiled JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> 

<h2>Selecione o Pais: </h2>
 <select class="form-control">
    <option *ngFor="let pais of paises" value= {{pais.id}}>
      {{pais.nome}}
    </option>
 </select>

Este eu apenas inclui as referência CDN do bootstrap para dar uma aparência melhor à página.

O código praticamente não houve alteração. Executando novamente o projeto e abrindo o navegador em : http://localhost:4200 obtemos o seguinte resultado:

Tudo bem, mas isso é o mesmo resultado do artigo anterior. Agora vamos implementar o segundo select e o evento que irá permitir saber qual o pais foi selecionado para assim podermos obter as respectivas cidades deste país.

Implementando a seleção em cascata

Para implementar a seleção em cascata vamos primeiro alterar o código do arquivo app.component.html conforme abaixo:

 <!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<!-- jQuery library -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!-- Latest compiled JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> 
<h2>País: </h2>
 <select class="form-control" [(ngModel)]="paisSelecionado.id" (change)="onSelect($event.target.value)" id="sel1">
    <option value="0">--Selecione--</option>
    <option *ngFor="let pais of paises" value= {{pais.id}}> {{pais.nome}} </option>
 </select>
 <br>
 <div>
  <select class="form-control">
    <option *ngIf='paisSelecionado.id == 0' value="0">--Selecione--</option>
    <option *ngFor="let cidade of cidades" value= {{cidade.id}}> {{cidade.nome}} </option>
  </select>
 </div>  

Neste código incluímos a diretiva [(ngModel)]="paisSelecionado.id" para obter o id do pais selecionado e vamos fazer isso toda vez que um pais for selecionado definindo o evento (change) com o nome onSelect e usando o objeto $event para obter o valor selecionado pelo usuário.

Quando o evento change é disparado, $event.target representa quem disparou o evento. Sendo um elemento do DOM, podemos acessar seu valor com $event.target.value. É este valor que é atualizado na propriedade paisSelecionado.id.

Nota: Todos os objetos de evento DOM padrão possuem uma propriedade de target, uma referência ao elemento que gerou o evento. Neste caso, target refere-se ao elemento <select> e event.target.value retorna o conteúdo atual desse elemento que é o pais selecionado.

A seguir definimos outro elemento o <select> para exibir as cidades do pais selecionado. Para isso estamos usando a diretiva *ngIf.

A diretiva ‘*ngIf’ recebe como parâmetro um valor booleano, que indicará se o elemento deverá ou não ser exibido na tela. O parâmetro pode ser uma expressão, a chamada para um método do componente, ou um simples atributo de classe, más o importante é que o valor passado retorne um valor booleano.

A seguir usamos novamente  a diretiva *ngFor  para percorrer o array de cidades do pais selecionado exibindo-as na página.

Ajuste o código do arquivo app.component.ts conforme abaixo:

import { DataserviceService } from './dataservice.service';
import { Component, NgModule} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
import { Pais } from './pais';
import { Cidade } from './cidade';
@Component({
  selector: 'minha-lista-paises',
  templateUrl : './app.component.html',
  providers : [DataserviceService]
})
export class AppComponent {
   paisSelecionado : Pais = new Pais(0,'Brasil') ;
   paises : Pais[];
   cidades : Cidade[];
   constructor(private _dataService: DataserviceService) {
     this.paises = this._dataService.getPaises();
   }
   onSelect(id) {
    this.cidades = this._dataService.getCidades().filter((item)=> item.paisid == id);
  }
}

Incluimos a definição da variável paisSelecionado do tipo Pais atribuindo um valor padrão.

Definimos o código do evento onSelect() que será acionado sempre que o usuário selecionar um país. Neste caso filtramos as cidades pelo id do pais selecionado e atribuímos ao array cidades.

Ajustando o arquivo app.module.ts

O arquivo app.module.ts define o módulo raiz do aplicativo. Nele, você identifica os módulos externos que você usará no aplicativo e declarará os componentes que pertencem a este módulo.

Como os formulários orientados por modelo estão em seu próprio módulo, você precisa adicionar o FormsModule à matriz de importações para o módulo de aplicativo antes de poder usar formulários.

Para concluir temos que incluir uma referência a FormsModule no arquivo app.module.ts

import { DataserviceService } from './dataservice.service';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms'
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

 

Agora tudo esta pronto. Para testar vamos abrir uma nova janela de prompt de comando na pasta do projeto e digitar o comando : ng serve

Nota: podemos também digitar :  npm start

Abrindo um navegador e digitando a url :  http://localhost:4200 iremos obter o seguinte :

E assim implementamos uma seleção em cascata simples no Angular 2.

Pegue o projeto, sem as referências, aqui :   selecaoCascata.zip

Todas as coisas são puras para os puros, mas nada é puro para os contaminados e infiéis; antes o seu entendimento e consciência estão contaminados.
Confessam que conhecem a Deus, mas negam-no com as obras, sendo abomináveis, e desobedientes, e reprovados para toda a boa obra.

Tito 1:15,16

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Referências:


José Carlos Macoratti