Baixe o app para aproveitar ainda mais
Prévia do material em texto
1.1 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 2.10 2.11 3.1 3.2 3.3 3.4 3.5 3.6 Tabela de conteúdos Introdução Conceitos iniciais Iniciando com o Angular Angular CLI Software de gerenciamento escolar Criando o projeto Depurando código no browser Iniciando com teste de software Apresentando a lista de disciplinas Cadastrando uma disciplina Excluindo uma disciplina Editando dados de uma disciplina Depurando código no browser Nível intermediário Interação entre componentes Serviços Acessando dados externos de forma assíncrona Acessando dados de uma API HTTP Rotas Feature-modules 1 Desenvolvimento web front-end com Angular Este é um livro open-source, tanto no conteúdo quanto no código-fonte associado. O conteúdo é resultado de algumas práticas com o Framework Angular e segue uma abordagem prática, com foco no entendimento de conceitos e tecnologias no contexto do desenvolvimento de um software de gerenciamento escolar. Com o passar dos capítulos será possível perceber a utilização iterativa e incremental de recursos até chegar à conclusão do desenvolvimento do gerenciador escolar. Parte do conteúdo do livro é baseada na documentação oficial do Angular, disponível em https://angular.io. Como o Angular é um projeto em constante desenvolvimento (pelo menos até agora) serão publicadas atualizações no conteúdo do livro sempre que possível, para refletir novos recursos e funcionalidades. No momento, o conteúdo do livro é baseado na versão 5.2.0. Conhecimentos desejáveis Este livro aborda o desenvolvimento de software front-end para a web do ponto-de-vista do Angular. Isso quer dizer que não trata de conceitos iniciais de HTML, CSS, JavaScript, TypeScript e Bootstrap. Entretanto, os conceitos fundamentais dessas tecnologias vão sendo apresentados no decorrer dos capítulos, conforme surge a necessidade deles. Para aprender mais sobre essas tecnologias recomendo essas fontes: TypeScript: Documentação oficial do TypeScript - Microsoft, TypeScript Deep Dive HTML, CSS e JavaScript: W3Schools Boostrap: Documentação oficial do Bootstrap Código-fonte O software Angular Escola, que serve como apoio para este livro está disponível gratuitamente no Github: https://github.com/jacksongomesbr/angular-escola. O repositório provê branches que demonstram a evolução do desenvolvimento do software na mesma ordem dos capítulos do livro. Cada capítulo, se necessário, indica qual branch utilizar. Introdução 2 https://angular.io https://www.typescriptlang.org/docs/home.html https://www.gitbook.com/book/basarat/typescript/details https://www.w3schools.com/ http://getbootstrap.com/docs/4.0/getting-started/introduction/ https://github.com/jacksongomesbr/angular-escola Dependências Para utilizar o código-fonte do livro e desenvolver em Angular você precisa, antes, montar seu ambiente de desenvolvimento com algumas ferramentas: 1. Git: disponível em https://git-scm.com/ 2. Node.js: disponível em https://nodejs.org representa um ambiente de execução do JavaScript fora do browser e também inclui o npm, um gerenciador de pacotes 3. Editor de textos ou IDE: atualmente há muitas opções, mas destaco estas: i. VisualStudioCode: disponível em https://code.visualstudio.com/ ii. Nodepad++: disponível em https://notepad-plus-plus.org/ iii. PHPStorm ou WebStorm: disponíveis em https://www.jetbrains.com Visão geral A tabela a seguir apresenta uma visão geral dos conceitos apresentados ou discutidos em cada capítulo do livro. Capítulo Conteúdos Iniciando com o Angular Arquitetura do Angular; Elementos da Arquitetura doAngular (ex.: módulos e componentes) Angular CLI Ferramenta Angular CLI; Comandos Criando o projeto Ferramenta Angular CLI; Comando new Apresentando a lista de disciplinas Data binding; Diretivas; Eventos Cadastrando uma disciplina Formulários; Two-way Data binding; Eventos Excluindo uma disciplina Two-way Data Binding; Eventos Editando dados de uma disciplina Formulários; Two-way Data Binding; Eventos Melhorando a interface gráfica do CRUD de disciplinas Formulários; Data Binding; Eventos; Bootstrap; Font Awesome Interação entre componentes Criação de componentes; Input e Output parainteração entre componentes Introdução 3 https://git-scm.com/ https://nodejs.org https://code.visualstudio.com/ https://notepad-plus-plus.org/ https://www.jetbrains.com Iniciando com o Angular [info] Objetivos do capítulo: demonstrar os elementos da Arquitetura do Angular demonstrar a relação entre os elementos da Arquitetura do Angular e arquivos do projeto Branch: iniciando O Angular é um framework para o desenvolvimento de software front-end. Isso quer dizer que utiliza tecnologias padrão do contexto web como HTML, CSS e uma linguagem de programação como JavaScript ou TypeScript. Um software desenvolvido em Angular é composto por diversos elementos como: módulos, componentes, templates e serviços. Esses elementos fazem parte da arquitetura do Angular, que é ilustrada pela figura a seguir. Essa arquitetura de software orientada a componentes implementa conceitos de dois padrões de arquitetura de software: MVC (Model, View, Controller) é um padrão de software que separa a representação da informação (Model) da interação do usuário com ele (View-Controller). Geralmente, Model e Controller são representados por código em linguagem de programação (classes e/ou funções) e View é representado por HTML e CSS. MVVM (Model, View, View-Model) é um padrão de software semelhante ao MVC, com a diferença de que o View-Model utiliza recurso de data binding (mais sobre isso Iniciando com o Angular 4 https://github.com/jacksongomesbr/angular-escola/tree/iniciando depois) para fazer com que a View seja atualizada automaticamente quando ocorrer uma modificação no Model. No contexto do Angular esses elementos são descritos conforme as seções a seguir. Elementos da Arquitetura do Angular Módulos Módulos representam a forma principal de modularização de código. Isso significa que um módulo é um elemento de mais alto nível da arquitetura do Angular e é composto por outros elementos, como componentes e serviços. Um software desenvolvido em Angular possui pelo menos um módulo, chamado root module (módulo raiz). Os demais módulos são chamados feature modules (módulos de funcionalidades). Bibliotecas Bibliotecas funcionam como um agrupador de elementos de software desenvolvido em Angular. Bibliotecas oficiais têm o prefixo @angular . Geralmente é possível instalar bibliotecas utilizando o npm (gerenciador de pacotes do NodeJs). Uma biblioteca pode conter módulos, componentes, diretivas e serviços. Componentes Um componente está, geralmente, relacionado a algo visual, ou seja, uma tela ou parte dela. Nesse sentido, um componente possui código (Controller) que determina ou controla o comportamento da interação com o usuário (View ou Template). O Template determina a parte visual do componente e é definido por código HTML e CSS, além de recursos específicos do Angular, como outros componentes e diretivas. Metadados Metadados são um recurso do Angular para adicionar detalhes a classes. Isso é utilizado para que o Angular interprete uma classe como um Módulo ou como um Componente, por exemplo. Data binding Iniciando com o Angular 5 Data binding (que seria algo como "vinculação de dados" em português) é um reucrso do Angular que representa um componente importante da sua arquitetura. Considere os seguintes elementos: um Model define dados que serão apresentados no Template um Template apresenta os dados do Model um Controller ou um View-Model determina o comportamento do Template Se o Controller atualiza o Model, então o Template tem que ser atualizado automaticamente. Se o usuário atualiza o Model por meio do Template (usando um formulário, por exemplo) o Controller também precisa ter acesso ao Model atualizado. O Data Binding atua garantindo que esse processo ocorra dessa forma. Diretivas Diretivas representam um conceito do Angular que é um pouco confuso. Na prática, um Componenteé uma Diretiva com um Template. Assim, um Componente é um tipo de Diretiva, que nem sempre está relacionada a algo visual. Angular define dois tipos de diretivas: Diretivas Estruturais: modificam o Template dinamicamente por meio de manipulação do DOM (Document Object Model), adicionando ou removendo elementos HTML Diretivas de Atributos: também modificam o Template, mas operam sobre elementos HTML já existentes Serviços Um Serviço é uma abstração do Angular utilizado para isolar a lógica de negócio de Componentes. Na prática, um Serviço é representado por uma classe com métodos que podem ser utilizados em Componentes. Para isso, para que um Componente utilize um serviço, o Angular utiliza o conceito de Injeção de Dependência (DI, do inglês Dependency Injection). DI é um padrão de software que faz com que dependências sejam fornecidas para quem precisar. Na prática, o Angular identifica as dependências de um Componente e cria automaticamente instâncias delas, para que sejam utilizadas posteriormente no Componente. Enquanto esses elementos da Arquitetura do Angular representam conceitos, é importante visualizar a relação deles com elementos práticos do software, ou seja, código. Para isso, a seção a seguir apresenta a estrutura padrão de um software desenvolvido em Angular. Iniciando com o Angular 6 Estrutura padrão de um software desenvolvido em Angular Um software desenvolvido em Angular é representado por vários arquivos HTML, CSS, TypeScript e de configuração (geralmente arquivos em formato JSON). + src + app - app.component.css - app.component.html - app.component.ts - app.module.ts + assets + environments - environment.prod.ts - environment.ts - index.html - maint.ts - polyfills.ts - styles.css - tsconfig.app.json - typings.d.ts - .angular-cli.json - package.json - tsconfig.json - tslint.json No diretório raiz do software: src : contém o código-fonte do software (módulos, componentes etc.) .angular-cli.json : contém configurações do projeto Angular (nome, scripts etc.) package.json : contém configurações do projeto NodeJS (um projeto Angular utiliza NodeJS para gerenciamento de pacotes e bibliotecas, por exemplo) tsconfig.json e tslint.json : contêm configurações do processo de tradução de código TypeScript para JavaScript No diretório src : app : contém o root module e os demais feature modules do projeto assets : contém arquivos CSS, JSON e scripts, por exemplo environments : contém arquivos de configuração do ambiente de desenvolvimento ( environment.ts ) e de produção ( environment.prod.ts ) index.html : contém o código HTML inicial para o projeto e inclui o componente principal do root module main.ts : contém o código TypeScript necessário para iniciar o software (processo Iniciando com o Angular 7 chamado de Bootstrap) polyfills.ts : contém código TypeScript que indica scripts adicionais a serem carregados pelo Browser para funcionamento do software como um todo e para questões de compatibilidade com versões antigas de Browsers style.css : contém o código CSS para definir estilos globais para o software tsconfig.app.json e typings.d.ts : complementam configurações do arquivo ../tsconfig.json específicas para o software em questão No diretório app : app.component.css , app.component.html e app.component.ts : definem o component AppComponent, respectivamente: apresentação por meio de CSS, Template e Controller. Basicamente, estes três arquivos formam a base de todo componente app.module.ts : código TypeScript que define o root module Esse capítulo incial do livro apresentou conceitos importantes do Angular. A maior parte dos conceitos está diretamente relacionada a código-fonte (HTML, CSS, TypeScript e JSON) que estão em arquivos do diretório onde encontra-se o projeto. Sempre que necessário, volte a esse capítulo para revisar conceitos do Angular. Muito provavelmente, mesmo desenvolvedores experientes precisem, de tempos em tempos, rever essas definições da arquitetura do Angular. Iniciando com o Angular 8 Angular CLI [info] Objetivos do capítulo: apresentar o Angular CLI apresentar comandos para criação de projetos, módulos, componentes e outros elementos apresentar o arquivo .angular-cli.json O Angular CLI (disponível em https://cli.angular.io) é uma ferramenta para inicializar, desenvolver, criar conteúdo e manter software desenvolvido em Angular. A principal utilização dessa ferramenta começa na criação de um projeto. Você pode fazer isso manualmente, claro, mas lidar com todas as configurações necessárias para o seu software Angular pode não ser algo fácil, mesmo se você for um desenvolvedor com nível de conhecimento médio a avançado. [info] cê-éle-í? Como parte do vocabulário do Angular, geralmente é interessante pronunciar angular cê-ele-i, isso mesmo, CLI é uma sigla para Command Line Interface (no português Interface de Linha de Comando). Assim, usar "cli" não é exclusividade alguma do Angular -- provavelmente você verá isso em outros projetos. As seções a seguir vão apresentar como instalar e utilizar o Angular CLI para desenvolver software Angular. Instalando O Angular CLI é distribuído como um pacote do NodeJS, então você não encontrará um instalador, como acontece com software tradicional. Ao invés disso, você precisa de um ambiente de desenvolvimento com o NodeJS instalado (esse capítulo não trata disso). Depois de ter o NodeJS instalado, a instalação do Angular CLI passa pela utilização do npm, o gerenciador de pacotes do NodeJS. Para isso, a seguinte linha de comando deve ser executada: npm install -g @angular/cli Isso fará uma instalação global do Angular CLI (por isso o -g na linha de comando) e disponibilizará o programa ng . Angular CLI 9 https://cli.angular.io [warning] Problemas na instalação? Podem acontecer certos problemas na instalação do Angular CLI. Por exemplo, você pode estar com uma versão bem desatualizada do NodeJS e do npm. Assim, garanta sempre que seu ambiente de desenvolvimento esteja com as versões corretas desses programas. Outra situação é se você tiver problemas para fazer uma instalação global. Se for o caso, faça uma instalação local (sem o -g na linha de comando) e execute o programa ng que está no caminho ./node_modules/.bin/ng . Comandos O Angular CLI disponibiliza uma série de comandos, que são fornecidos como parâmetros para o programa ng. Além disso, cada comando possui diversas opções (ou flags). Faça o exercício de se acostumar a essas opções para aumentar a sua produtividade. Os principais comandos serão apresentados nas seções seguintes. help O comando help é muito importante porque apresenta uma documentação completa do Angular CLI, ou seja, todos os comandos e todas as suas opções. Exemplo: documentação completa ng help Essa linha de comando apresenta todos os comandos e todas as suas opções. Exemplo: documentação do comando new : ng help new Essa linha de comando apresenta apenas as opções do comando new . As opções de cada comando são sempre precedidas de -- (dois traços). Algumas opções aceitam um valor e geralmente possuem um valor padrão. Exemplo: comando new com duas opções ng new escola-app --skip-install --routing true --minimal true Angular CLI 10 Essa linha de comando fornece três opções para o comando new : --skip-install , -- routing (que recebe o valor true ) e --minimal (que recebe o valor true ). new O comando new é utilizado para criar um projeto (um software Angular). Exemplo: ng new escola-app Quando a linha de comando do exemplo for executada será criado um software Angular chamado escola-app e estará em um diretório chamado escola-app , localizado a partir de onde a linha de comando estiver sendo executada. A parte mais interessante de criar um projeto Angular com esse comando é que ele cria todos os arquivos necessários para um software bastante simples, mas funcional. Para cada comando do Angular CLI é possível informar opções. Asopções mais usadas do comando new são: --skip-install : faz com que as dependências não sejam instaladas --routing : se for seguida de true , o comando cria um módulo de rotas (isso será visto em outro capítulo generate O comando generate é utilizado para criar elementos em um projeto existente. Para simplificar, é bom que o Angular CLI seja executado a partir do diretório do projeto a partir de agora. Por meio desse comando é possível criar: class component directive enum guard interface module pipe service Como acontece com outros comandos, o generate também pode ser utilizado por meio do atalho g . Exemplo: criar component ListaDeDisciplinas Angular CLI 11 ng generate component ListaDeDisciplinas Essa linha de comando criará o diretório ./src/app/lista-de-disciplinas e outros quatro arquivos nesse mesmo local: lista-de-disciplinas.component.css lista-de-disciplinas.component.html lista-de-disciplinas.component.spec.ts lista-de-disciplinas.component.ts É importante notar que esses arquivos não estão vazios, mas já contêm código que mantém o software funcional e utilizável. Por isso o Angular CLI considera que as opções (class, component etc.) são como blueprints (ou plantas, em português). Outra coisa importante é que o Angular CLI também modificará o arquivo ./src/app/app.module.ts se necessário (ou outro arquivo que represente um módulo que não seja o root module -- mais sobre isso depois). serve O comando serve compila o projeto e inicia um servidor web local. Por padrão o servidor web executa no modo --watch , que reinicia o comando novamente, de forma incremental, sempre que houver uma alteração em algum arquivo do projeto, e --live-reload , que recarrega o projeto no Browser (se houver uma janela aberta) sempre que houver uma mudança. build O comando build compila o projeto, mas não inicia um servidor web local. Ao invés disso, gera os arquivos resultantes da compilação em um diretório indicado. Veremos mais sobre esses e outros comando no decorrer do livro. Angular CLI 12 [info] Espera, compilação? Como já disse, você pode utilizar JavaScript ou TypeScript ao desenvolver projetos Angular. Isso não é bem assim. Na prática, geralmente você encontrará a recomendação (senão uma ordem) de utilizar TypeScript. O problema é que seu Browser não entende TypeScript, por isso é necessário um processo de tradução do código TypeScript para JavaScript. E não é só isso. Há vários recursos utilizados no projeto Angular criado com TypeScript que precisam de ajustes para que funcionem no seu Browser. Por isso os comandos serve e build são tão importantes e -- por que não dizer? -- browser-friendly =) Angular CLI 13 Software de gerenciamento escolar [info] Objetivo do capítulo: apresentar informações sobre gerenciamento escolar apresentar requisitos do software de gerenciamento escolar Neste livro você verá informações técnicas sobre desenvolvimento de software web com Angular e isso é colocado em prática por meio do desenvolvimento de um software de gerenciamento escolar. Antes disso, entretanto, vamos ao contexto: gerenciamento escolar. Contexto da escola No contexto desse livro o gerenciamento escolar é aplicado em uma escola fictícia com as seguintes características: a estrutura de pessoal (recursos humanos) tem as seguintes características: funcionários são pessoas contratadas para ocupar cargos e realizar funções um funcionário tem um cargo um funcionário tem uma ou mais funções um funcionário pode ser: administrativo ou docente a estrutura acadêmica da escola tem as seguintes características: há três níveis de ensino: ensino infantil, ensino fundamental, ensino médio cada nível de ensino pode ser organizado em anos de ensino e cada um possui disciplinas a relação entre nível de ensino e ano de ensino é chamada de estrutura curricular, cada qual com disciplinas e suas cargas horárias anuais uma turma é quando um item do componente curricular é ministrado durante um ano letivo e tem as seguintes características: pode ter um ou mais professores por disciplina pode ter um ou mais alunos matrícula é quando um aluno é matriculado em uma turma e cursa todas as disciplinas do ano letivo fisicamente, está presente em uma sala de aula (sala padrão) uma aula é quando conteúdos didáticos de uma disciplina da turma são trabalhados com professores e alunos em horários específicos na semana uma aula pode ter participação de um ou mais professores [da turma] uma aula pode ser realizada na sala de aula padrão da turma ou em instalação física Software de gerenciamento escolar 14 uma aula é realizada em um dia da semana, em um horário específico (informação utilizada para compor a grade horária) em cada aula o professor registra a frequência do aluno (presença ou falta) cada aula tem a duração de 50 minutos e conta como uma hora (na Carga Horária) os conteúdos das aulas são organizados em bimestres e, por isso, são realizadas avaliações bimestrais, totalizando quatro (para cada disciplina) no ano letivo ao final do ano letivo: as quatro notas são utilizadas para compor a nota final, que é uma média aritmética das notas dos bimestres o aluno é considerado "APROVADO" na disciplina se tiver média aritmética igual ou superior a 6,0 e frequência superior a 75% o aluno que não conseguir nota final suficiente pode realizar uma prova de recuperação, cuja nota substitui a nota final. Por fim, se o aluno não obtiver nota igual ou superior a 6,0 na recuperação ele é considerado "REPROVADO" em situações excepcionais o aluno pode ser considerado "APROVADO" em conselho de classe (uma reunião realizada ao final do ano letivo com os professores) com exceção do ensino infantil, o aluno só pode ser matriculado no ano seguinte se tiver sido aprovado em todas as disciplinas do ano corrente a estrutura física da escola tem as seguintes características: uma instalação física pode ser: prédio, sala admnistrativa, laboratório ou sala de aula Para ilustrar esse contexto, alguns exemplos de estruturas curriculares: Figura 2.3.1 - Exemplo de estrutura curricular do Ensino Médio - 1º ao 5º ano Software de gerenciamento escolar 15 Figura 2.3.2 - Exemplo de estrutura do Ensino Fundamental - 6º ao 9º ano Figura 2.3.3 - Exemplo de Estrutura curricular do Ensino Médio Software de gerenciamento escolar 16 Figura 2.3.4 - Exemplo de Estrutura curricular do Ensino Infantil Gerenciamento escolar Com base nas características da escola usada nesse contexto, o software de gerenciamento escolar compreende os seguintes requisitos conforme os atores: gerente do sistema gerenciar o cadastro de usuários e permissões de acesso gerente administrativo gerenciar o cadastro de cargos gerenciar o cadastro de funções gerenciar o cadastro de funcionários gerenciar o cadastro de instalações físicas gerente de ensino gerenciar o cadastro de disciplinas gerenciar o cadastro de níveis de ensino gerenciar o cadastro de estruturas curriculares gerenciar o cadastro de turmas (bem como professores nas turmas e horários) gerenciar o cadastro de alunos (geral) gerenciar o cadastro de matrículas (alunos nas turmas) registrar aprovação ou reprovação excepcional em conselho de classe professor ver as turmas em que ministra disciplinas ver os alunos das turmas em que ministra disciplinas ver a agenda de aula das turmas em que ministra disciplinas (turmas, disciplinas, salas de aula, dias da semana e horários de aula) ver a agenda de aula da escola (todas as turmas do ano letivo) gerenciar o cadastro de frequência dos alunos (nas suas turmas e disciplinas) Software de gerenciamento escolar 17 gerenciar as notas das avaliações bimestrais dos alunos (nas suas turmas e disciplinas) aluno (e/ou pais ou responsáveis) ver a estrutura curricular do nível e ano de ensino em que está matriculado ver a estrutura curricular de todos os níveis e anos de ensino ver a agenda de aula da turma em que está matriculado (disciplinas, professores, salas de aula, dias da semana e horários de aula)ver o histórico acadêmico (notas e situação final para as turmas cursadas) Uma descrição mais completa dos requisitos pode ser encontrada aqui: http://200.199.229.99/reqman/projects/3/documentation.pdf. Software de gerenciamento escolar 18 http://200.199.229.99/reqman/projects/3/documentation.pdf Criando o projeto [info] Objetivos do capítulo: demonstrar como utilizar o Angular CLI para criar o projeto angular-escola apresentar os requisitos de software que serão implementados no capítulo Branch: iniciando O capítulo Angular CLI apresentou essa ferramenta, comandos e opções. A primeira utilização da ferramenta está na criação do projeto com o comando: ng new angular-escola Isso fará com que seja criado um diretório angular-escola no local onde o comando foi executado. Como já informado, o diretório contém código-fonte de um software Angular funcional. Para vê-lo em execução acesse o diretório angular-escola e execute o comando: npm start Esse script executa o comando ng serve do Angular CLI e cria um servidor web local, por padrão na porta 4200. Para ver o software em funcionamento, abra o endereço http://localhost:4200 no seu browser de preferência. Mais para frente podemos discutir um pouco mais a saída desse comando, porque é muito interessante. O resultado deverá ser semelhante ao da figura a seguir. Criando o projeto 19 https://github.com/jacksongomesbr/angular-escola/tree/iniciando Figura 2.4.1 - Versão inicial do software em execução no Browser Esse não é um comando do Angular CLI. Antes, é um comando (script) que está definido no arquivo package.json . O arquivo package.json O arquivo package.json contém informações do projeto, scripts e dependências. O arquivo criado pelo Angular CLI tem mais ou menos o seguinte conteúdo: Criando o projeto 20 { "name": "angular-escola", ... "scripts": { "ng": "ng", "start": "ng serve", "build": "ng build --prod", ... }, "private": true, "dependencies": { "@angular/animations": "^5.2.0", "@angular/common": "^5.2.0", "@angular/compiler": "^5.2.0", "@angular/core": "^5.2.0", "@angular/forms": "^5.2.0", "@angular/http": "^5.2.0", "@angular/platform-browser": "^5.2.0", "@angular/platform-browser-dynamic": "^5.2.0", "@angular/router": "^5.2.0", "core-js": "^2.4.1", "rxjs": "^5.5.6", "zone.js": "^0.8.19" }, "devDependencies": { "@angular/cli": "1.6.6", "@angular/compiler-cli": "^5.2.0", "@angular/language-service": "^5.2.0", ... } } Por ser um conteúdo em formato JSON, o interpretamos da seguinte forma: name : é o atributo que define o nome do projeto para o NodeJS (nesse ponto, não tem a ver com o Angular) scripts : é o atributo que contém scripts que podem ser executados no prompt (exemplos: npm start e npm run build ). Nesse caso esses scripts usam o Angular CLI (veja os valores dos atributos start e build ) dependencies : é o atributo que define as dependências do projeto. Basicamente, as dependências atuais são padrão para software desenvolvido em Angular. Importante notar que o nome de cada atributo determina o pacote (como @angular/core ) e seu valor determina a versão (como ̂ 5.2.0 , indicando a versão desses pacotes do Angular) devDependencies : é o atributo que define as dependências de desenvolvimeto. São ferramentas que não geram código-fonte que será distribuído junto com o software que está sendo desenvolvido Criando o projeto 21 Examinando o código-fonte#0 Agora que você já conhece um pouco mais da estrutura do software Angular, do Angular CLI e do npm, vamos dar uma olhada no código-fonte que está gerando o software atual, começando pelo AppComponent e seu Template, que está no arquivo ./src/app/app.component.html , cujo trecho de conteúdo é apresentado a seguir. <div style="text-align:center"> <h1> Welcome to {{ title }}! </h1> <img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN...Pg=="> </div> ... Se HTML for familiar pra você então não há problema para entender o código do Template. Na verdade, tem algo diferente ali perto de Welcome to e vamos falar disso daqui a pouco. Agora, o Controller do mesmo componente, no arquivo ./src/app/app.component.ts , com código-fonte a seguir: import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'app'; } Há pouco conteúdo, mas já são usados vários conceitos do Angular, vamos examinar um-a- um: 1. a primeira linha usa a instrução import para incluir algo chamado Component e que é disponibilizado pelo pacote @angular/core . 2. Component é uma annotation function e é utilizada pelo Angular para adicionar metadados à classe AppComponent . 3. Os metadados são fornecidos como parâmetro para a função Component : i. selector : indica que o seletor é app-root . E o que é seletor? Veremos daqui a pouco ii. templateUrl : indica que o arquivo utilizado para o template do componente é ./app.component.html (que vimos há pouco) Criando o projeto 22 iii. styleUrls : é um array que indica quais são os arquivos CSS utilizados no componente. Nesse caso, está sendo utilizado apenas o arquivo ./app.component.css 4. a instrução export é utilizada para que outros arquivos possam utilizar a classe AppComponent Antes de prosseguirmos, perceba que há um atributo da classe AppComponent chamado title e tem o valor 'app' (onde já vimos isso?). A figura a seguir ilustra a estrutura do AppComponent . Figura 2.4.2 - Estrutura do component AppComponent Como mostra a figura o componente AppComponent é composto por Template, Controller e Model. O Template está definido no arquivo ./src/app/app.component.html , enquanto o Controller e o Model estão definidos no arquivo ./src/app/app.component.ts . Template e Controller são mais fáceis de identificar nessa arquitetura. Enquanto o primeiro está no arquivo HTML o segundo é a classe no arquivo TypeScript. Entretanto, onde está o Model? Antes de responder essa pergunta faça um alteração no Controller: altere o valor do atributo title para, por exemplo, 'Angular' . Se você não tiver interrompido a execução do script npm start acontecerá uma recompilação incremental automaticamente e a janela do Browser será recarregada. Na página, o que está logo depois de Welcome to ? Isso mesmo, a palavra "Angular". Que mágica é essa? Nenhuma mágica! Veja novamente o Template, mais de perto: <h1> Welcome to {{ title }}! </h1> Criando o projeto 23 Nesse momento você já deve ter aprendido que o trecho {{ title }} é responsável por fazer com que o valor do atributo title to Controller apareça no Browser. Isso é resultado do processo de Data binding: analisa o Controller e identifica métodos e atributos interpreta e analisa o Template e procura por, por exemplo, conteúdo que esteja entre {{ e }} utiliza o recurso de interpolação, que faz com a expressão dentro de {{ e }} seja interpretada e gere uma alteração no conteúdo do Template ao ser apresentado no Browser. Nesse caso {{ title }} significa: mostre o valor de title . A figura a seguir ilustra os elementos desse processo. Figura 2.4.3 - Relação entre Model-Template-Controller no AppComponent Aqui aprendemos algo um conceito importante: o Angular interpreta o Template. Isso significa que todo o seu conteúdo, HTML e CSS, é interpretado pelo Angular antes de ser entregue para o Browser. É isso que faz com que o recurso de interpolação funcione. É por isso, também, que Data binding é tão importante no Angular. Ele é responsável por fazer com que o atributo title , que compõe o Model (ah, agora sim!), esteja disponível para ser usado no Template. Além disso, qualquer alteração no valor desse atributo representará uma alteração na visualização do componente no Browser. Se você se lembrar da discussão sobreAngular ser MVC ou MVVM é aqui que o entendimento pesa a favor do segundo. O Controller não está desassociado do Model e, por causa do Data binding, sua função inclui informar o Template de que ocorreu uma alteração no Model, de forma que ele seja atualizado. Criando o projeto 24 É isso, o Model é um conceito abstrato e está, geralmente, no Controller, representado por atributos e métodos -- sim, também é possível mostrar o valor retornado por um método no template (mais sobre isso depois). Para finalizar essa seção falta entender como o AppComponent é apresentado e qual a relação dele com o AppModule ou outros componentes do projeto. Para isso, veja o arquivo ./src/index.html : <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Angular Escola</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> </head> <body> <app-root></app-root> </body> </html> Ele é o primeiro arquivo que o Browser acessa, embora não somente com esse conteúdo. Quando o Angular compila o código-fonte ele entrega um pouco mais, veja: <!doctype html> <html lang="en"> ... </head> <body> <app-root></app-root> <script type="text/javascript" src="inline.bundle.js"></script> <script type="text/javascript" src="polyfills.bundle.js"></script> <script type="text/javascript" src="styles.bundle.js"></script> <script type="text/javascript" src="vendor.bundle.js"></script> <script type="text/javascript" src="main.bundle.js"></script> </body> </html> Esse é um trecho do código-fonte visualizado pelo Browser. Perceba as linhas com elementos script antes da tag </body> . Esses elementos não estão no código-fonte original, eles foram embutidos aí pelo processo de compilação do Angular (que foi iniciado quando o script npm start foi executado). Não precisa fazer isso agora (sério!) mas em algum momento você vai perceber que o conteúdo daqueles arquivos indicados nos elementos script é realmente muito importante porque eles representam o resultado da Criando o projeto 25 tradução de todo o código-fonte do projeto em conteúdo que o Browser consegue interpretar. Destaque para o arquivo main.bundle.js , que contém o conteúdo do arquivo ./src/main.ts cujo trecho é: import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; ... platformBrowserDynamic().bootstrapModule(AppModule) .catch(err => console.log(err)); O método platformBrowserDynamic() retorna um objeto que contém o método boostrapModule() que, ao ser executado, recebe como parâmetro a classe AppModule , que será utilizada para representar o root module. Lembra disso? No capítulo Iniciando com o Angular vimos que o root module é o módulo mais importante de todo projeto Angular, porque é o primeiro a ser executado (agora você deve estar entendendo isso). Certo, então o Angular carrega primeiro o index.html , depois usa o AppModule como root module, mas isso ainda não explica como o AppComponent passou a aparecer no Browser. Para entender isso, veja o arquivo ./src/app/app.module.ts , que define o AppModule : import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { } As três primeiras linhas importam os módulos BrowserModule e NgModule dos seus respectivos pacotes, bem como a classe AppComponent . Como você já sabe, o Angular utiliza annotation functions para adicionar metadados a classes. Aqui está sendo utilizada a função NgModule() que transforma a classe AppModule em um módulo para o Angular. O objeto passado como parâmetro para a função possui características importantes a destacar: declarations : é um vetor que contém componentes do módulo (isso mesmo, Criando o projeto 26 AppComponent está contido no AppModule ); imports : é um vetor que contém as dependências (outros módulos); bootstrap : é um vetor que contém os componentes que devem ser utilizados quando uma instância do AppModule passar a existir (isso, indicado ali no arquivo ./src/main.ts ). As coisas estão ficando mais conectadas agora e a figura a seguir ilustra isso. É isso! O elemento app-root é interpretado corretamente pelo Browser quando recebe o arquivo index.html porque ele está indicado como selector do AppComponent , que é o componente executado pelo AppModule , qué o root module. O selector do AppComponent é utilizado pelo Angular para saber onde apresentar o component no Template. O valor 'app-root' indica que o Angular deve procurar um elemento com esse nome. E é o que acontece. Viu? Não é mágica. É software! O que vem a seguir? Como agora você já deve estar entendendo melhor o funcionamento do Angular e o código- fonte do projeto, é hora de sujar as mãos =) No capítulo a seguir você vai implementar o requisito: ver as disciplinas. Criando o projeto 27 Depurando código no browser [info] Objetivos do capítulo: demonstrar como utilizar o recurso de depuração de código do Browser Branch: iniciando Devido ao processo de compilação/tradução do código-fonte para que o browser execute o software os recursos de inspeção do código-fonte/elementos apresenta o que o browser interpreta, ou seja, o resultado do processo de compilação/tradução. A figura a seguir ilustra a tela de um browser com o painel das ferramentas de desenvolvedor. Enquanto isso é útil para o browser, não permite que o desenvolver possa acompanhar a execução de partes do software observando o código-fonte original. Entretanto, a mesma ferramenta dá acesso à importante funcionalidade de depuração de código. Para isso, basta acessar o painel Sources e, na aba Network, à esquerda, acessar o nó webpack:// e seu filho que corresponde ao caminho do projeto do software no ambiente local (ex: D:/developer/angular-escola ), como mostra a figura a seguir. Depurando código no browser 28 https://github.com/jacksongomesbr/angular-escola/tree/iniciando A figura demonstra que o arquivo /src/app/app.component.ts está selecionado. Do lado direito da lista dos arquivos a tela apresenta o código-fonte original do arquivo. No painel que mostra o código-fonte há um breakpoint (ponto de parada) na linha 9. Isso significa que a execução do software no browser terá uma pausa quando da sua execução. Ainda mais à direita da tela há uma barra de ferramentas que permitem controlar a depuração: Os botões representam, nesta ordem: pausar/continuar a execução do código executar a próxima linha e não entrar no código da função/método executar a próxima linha e entrar no código da função/método executar a próxima linha e sair do código da função/método desativar os breakpoints pausar em exceções A figura a seguir ilustra a tela durante o processo de depuração. Depurando código no browser 29 Por fim, no painel mais à direita é possível acessar funcionalidades como inspecionar variáveis ou expressões (Watch), ver a pilha de chamadas (Call stack) e inspecionar as variáveis do escopo (Scope). Depurando código no browser 30 Iniciando com teste de software [info] Objetivos do capítulo: apresentar o conceito de teste de software demonstrar como escrever testes com o Angular utilizando Karma e Jasmine Branch: iniciando Teste de software é uma área da Engenharia de Software que envolve um conjunto de práticas para garantir a qualidade de software conforme requisitos e expectativas dos interessados, clientes ou desenvolvedores. Durante esse processo há geração de informação e conhecimento que permite entender o comportamento do software em situações simuladas ou com dados fictícios. Uma forma de avaliar a qualidade do software utilizando testede software é utilizar mecanismos que permitam identificar se o software está operando como esperado, fazendo o que deveria fazer. Ao criar o projeto com o Angular CLI, junto com o componente AppComponent também é criado o arquivo src/app/app.component.spec.ts . Esse arquivo TypeScript descreve testes utilizando Jasmine. Por definição Jasmine é um framework de teste de software que segue a técnica BDD (Behaviour Driven Development). Embora o Jasmine possa ser utilizado diretamente para testar JavaScript o Angular utiliza duas outras tecnologias: Karma, uma ferramenta utilizada para executar os testes escritos em Jasmine (por definição, Karma é um test runner); e Protractor, um framework para testes end-to-end (e2e). Então, um trecho do arquivo src/app/app.component.spec.ts : Iniciando com teste de software 31 https://github.com/jacksongomesbr/angular-escola/tree/iniciando https://jasmine.github.io/index.html https://karma-runner.github.io/1.0/index.html http://www.protractortest.org import { TestBed, async } from '@angular/core/testing'; import { AppComponent } from './app.component'; describe('AppComponent', () => { ... })); it('should create the app', async(() => { ... })); it(`should have as title 'Angular'`, async(() => { ... })); it('should render title in a h1 tag', async(() => { ... })); }); Testes escritos em Jasmine são chamados de specs e, por padrão, precisam estar em arquivos com nomes que terminem em .spec.ts (isso está definido no arquivo src/tsconfig.spec.json ). Depois das primeiras linhas com import há a primeira utilização de um recurso do Jasmine: a chamada do método describe() . Esse método cria uma suíte de testes (um grupo de testes). Os parâmetros para o método são: o nome da suíte de testes; e uma função que define suítes internas e specs. No exemplo, o nome da suíte é 'AppComponent' e a função que define as suítes internas e as specs está definindo três testes. As specs (testes) são definidas por meio de uma chamada ao método it(). Esse método cria uma spec. Os parâmetros do método são: o nome da spec (na prática, uma descrição do que a spec está testando); uma função que descreve o código do teste; e um número que representa o tempo limite de execução do teste (timeout). No caso do exemplo há três specs: should create the app (deveria criar o app -- ou o app deveria ser criado) should have title as 'Angular' (deveria ter o título como 'Angular' -- ou o título deveria ser 'Angular') should render title in a h1 tag (deveria renderizar o título em uma tag h1 -- ou o título deveria estar em uma tag h1) Iniciando com teste de software 32 Percebeu o "should" (deveria) no nome de cada spec? Isso faz parte da filosofia BDD que indica que cada spec checa se as expectativas sobre o teste são válidas (passam) ou não (falham). Antes de ver mais detalhes, vamos testar o software. Execute o comando: npm test Esse comando executa o script "test" definido no arquivo package.json . Na prática, ele executa o comando ng test . Dentro de instantes você deveria ver uma janela do browser mostrando algo semelhante ao da figura a seguir. Figura 2.6.1 - Janela do browser demonstrando o resultado dos testes iniciais Note que a URL do browser é http://localhost:9876 . Essa é a URL padrão que apresenta os resultados do teste do software. A tela apresenta várias informações importantes: versão do sistema operacional (Windows 10) Iniciando com teste de software 33 versão do browser (Chrome 63) versão do Karma (1.7.1) versão do Jasmine (2.6.4) tempo total de execução dos testes (0.187s) quantidade de specs (3) quantidade de falhas é zero (0), o que indica que todos os testes passaram (ou que não houve falha nos testes) Além disso a tela apresenta o próprio componente ( AppComponent ) em execução. Ainda, também há informações no prompt. Você deveria ver algo como o seguinte: 15 12 2017 01:56:31.937:WARN [karma]: No captured browser, open http://localhost:9876/ 15 12 2017 01:56:32.381:INFO [Chrome 63.0.3239 (Windows 10 0.0.0)]: Connected on socke t ... Chrome 63.0.3239 (Windows 10 0.0.0): Executed 0 of 3 SUCCESS (0 secs / 0 secs) Chrome 63.0.3239 (Windows 10 0.0.0): Executed 1 of 3 SUCCESS (0 secs / 0.101 secs) Chrome 63.0.3239 (Windows 10 0.0.0): Executed 2 of 3 SUCCESS (0 secs / 0.143 secs) Chrome 63.0.3239 (Windows 10 0.0.0): Executed 3 of 3 SUCCESS (0 secs / 0.184 secs) Chrome 63.0.3239 (Windows 10 0.0.0): Executed 3 of 3 SUCCESS (0.092 secs / 0.184 secs) Essas informações indicam dados adicionais: o primeiro teste executou em 0.101s o segundo teste executou em 0.143 - 0.101 = 0.042s o terceiro teste executou em 0.184 - 0.143 = 0.041s Ok, agora vamos a detalhes dos testes mas, antes de ver cada teste individualmente, o segundo parâmetro do método describe() chama o método beforeEach() , que é utilizada para realizar uma espécie de setup dos testes, executando um código antes deles: import { TestBed, async } from '@angular/core/testing'; import { AppComponent } from './app.component'; describe('AppComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ AppComponent ], }).compileComponents(); })); ... A classe TestBed é um utilitário do Angular para testes de software. Seu método configureTestingModule() cria um módulo específico para executar os testes que serão descritos a seguir. Pense nisso como um módulo mais enxuto que os utilizados no restante Iniciando com teste de software 34 do software, voltando apenas para esses testes. Por isso você deveria entender que o parâmetro fornecido para o método é muito similar aos metadados de um módulo e, nesse caso, indicam os componentes do módulo (atributo declarations ). Portanto, o setup permite que cada teste utilize o componente AppComponent . Primeiro teste O trecho de código a seguir apresenta o primeiro teste. it('should create the app', async(() => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.debugElement.componentInstance; expect(app).toBeTruthy(); })); Esse primeiro teste verifica se foi possível criar uma instância do componente AppComponent , o que é feito por meio de uma sequência de passos: 1. criar uma instância de AppComponent : isso é feito por meio do método TestBed.createComponent() (armazenado na variável fixture ) que retorna uma instância de ComponentFixture , referência ao ambiente de execução dos testes. O ambiente de execução dos testes dá acesso ao DebugElement , que representa o elemento do DOM que representa o componente 2. acessar a instância do componente ( fixture.debugElement.componentInstance ), armazenada na variável app ; 3. criar uma expectativa para o teste Uma expectativa é criada por meio do método expect() . Uma expectativa representa o comportamento esperado do software: que app (uma instância do AppComponent ) não seja null . O parâmetro de expect() indica a expressão que será analisada, comparada, com outra expressão ou valor. Nesse caso a comparação é realizada por meio do método toBeTruthy() . Se a expectativa não for satisfeita o teste falhará. Isso seria o comportamento obtido, por exemplo, por causa de um erro de tempo de execução (runtime). Segundo teste O trecho de código a seguir apresenta o segundo teste. Iniciando com teste de software 35 it(`should have as title 'Angular'`, async(() => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.debugElement.componentInstance; expect(app.title).toEqual('Angular'); })); A sequência de passos é semelhante à do primeiro teste. A diferença está na expectativa: o teste deseja verificar se o atributo title da instância do AppComponent ( app ) tem valor igual a 'Angular' . Novamente, a expectativa é criada por meio do método expect() . Dessa vez a comparação entre o valor esperado ( app.title ) e o valor obtido ( 'Angular' ) é feita por meio do método toEqual(). Com isso aprendemos que é possível testar membros de um componente (atributos e métodos), o que é muito valioso. Terceiro teste O trecho de código a seguir apresenta o terceiro teste. it('should render title in a h1 tag', async(() => { const fixture = TestBed.createComponent(AppComponent); fixture.detectChanges(); const compiled = fixture.debugElement.nativeElement; expect(compiled.querySelector('h1').textContent).toContain('Welcome to Angular!'); })); Em relação aos testes anteriores a sequência de passos inclui uma chamada ao método ComponentFixture.detectChanges() . Você deveria aprender, nos capítulos seguintes, que o Angular utiliza, muito, um recurso chamado data binding. Esse recurso faz com que o valor do atributo title seja apresentado no Template. Para checar se esse é o comportamento obtido é necessário, primeiro, identificar que ocorreu data binding. Uma forma de fazer isso é usando o método detectChanges() . Outra diferença é que o teste lida com o DOM depois de ter sido transformado pelo Angular, ou seja, na prática, em como o usuário está vendo o componente no momento. Por causa disso não é utilizada uma instância do componente, mas uma referência ao DOM compilado (o elemento DOM na raiz do componente, na verdade). Isso é feito por meio de fixture.debugElement.nativeElement (armazenado na variável compiled ). Por fim, a expectativa é um pouco mais complexa, pois envolve lidar com o DOM. Para isso ocorre o seguinte: 1. encontrar o elemento h1 no DOM: isso é feito por meio do método querySelector() ; Iniciando com teste de software 36 2. comparar o conteúdo textual do elemento h1 com a string Welcome to Angular! : isso é feito por meio de uma expectativa sobre o valor do atributo textConent do objeto que representa o elemento h1 conter a string desejada, comparação implementada pelo método toContain() . You shall not pass! Ou provocando uma falha nos testes Suponha que você modifique o valor do atributo title do AppComponent para 'Web' . O que os testes diriam? Se os testes ainda estiverem em execução, apenas observe a janela do browser que mostra o resultado dos testes (ilustrada pela figura a seguir). Iniciando com teste de software 37 A saída dos testes mostra claramente que algo errado aconteceu: dos três testes, dois falharam. A saída indica cada teste que falhou (detalhe em vermelho): o segundo teste falhou porque a string 'Web' não igual a 'Angular' ; e o terceiro teste falhou porque a string 'Welcome to Web!' não contém 'Welcome to Angular!' . Percebeu que os testes são executados novamente sempre que há uma mudança no código do software? Esse é o comportamento padrão, mas se você precisar desligá-lo precisará de uma mudança no arquivo package.json , modificando o script de teste de ( ng test ): ... "scripts": { ... "test": "ng test", ... }, ... para ( ng test --single-run ): ... "scripts": { ... "test": "ng test --single-run", ... }, ... O problema disso é que, ao executar o comando npm test você verá a janela do browser aparecer e sumir logo depois de alguns instantes. Assim, precisará lidar com a saída do comando no prompt para identificar o que ocorreu (testes passaram ou falharam). Iniciando com teste de software 38 [info] Resumo do capítulo: testes de software permitem avaliar se o comportamento do software está como o esperado Jasmine é um framework utilizado para escrever testes usando a técnica BDD Karma e Protractor são ferramentas do Angular para executar testes o comando npm test é usado para executar testes testes são definidos dentro de suítes uma suíte é criada usando o método describe() um teste é criado usando o método it() um teste pode validar diversos comportamentos do software, mas cada comportamento é validado usando o método expect() , que compara um valor esperado com um valor obtido testes do Angular envolvem verificar membros do componente (atributos e método) e o DOM compilado, que é visualizado pelo cliente no browser Iniciando com teste de software 39 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 e ngIf 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. Apresentando a lista de disciplinas 40 https://github.com/jacksongomesbr/angular-escola/tree/livro-listando-disciplinas https://github.com/jacksongomesbr/angular-escola/tree/livro-listando-disciplinas-objetos https://github.com/jacksongomesbr/angular-escola/tree/livro-listando-disciplinas-classes https://github.com/jacksongomesbr/angular-escola/tree/livro-listando-disciplinas-interacao 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 ler NgForOf 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 de disciplina 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. Apresentando a lista de disciplinas 41 Figura 2.7.1 - Apresentando a lista das disciplinas 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. Apresentando a lista de disciplinas 42 ... 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: Apresentando a lista de disciplinas 43 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: Apresentando a lista de disciplinas 44 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 seja pointer (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 Apresentando a lista de disciplinas 45 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: Apresentando a lista de disciplinas 46 <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 objetivonorteador 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 possui class="selecionado" o elemento p que mostra a descrição da disciplina está presente no HTML para os demais elementos li : o elemento p que mostra o nome da disciplina não possui o atributo class o elemento p que mostra a descrição da disciplina não existe no HTML Apresentando a lista de disciplinas 47 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. Figura 2.7.2 - Execução do software no browser, listando disciplinas e interagindo com o usuário Pronto, esse capítulo mostrou a utilização de vários recursos do Angular para implementar o requisito "Apresentar a lista das disciplinas". Apresentando a lista de disciplinas 48 [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 Apresentando a lista de disciplinas 49 Cadastrando uma disciplina [info] Objetivos do capítulo: demonstrar o uso de formulários e two-way data binding implementar o requisito "Cadastrar disciplina" Branches: livro-cadastrando-disciplina Neste capítulo vamos começar pelo final: primeiro, veja a figura que ilustra como o software estará ao final do capítulo. Figura 2.8.1 - Tela do software apresentando a lista das disciplinas e o formulário de cadastro A tela do aplicativo tem duas funções: na primeira parte, apresenta a lista das disciplinas; na segunda parte, apresenta um formulário que permite o cadastro de uma disciplina. A primeira parte você já conhece do capítulo anterior. Agora, veremos os detalhes para o formulário de cadastro. Cadastrando uma disciplina 50 https://github.com/jacksongomesbr/angular-escola/tree/livro-cadastrando-disciplina Antes de prosseguir, um fator importante: o projeto original criado pelo Angular CLI não suporta formulários. Isso mesmo que você leu. O Angular trata formulários de três formas diferentes, uma delas, a que vamos utilizar, chamada de template driven forms (algo como formuários orientados a templates). Para habilitar o suporte a formulários é necessário importar o módulo FormsModule no root module: import {BrowserModule} from '@angular/platform-browser'; import {NgModule} from '@angular/core'; import {FormsModule} from '@angular/forms'; import {AppComponent} from './app.component'; @NgModule({ ... imports: [ BrowserModule, FormsModule ], ... }) export class AppModule { } O módulo FormsModule está definido no pacote @angular/forms e é importado no AppModule por meio do atributo imports dos metadados da classe. A partir de então a utilização de formulários do tipo template-driven forms está garantida. O formulário de cadastro tem dois campos: nome e descrição. O comportamento esperado pelo usuário é preencher os valores dos campos, clicar no botão "Salvar" e ver a lista de disciplinas atualizada, contendo a disciplina que acabou de ser cadastrada. Para fazer isso, primeiro vamos ao Controller do AppComponent : Cadastrando uma disciplina 51 ... export class AppComponent { selecionado = null; nome = null; descricao = null; disciplinas = [ new Disciplina('Língua Portuguesa', 'O objetivo norteador da BNCC de ...'), ... ]; salvar() { const d = new Disciplina(this.nome, this.descricao); this.disciplinas.push(d); this.nome = null; this.descricao = null; } } São adicionados dois atributos e um método: nome : está ligado ao campo nome do formulário por meio de data binding descricao : está ligado ao campo descricao do formulário por meio de data binding salvar() : é executado quando o usuário clicar no botão "Salvar" Os dois atributos são inciados com null . O método salvar() cria uma instância da classe Disciplina usando os atributos nome e descrição , respectivamente. Posteriormente, adiciona esse objeto ao vetor disciplinas por meio do método push() . Por fim, atribui o valor null novamente aos atributos nome e descricao . Mas... o que significa um atributo do Controller estar ligado a um campo do formulário via data binding? Você já sabe que data binding é o recurso do Angular que faz com que o valor de um atributo do Controller seja apresentado no Template por meio de interpolação. E quanto ao formulário? Vamos ao Template: Cadastrando uma disciplina 52 <h1>Disciplinas</h1> ... <h2>Cadastrar</h2> <p>Use este formulário para cadastrar uma disciplina.</p> <form #cadastro="ngForm" (submit)="salvar()"> <div> <label for="nome">Nome</label><br> <input type="text" id="nome" name="nome" [(ngModel)]="nome" required> </div> <div> <label for="descricao">Descrição</label><br> <textarea id="descricao" name="descricao" [(ngModel)]="descricao" cols="70" rows="5"> </textarea> </div> <button type="submit" class="btn btn-primary" [disabled]="!cadastro.valid"> Salvar </button> </form> Como a parte da lista das disciplinas não muda muito, vamos omitir essa parte e apresentar o formulário, começando pela tag <form> . Em termos de HTML, a tag possui um atributo #cadastro , com valor ngForm . Esse recurso, na verdade, é uma sintaxe do Angular para criar uma variável (chamada cadastro ) vinculada ao formulário. Importante também destacar o evento submit . Você já entende a sintaxe (submit) , então o tratador do evento é uma chamada ao método salvar() . Na sequência são definidos os campos do formulário. A parte mais importante está nas tags input e textarea , principalmente o que, em termos de HTML, parece um atributo: [(ngModel)] . Você já sabe a sintaxe para eventos (entre parênteses) e já viu que o Angular consegue alterar um atributo do HTML por meio de colchetes. Essa sintaxe, que mistura as duas anteriores, é a sintaxe do recurso two-way data binding. No campo para o nome, [(ngModel)] tem o valor nome , para o campo descrição, tem o valor descricao . Ambos são atributos do Controller. Assim, o two-way data binding significa que o valor do atributo pode ser modificado por meio de uma alteração do campo de formulário correspondente, e vice-versa: se o valor do atributo nome for modificado no Controller, o Template será atualizado, mostrando esse valor no input se o valor do atributo nome for modificado no Template, por uma alteração no input , esse valor estará disponível no Controller Por isso o two-way: duas vias. A figura a seguir ilustra esse processo. Cadastrando uma disciplina 53 Figura 2.8.2 - Ilustração do two-way data binding, ligando formulário e Controller O formulário tem mais uma característica: a entrada do usuário está passando por validação. O elemento input para o camponome tem o atributo required , que o torna de preenchimento obrigatório. A partir de então há um comportamento muito interessante para o botão "Salvar": o elemento button possui um atributo disabled , que é modificado conforme a expressão !cadastro.valid . Lembra da variável de template #cadastro ? Aqui ela é usada como uma referência ao formulário. Portanto, se o formulário de cadastro estiver válido, o botão "Salvar" estará habilitado para clique. Caso contrário, estará desabilitado. Execute o software no browser para notar o comportamento citado no capítulo. [info] Resumo do capítulo: Implementamos o requisito "Cadastrar uma disciplina" Utilizamos o recurso two-way data binding Utilizamos formulário para receber entrada do usuário Cadastrando uma disciplina 54 Excluindo uma disciplina [info] Objetivos do capítulo: demonstrar a implementação do requisito "Excluir uma disciplina" Branch: livro-excluindo-disciplina Este é um capítulo mais simples. Basicamente os recursos utilizados já foram apresentados, então ele é mais curto. Primeiro, um trecho do Controller do AppComponent : ... export class AppComponent { selecionado = null; nome = null; descricao = null; disciplinas = [ new Disciplina('Língua Portuguesa', '...'), ... ]; salvar() { ... } excluir(disciplina) { if (confirm('Tem certeza que deseja excluir a disciplina "' + disciplina.nome + '"?')) { const i = this.disciplinas.indexOf(disciplina); this.disciplinas.splice(i, 1); } } } Com exceção do código omitido, que você já conheceu nos capítulos anteriores, o método excluir() recebe como parâmetro um objeto, chamado disciplina . Esse método exclui uma disciplina da lista, mas, antes, solicita uma confirmação do usuário (por isso está usando a função confirm() ). Se o usuário confirmar que deseja excluir a disciplina, então o código prossegue: encontra o índice da disciplina na lista usando o método indexOf() remove um elemento da lista usando o método splice() Agora, então, um trecho do código do Template: Excluindo uma disciplina 55 https://github.com/jacksongomesbr/angular-escola/tree/livro-excluindo-disciplina <h1>Disciplinas</h1> <hr> <ul> <li *ngFor="let disciplina of disciplinas"> {{disciplina.nome}} <button (click)="excluir(disciplina)"> Excluir </button> </li> </ul> <h2>Cadastrar</h2> <p>Use este formulário para cadastrar uma disciplina.</p> <form #cadastro="ngForm" (submit)="salvar()"> ... </form> A parte importante do template é o elemento button ao lado do nome da disciplina. Há um tratador do evento (click) que chama o método excluir() passando como parâmetro a disciplina atual da repetição do *ngFor . O resultado da execução do software no browser é ilustrado pela figura a seguir. Excluindo uma disciplina 56 Figura 2.9.1 - Execução do software no browser ilustrando a confirmação do usuário para excluir uma disciplina Esse capitulo é bem curto e não há muito o que resumir, mas é bom notar que os conceitos vistos nos capítulos anteriores continuam funcionando aqui, mesmo que em aplicações diferentes. Excluindo uma disciplina 57 Editando dados de uma disciplina [info] Objetivos do capítulo: demonstrar a implementação do requisito "Editar dados de uma disciplina" reforçar os conceitos anteriores demonstrar a implementação mais completa de um CRUD Branch: livro-crud-disciplina Como o capítulo anterior, esse capítulo não é muito extenso em termos de conceitos novos. Entretanto, ele reúne os capítulos anteriores para implementar um CRUD, que significa: Create (cadastrar) Retrieve (recuperar, listar, consultar) Update (atualizar, editar) Delete (excluir) A figura a seguir ilustra a interface do software como construída ao final do capítulo. Editando dados de uma disciplina 58 https://github.com/jacksongomesbr/angular-escola/tree/livro-crud-disciplina Figura 2.10.1 - Software com o CRUD de Disciplinas A interface continua com duas partes: uma apresentando uma lista e outra apresentando um formulário. Na lista, para cada item, há dois botões: "Excluir" e "Editar". Ao clicar no botão "Excluir" o software solicita uma confirmação do usuário. Ao clicar no botão "Editar" os dados da disciplina em questão são apresentados no formulário (nome e descrição). Nesse momento o formulário entra no modo de edição e: se o usuário clicar em "Salvar" os dados da disciplina serão atualizados se o usuário clicar em "Cancelar" qualquer alteração não será armazenada O modo padrão do formulário é o cadastro. Nesse sentido se o usuário preencher os campos (nome e descrição) e: clicar em "Salvar", os dados serão armazenados em uma nova disciplina, apresentando-a na lista clicar em "Cancelar", os dados não serão armazenados Vamos ao código-fonte. Primeiro, um trecho do Controller: Editando dados de uma disciplina 59 ... export class AppComponent { editando = null; nome = null; descricao = null; disciplinas = [ new Disciplina('Língua Portuguesa', 'O objetivo norteador da BNCC...'), ... ]; salvar() { if (this.editando) { this.editando.nome = this.nome; this.editando.descricao = this.descricao; } else { const d = new Disciplina(this.nome, this.descricao); this.disciplinas.push(d); } this.nome = null; this.descricao = null; this.editando = null; } excluir(disciplina) { if (this.editando == disciplina) { alert('Você não pode excluir uma disciplina que está editando'); } else { if (confirm('Tem certeza que deseja excluir a disciplina "' + disciplina.nome + '"?')) { const i = this.disciplinas.indexOf(disciplina); this.disciplinas.splice(i, 1); } } } editar(disciplina) { this.nome = disciplina.nome; this.descricao = disciplina.descricao; this.editando = disciplina; } cancelar() { this.nome = null; this.descricao = null; this.editando = null; } } As novidades são: adição do atributo editando : é inicializado com null e manterá uma referência para a Editando dados de uma disciplina 60 disciplina que está sendo editada (quando houver alguma) atualização no método salvar() : para tratar a situação do formulário (cadastrando ou editando disciplina) atualização no método excluir() : para tratar a situação em que o usuário quer excluir uma disciplina que está sendo editada adição do método editar() O método salvar() tem um novo comportamento: se o usuário estiver editando uma disciplina (verifica o valor do atributo editando ), então usa os dados do formulário e atualiza os dados da disciplina que está sendo editada. Caso contrário, continua com o comportamento anterior: usa os dados do formulário para criar uma instância de Disciplina e adiciona no vetor disciplinas . O método excluir() tem um novo comportamento: se o usuário estiver editando uma disciplina (verifica o valor do atributo editando), então informa o usuário que não é possível excluir a disciplina que está sendo editada. Caso contrário, continua com o comportamento normal: solicita uma confirmação do usuário e exclui a disciplina. O método editar() recebe como parâmetro um objeto que representa a disciplina a ser editada, atualiza os campos do formulário com os atributos de disciplina e faz com que o atributo editando receba disciplina para indicar que o formulário está no modo de edição (há uma disciplina sendo editada). O método cancelar() tem a função de reiniciar o formulário para os valores-padrão, atributo o valor null para os atributos nome , descricao e editando . Para concluir, o Template: Editando dados de uma disciplina 61 <h1>Disciplinas</h1> <hr> <ul> <li *ngFor="let disciplina of disciplinas"> {{disciplina.nome}} <button (click)="excluir(disciplina)"> Excluir </button> <button (click)="editar(disciplina)">
Compartilhar