Apresentando a lista de disciplinas
[info] Objetivos do capítulo:
- demonstrar a implementação do requisito "apresentar as disciplinas" de forma iterativa
- demonstrar o uso das diretivas
ngFor
engIf
- reforçar o entendimento do Data binding
Branches: livro-listando-disciplinas, livro-listando-disciplinas-objetos, livro-listando-disciplinas-classes, livro-listando-disciplinas-interacao
Começando pelo Controller do AppComponent
, remova o atributo title
e adicione o atributo disciplinas
. Esse atributo é um array de string, contendo os nomes das disciplinas. O conteúdo do Controller deve ficar mais ou menos assim (trecho):
export class AppComponent {
disciplinas = [
'Língua Portuguesa',
'Arte',
'Educação Física',
'Matemática',
'História',
'Geografia',
'Ciências',
'Redação',
'Língua Estrangeira Moderna - Inglês',
'Ensino Religioso'
];
}
Na sequência altere o Template do AppComponent
para o seguinte conteúdo:
<h1>Disciplinas</h1>
<hr>
<ul>
<li *ngFor="let disciplina of disciplinas">
{{disciplina}}
</li>
</ul>
Parte importante do Template está no elemento li
: o atributo *ngFor
é, na verdade, uma diretiva estrutural que modifica o Template no browser, gerando novos elementos com base em uma repetição.
A diretiva, na verdade mesmo, é chamada
NgForOf
. Usamos uma espécie de atalho por meio do atributo*ngFor
(sem mais explicações sobre isso, por enquanto). Então, ao lerNgForOf
ou*ngFor
saiba que estamos falando da mesma coisa.
A diretiva *ngFor
é utilizada para repetir um elemento do Template. Nesse caso, é justamente essa a sua aplicação: repetir o elemento li
para apresentar os nomes das disciplinas. A repetição é realizada por uma expressão: let disciplina of disciplinas
significa que o *ngFor
vai repetir o elemento li
para cada item do array disciplinas
e cada item recebe o nome de disciplina
, o que demonstra outra utilização do Data binding. Explicando a mesma coisa de outra forma:
- a repetição sempre é baseada em uma coleção de itens (ou em algo que possa ser interpretado como tal)
- o Angular cria uma variável de template (existe apenas dentro do elemento
li
) com o nome dedisciplina
- essa variável de template é usada como uma referência para cada item da repetição
É por isso que a interpolação funciona dentro do elemento li
para apresentar o nome de cada disciplina.
A documentação do Angular afirma que
let disciplina of disciplinas
é uma microssintaxe e isso não é a mesma coisa que uma expressão usada em uma interpolação.
Abra o aplicativo no browser. O resultado deve ser semelhante à figura a seguir.
Melhorando o modelo de dados usando objetos
A utilização de um array de strings pode ser suficiente em vários casos. Entretanto, quando se deseja apresentar mais informações é necessário utilizar outra abordagem. Suponha, portanto, que precisemos apresentar dois dados de cada disciplina: nome e descrição. Uma boa forma de fazer isso é utilizar objetos.
Modifique o Controller do AppComponent
para que cada item do array disciplinas
seja um objeto com dois atributos: nome
e descricao
, como mostra o trecho de código a seguir.
...
export class AppComponent {
disciplinas = [
{
nome: 'Língua Portuguesa',
descricao: 'O objetivo norteador da BNCC de Língua Portuguesa é ' +
'garantir a todos os alunos o acesso aos saberes ' +
'linguísticos necessários para a participação social e o exercício ' +
'da cidadania, pois é por meio da língua que ' +
'o ser humano pensa, comunica-se, tem acesso à informação, expressa ' +
'e defende pontos de vista, partilha ou ' +
'constrói visões de mundo e produz conhecimento.'
},
...
];
}
Essa notação do TypeScript para representar objetos de forma literal é muito útil quando não se tem uma classe para descrever objetos.
Na sequência ocorre uma pequena alteração no Template:
<h1>Disciplinas</h1>
<hr>
<ul>
<li *ngFor="let disciplina of disciplinas">
<strong>{{disciplina.nome}}</strong>: {{disciplina.descricao}}
</li>
</ul>
A principal mudança está no fato de que as interpolações usam expressões baseadas nos atributos do objeto disciplina
.
Para ver o resultado, abra o aplicativo no browser.
Melhorando o modelo de dados usando classes
Enquanto a utilização de objetos literais é bastante útil, também é interessante expressar o modelo de dados utilizando classes. Para isso, crie o arquivo ./src/app/disciplina.model.ts
com o conteúdo a seguir:
export class Disciplina {
nome: string;
descricao: string;
constructor(nome: string, descricao?: string) {
this.nome = nome;
this.descricao = descricao;
}
}
Para utilizar mais recursos do TypeScript, a classe Disciplina
possui dois atributos: nome
e descricao
, ambos do tipo string
. O método constructor()
recebe dois parâmetros, ambos do tipo string
: nome
e descricao
(que é opcional -- note o uso de ?
). Para concluir, a classe é disponibilizada para uso externo por meio da instrução export
.
Para usar a classe Disciplina
, modifique o Controller do AppComponent
:
import {Component} from '@angular/core';
import {Disciplina} from './disciplina.model';
...
export class AppComponent {
disciplinas = [
new Disciplina('Língua Portuguesa', 'O objetivo norteador ...'),
new Disciplina('Educação Física', 'A Educação Física é ...'),
...
];
}
Perceba que os elementos do atributo disciplinas
agora são instâncias da classe Disciplina
. O mais interessante é que o Template não precisa ser alterado, já que está utilizando objetos com a mesma estrutura de antes, ou seja, espera que os itens do atributo disciplinas
sejam objetos que tenham os atributos nome
e descricao
.
Adicionando interação com o usuário
A apresentação de todos os dados das disciplinas na tela pode ser melhorada usando interação com o usuário. Suponha que o usuário veja a lista das disciplinas contendo apenas seus nomes; ele deve clicar no nome de uma disciplina para ver sua descrição. Além disso, para dar uma resposta visual para o usuário quando ele clicar no nome da disciplina, considere que será usada uma formatação em negrito.
Para implementar esses requisitos vamos usar outros recursos do Angular.
Começando pelo Controller do AppComponent
são acrescentados:
- o atributo
selecionado
- o método
selecionar()
export class AppComponent {
selecionado = null;
disciplinas = [
new Disciplina('Língua Portuguesa', 'O objetivo norteador da BNCC ...'),
...
];
selecionar(disciplina) {
this.selecionado = disciplina;
}
}
O atributo selecionado
será utilizado para que o Controller saiba qual disciplina o usuário selecionou. O usuário fará isso por meio de uma chamada para o método selecionar()
, que recebe o parâmetro disciplina
e o atribui para o atributo selecionado
. Quando isso acontecer (o usuário selecionar uma disciplina) o software apresentará a sua descrição.
Para utilizar mais recursos do desenvolvimento front-end, dessa vez também alteramos o arquivo ./src/app/app.component.css
:
.selecionado {
font-weight: bold;
}
li p:nth-child(1) {
cursor: pointer;
font-size: 12pt;
}
li p:nth-child(1):hover {
font-weight: bold;
}
li p:nth-child(2) {
font-size: 10pt;
}
O arquivo CSS do AppComponent
possui regras de formatação:
- seletor
.selecionado
: para deixar o nome da disciplina selecionada em negrito - seletor
li p:nth-child(1)
: para que o cursor do mouse sobre o nome da disciplina sejapointer
(semelhante ao comportamento do cursor em links) e tenha fonte de tamanho 12pt - seletor
li p:nth-child(1):hover
: para que o nome da disciplina sob o mouse fique em negrito - seletor
li p:nth-child(2)
: para que a fonte da descrição seja de tamanho 10pt
Agora, o Template:
<h1>Disciplinas</h1>
<hr>
<ul>
<li *ngFor="let disciplina of disciplinas" (click)="selecionar(disciplina)">
<p [class.selecionado]="selecionado == disciplina">{{disciplina.nome}}</p>
<p *ngIf="selecionado == disciplina">{{disciplina.descricao}}</p>
</li>
</ul>
A primeira coisa a destacar é o recurso que proporciona a interação com o usuário. Como já comentado, o requisito determina que o usuário possa clicar no nome da disciplina. Isso é um ação do usuário para a interface. O Angular implementa isso por meio de eventos e de uma sintaxe própria: o atributo (click)
no elemento li
indica que estamos fornecendo um tratador para o evento click
; o tratador é o valor do atributo, ou seja, uma expressão que representa a chamada do método selecionar(disciplina)
, definido no Controller.
Outra característica importante é a chamada do método selecionar()
fornece como parâmetro disciplina
, que é o item da repetição realizada pelo *ngFor
. Se você voltar no código do Controller vai ver que esse parâmetro é atribuído para o atributo selecionado
, que faz justamente isso: guarda uma referência para a disciplina selecionada.
O próximo passo é garantir o determinado no requisito no sentido da apresentação. Para fazer com que a disciplina selecionada fique em negrito vamos aplicar uma classe CSS chamada selecionado
em um elemento p
que contém o nome da disciplina sempre que a disciplina
da repetição do *ngFor
for igual à disciplina selecionada. O Angular fornece um recurso para modificar características de elementos do HTML por meio de expressões. Esse recurso está sendo usado na tag <p>
: [class.selecionado]
parece como um atributo do HTML, mas é tratado pelo Angular antes de o HTML ser fornecido para o browser, modificando o atributo class
e inserindo nele a classe CSS chamada selecionado
. Nesse sentido, o valor do atributo é uma expressão e, nesse caso, uma expressão booleana: selecionado == disciplina
determina, então, que o elemento p
terá o atributo class
com valor selecionado
se a disciplina
atual da iteração do *ngFor
for igual à disciplina selecionada.
Para finalizar, o software deve apresentar a descrição da disciplina selecionada. Para isso vamos usar outra diretiva, a NgIf
. Veja o trecho:
<li *ngFor="let disciplina of disciplinas" ...>
...
<p *ngIf="selecionado == disciplina">{{disciplina.descricao}}</p>
</li>
A diretiva *ngIf
é aplicada no elemento p
usado para apresentar a descrição da disciplina. Como o propósito dessa diretiva é funcionar como um condicional o valor que o atributo recebe é uma expressão booleanda: selecionado == disciplina
. Se a expressão for verdadeira (se a disciplina
da repetição do *ngFor
for igual à disciplina selecionada), o elemento p
estará presente no HTML, caso contrário, não estará presente. Perceba que usei o termo "estar presente" ao invés de "visível" porque é isso que acontece. Se a expressão booleanda for falsa, o elemento no qual a diretiva *ngIf
é aplicada nem está presente no HTML.
Para ilustrar isso, veja um trecho da estrutura HTML no browser:
<ul _ngcontent-c0="">
<!--bindings={"ng-reflect-ng-for-of": "[object Object],[object Object"}-->
<li _ngcontent-c0="">
<p _ngcontent-c0="" class="selecionado">Língua Portuguesa</p>
<!--bindings={"ng-reflect-ng-if": "true"}-->
<p _ngcontent-c0="">O objetivo norteador da BNCC de Língua Portuguesa ...</p>
</li>
<li _ngcontent-c0="">
<p _ngcontent-c0="" class="">Educação Física</p>
<!--bindings={"ng-reflect-ng-if": "false"}-->
</li>
...
</ul>
Os vários comentários presentes no HTML parecem ser algo que o Angular utiliza para tratar o comportamento da interface? É justamente isso que acontece (não precisa tentar interpretar os comentários agora). No caso desse exemplo, o usuário clicou na disciplina "Língua Portuguesa". Características importantes:
- para o elemento
li
correspondente à disciplina selecionada:- o elemento
p
que mostra o nome da disciplina possuiclass="selecionado"
- o elemento
p
que mostra a descrição da disciplina está presente no HTML
- o elemento
- para os demais elementos
li
:- o elemento
p
que mostra o nome da disciplina não possui o atributoclass
- o elemento
p
que mostra a descrição da disciplina não existe no HTML
- o elemento
Se você já tiver programado para front-end anteriormente vai entender melhor a complexidade do que está acontecendo aqui, que o Angular não passa para o programador. O Angular está realizando a chamada manipulação do DOM para que o HTML entregue para o browser tenha o comportamento esperado (ou explícito no software).
Por fim, a figura a seguir ilustra o funcionando do software no browser.
Pronto, esse capítulo mostrou a utilização de vários recursos do Angular para implementar o requisito "Apresentar a lista das disciplinas".
[info] Para resumir:
- utilizar recursos da Programação Orientada a Objetos é muito bom para representação do modelo de dados
- a diretiva
NgForOf
é utilizada para repetir elementos no Template com base em uma coleção de itens (um array)- a diretiva
NgIf
é utilizada para incluir ou excluir elementos do Template com base em um condicional(click)
representa a sintaxe do Angular para criar tratadores de eventos[class.selecionado]
representa a sintaxe do Angular para adicionar ou remover atributos no HTML