├── .gitignore ├── README.md ├── SUMMARY.md ├── acessando-dados-de-uma-api-http.md ├── acessando-dados-externos-de-forma-assincrona.md ├── assets ├── acessando-dados-api-exemplo-subscribe-sucesso-erro.png ├── angular-inicial-app-component-template-model.png ├── angular-inicial-estrutura-appmodule-appcomponent-index.png ├── browser-com-ferramentas-desenvolvedor.png ├── browser-debug-barra-de-ferramentas.png ├── browser-depuracao-ocorrendo.png ├── browser-listando-disciplinas.png ├── depuracao-browser-sources-webpack-breakpoint.png ├── escola-estrutura-curricular-1-5.png ├── escola-estrutura-curricular-6-9.png ├── escola-estrutura-curricular-medio.png ├── escola-estrutura-ensino-infantil.png ├── feature-modules-navegacao.png ├── formularios-two-way-data-binding.png ├── httpclient-observer-subscribe-callback.png ├── ilustracao-procurando-rota.png ├── ilustracao-rotas-router-outlet-combinacao.png ├── ilustracao-uri-url.png ├── images │ ├── README.md │ └── uml │ │ └── README.md ├── interacao-componentes-app-listadedisciplinas.png ├── intermediario-componente-listadedisciplinas-browser.png ├── primeiro-teste-browser-inicial.png ├── primeiro-teste-com-falha.png ├── projeto-original-appcomponent.png ├── rotas-estrutura-classes.png ├── software-arquitetura-modulos-api.png ├── software-cadastrando-disciplina-browser.png ├── software-componentes-app-listadedisciplinas.png ├── software-crud-disciplinas-com-tables-browser.png ├── software-dados-api-http-demonstracao-erro.png ├── software-diagrama-de-classes-servicos.png ├── software-excluindo-disciplina-browser.png ├── software-inicial-crud-disciplinas.png ├── software-interface-bootstrap-browser.png ├── software-listando-disciplinas-interacao-browser.png ├── software-modelo-crud-template-component.png ├── software-versao-inicial-browser.png └── visao-geral-da-arquitetura-do-angular.png ├── book.json ├── cadastrando-uma-disciplina.md ├── conceitos-iniciais ├── angular-cli.md ├── apresentando-a-lista-de-disciplinas.md ├── criando-o-projeto.md ├── criando-o-projeto │ └── depurando-codigo-no-browser.md ├── depurando-codigo-no-browser.md ├── editando-dados-de-uma-disciplina.md ├── iniciando-com-o-angular.md ├── melhorando-a-interface-com-bootstrap.md └── primeiro-teste.md ├── excluindo-uma-disciplina.md ├── feature-modules.md ├── interacao-entre-componentes.md ├── nivel-intermediario └── README.md ├── rotas.md ├── servicos.md ├── software-de-gerenciamento-escolar.md └── testando-a-funcionalidade-de-listar-disciplinas.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Node rules: 2 | ## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 3 | .grunt 4 | 5 | ## Dependency directory 6 | ## Commenting this out is preferred by some people, see 7 | ## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git 8 | node_modules 9 | 10 | # Book build output 11 | _book 12 | 13 | # eBook build output 14 | *.epub 15 | *.mobi 16 | *.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Desenvolvimento web front-end com Angular 2 | 3 | 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. 4 | 5 | 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. 6 | 7 | Parte do conteúdo do livro é baseada na documentação oficial do Angular, disponível em [https://angular.io](https://angular.io). 8 | 9 | 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**. 10 | 11 | ## Conhecimentos desejáveis 12 | 13 | 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. 14 | 15 | Para aprender mais sobre essas tecnologias recomendo essas fontes: 16 | 17 | * **TypeScript**: [Documentação oficial do TypeScript - Microsoft](https://www.typescriptlang.org/docs/home.html), [TypeScript Deep Dive](https://www.gitbook.com/book/basarat/typescript/details) 18 | * **HTML, CSS e JavaScript:** [W3Schools](https://www.w3schools.com/) 19 | * **Boostrap**: [Documentação oficial do Bootstrap](http://getbootstrap.com/docs/4.0/getting-started/introduction/) 20 | 21 | ## Código-fonte 22 | 23 | O software _Angular Escola_, que serve como apoio para este livro está disponível gratuitamente no Github: [https://github.com/jacksongomesbr/angular-escola](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. 24 | 25 | ## Dependências 26 | 27 | Para utilizar o código-fonte do livro e desenvolver em Angular você precisa, antes, montar seu ambiente de desenvolvimento com algumas ferramentas: 28 | 29 | 1. **Git**: disponível em [https://git-scm.com/](https://git-scm.com/) 30 | 2. **Node.js**: disponível em [https://nodejs.org](https://nodejs.org) representa um ambiente de execução do JavaScript fora do browser e também inclui o **npm**, um gerenciador de pacotes 31 | 3. **Editor de textos ou IDE**: atualmente há muitas opções, mas destaco estas: 32 | 1. **VisualStudioCode**: disponível em [https://code.visualstudio.com/](https://code.visualstudio.com/) 33 | 2. **Nodepad++**: disponível em [https://notepad-plus-plus.org/](https://notepad-plus-plus.org/) 34 | 3. **PHPStorm **ou **WebStorm**: disponíveis em [https://www.jetbrains.com](https://www.jetbrains.com) 35 | 36 | ## Visão geral 37 | 38 | A tabela a seguir apresenta uma visão geral dos conceitos apresentados ou discutidos em cada capítulo do livro. 39 | 40 | | Capítulo | Conteúdos | 41 | | :--- | :--- | 42 | | [Iniciando com o Angular](/conceitos-iniciais/iniciando-com-o-angular.md) | Arquitetura do Angular; Elementos da Arquitetura do Angular \(ex.: módulos e componentes\) | 43 | | [Angular CLI](/conceitos-iniciais/angular-cli.md) | Ferramenta Angular CLI; Comandos | 44 | | [Criando o projeto](/conceitos-iniciais/criando-o-projeto.md) | Ferramenta Angular CLI; Comando new | 45 | | [Apresentando a lista de disciplinas](/conceitos-iniciais/apresentando-a-lista-de-disciplinas.md) | Data binding; Diretivas; Eventos | 46 | | [Cadastrando uma disciplina](/cadastrando-uma-disciplina.md) | Formulários; Two-way Data binding; Eventos | 47 | | [Excluindo uma disciplina](/excluindo-uma-disciplina.md) | Two-way Data Binding; Eventos | 48 | | [Editando dados de uma disciplina](/conceitos-iniciais/editando-dados-de-uma-disciplina.md) | Formulários; Two-way Data Binding; Eventos | 49 | | [Melhorando a interface gráfica do CRUD de disciplinas](/conceitos-iniciais/melhorando-a-interface-com-bootstrap.md) | Formulários; Data Binding; Eventos; Bootstrap; Font Awesome | 50 | | [Interação entre componentes](/interacao-entre-componentes.md) | Criação de componentes; Input e Output para interação entre componentes | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [Introdução](README.md) 4 | 5 | ## Conceitos iniciais 6 | 7 | * [Iniciando com o Angular](conceitos-iniciais/iniciando-com-o-angular.md) 8 | * [Angular CLI](conceitos-iniciais/angular-cli.md) 9 | * [Software de gerenciamento escolar](software-de-gerenciamento-escolar.md) 10 | * [Criando o projeto](conceitos-iniciais/criando-o-projeto.md) 11 | * [Depurando código no browser](conceitos-iniciais/depurando-codigo-no-browser.md) 12 | * [Iniciando com teste de software](conceitos-iniciais/primeiro-teste.md) 13 | * [Apresentando a lista de disciplinas](conceitos-iniciais/apresentando-a-lista-de-disciplinas.md) 14 | * [Cadastrando uma disciplina](cadastrando-uma-disciplina.md) 15 | * [Excluindo uma disciplina](excluindo-uma-disciplina.md) 16 | * [Editando dados de uma disciplina](conceitos-iniciais/editando-dados-de-uma-disciplina.md) 17 | * Depurando código no browser 18 | 19 | ## Nível intermediário 20 | 21 | * [Interação entre componentes](interacao-entre-componentes.md) 22 | * [Serviços](servicos.md) 23 | * [Acessando dados externos de forma assíncrona](acessando-dados-externos-de-forma-assincrona.md) 24 | * [Acessando dados de uma API HTTP](acessando-dados-de-uma-api-http.md) 25 | * [Rotas](rotas.md) 26 | * [Feature-modules](feature-modules.md) 27 | 28 | -------------------------------------------------------------------------------- /acessando-dados-de-uma-api-http.md: -------------------------------------------------------------------------------- 1 | # Acessando dados de uma API HTTP 2 | 3 | > **\[info\] Objetivos do capítulo:** 4 | > 5 | > * reforçar o conceito de requisição assícrona utilizando a classe HttpClient 6 | > * demonstrar a utilização da classe HttpClient para acessar dados de uma API HTTP REST 7 | > * demonstrar como lidar com situações de sucesso e erro ao lidar com requisições assíncronas 8 | > 9 | > **Branch**: [livro-dados-externos-api-http-rest](https://github.com/jacksongomesbr/angular-escola/tree/livro-dados-externos-api-http-rest) 10 | 11 | O capítulo [Acessando dados externos de forma assíncrona](/acessando-dados-externos-de-forma-assincrona.md) apresentou como utilizar recursos do Angular para acessar dados de um arquivo JSON. Entretanto, a limitação daquela abordagem, embora seja suficiente para várias situações, precisa de uma alternativa mais completa, que permita a persistência dos dados. Uma solução para essa questão é utilizar uma API HTTP. Vamos utilizar uma API HTTP REST disponível no repositório [angular-escola-api](https://github.com/jacksongomesbr/angular-escola-api). Este capítulo não apresenta detalhes de como fazer essa API funcionar, mas vez ou outras algumas informações serão apresentadas. 12 | 13 | ## Iniciando o servidor web da API 14 | 15 | Depois de clonar o repositório da API execute o comando: 16 | 17 | ``` 18 | npm start 19 | ``` 20 | 21 | Após alguns instantes o servidor estará disponível para aceitar requisições. Se quiser testar a API diretamente pelo browser acesse o endereço da API, indicado na mensagem de saída do comando anterior. 22 | 23 | ## Modificando o DisciplinasService 24 | 25 | Como você já sabe o serviço DisciplinasService do capítulo anterior está fazendo requisições para o servidor web de desenvolvimento em busca do arquivo JSON que contém a lista das disciplinas. Para consultar a API a primeira alteração está na estrutura do serviço: 26 | 27 | * remover todos os atributos 28 | * adicionar o atributo `API_URL` com o valor `"http://localhost:3000"` \(que é o endereço padrão do servidor web da API\) 29 | * remover o método `carregarDados()` 30 | * alterar o código dos métodos `todas()`, `salvar()`, `excluir()` e `encontrar()`. 31 | 32 | Assim, o serviço DisciplinasService fica dessa maneira: 33 | 34 | ``` 35 | ... 36 | @Injectable() 37 | export class DisciplinasService { 38 | API_URL = 'http://localhost:3000'; 39 | 40 | constructor(private http: HttpClient) { } 41 | 42 | todas() { 43 | return this.http.get(this.API_URL + '/disciplinas'); 44 | } 45 | 46 | salvar(id: number, nome: string, descricao: string) { 47 | const disciplina = {nome: nome, descricao: descricao}; 48 | if (id) { 49 | return this.http.patch(this.API_URL + '/disciplinas/' + id, disciplina); 50 | } else { 51 | return this.http.post(this.API_URL + '/disciplinas', disciplina); 52 | } 53 | } 54 | 55 | excluir(disciplina: number | Disciplina) { 56 | let id; 57 | if (typeof(disciplina) === 'number') { 58 | id = disciplina; 59 | } else { 60 | id = disciplina.id; 61 | } 62 | return this.http.delete(this.API_URL + '/disciplinas/' + id); 63 | } 64 | 65 | encontrar(arg: number) { 66 | return this.http.get(this.API_URL + '/disciplinas/' + arg); 67 | } 68 | } 69 | ``` 70 | 71 | O método `todas()` utiliza o método `get()` do `HttpClient` para realizar uma requisição **GET** para a URL `/disciplinas` \(a partir da URL da API\) para retornar a lista das disciplinas. A API retorna um array de objetos com os atributos: `id`, `codigo`, `nome` e `descricao`. Da mesma forma como antes \(no antigo método `carregarDados()`\) os dados são convertidos para objetos automaticamente quando a requisição HTTP for realizada -- lembre-se que é preciso chamar o método `subscribe()` para isso. 72 | 73 | O método `salvar()` recebe os dados de uma disciplina como parâmetro \(`id`, `nome`, `descricao`\), define um objeto chamado `disciplina` com os valores dos parâmetros e checa o valor do parâmetro `id` para saber se é necessário: 74 | 75 | * **alterar**: nesse caso o `id` possui algum valor e é feita uma requisição **PATCH **para a URL da disciplina na API, por exemplo `/disciplinas/1`, por meio do método `patch()` da classe `HttpClient`; ou 76 | * **cadastrar**: nesse caso o `id` não possui valor \(ex: `null`\) e é feita uma requisição **POST **para a URL `/disciplinas` da API por meio do método `post()` da classe `HttpClient`. 77 | 78 | Perceba que nos dois casos o segundo parâmetro da chamada dos métodos `patch()` e `post()`é o objeto `disciplina`. Isso é necessário porque a API espera que os dados estejam em formato JSON, o que também é feito automaticamente pelo Angular \(converte o objeto `disciplina` para sua representação em JSON ao enviar os dados para a API\). Além disso, da mesma forma como acontece com o método `get()` da classe `HttpClient`, a requisição HTTP só acontece, efetivamente, quando é chamado o método `subscribe()`. 79 | 80 | O método `excluir()` recebe o parâmetro `disciplina`, que pode ser um número \(o identificador da disciplina\) ou o objeto que representa a disciplina. Com base no tipo do parâmetro o código obtém o identificador da disciplina e o utiliza para fazer uma requisição **DELETE** para a URL da API que representa a disciplina \(ex: `/disciplinas/1`\). Isso é feito por meio de uma chamada para o método `delete()` da classe `HttpClient`. 81 | 82 | Por último, o método `encontrar()` recebe o identificador da disciplina como parâmetro e faz uma requisição **GET** para a URL da API que representa a disciplina por meio do método `get()` da classe `HttpClient`. 83 | 84 | ## Modificando o AppComponent 85 | 86 | As alterações mais expressivas acontecem no Controller do `AppComponent` para usar o serviço `DisciplinasService`. Primeiro, o `constructor()` chama o novo método `atualizarLista()`: 87 | 88 | ```typescript 89 | constructor(private disciplinasService: DisciplinasService) { 90 | this.atualizarLista(); 91 | } 92 | 93 | atualizarLista() { 94 | this.disciplinasService.todas() 95 | .subscribe(disciplinas => { 96 | this.disciplinas = disciplinas; 97 | this.status_lista = true; 98 | }, () => this.status_lista = false); 99 | } 100 | ``` 101 | 102 | O método `atualizarLista()` chama o método `todas()` do `DisciplinasService`. Perceba que, porque o retorno do método é um `Observable`, é possível chamar o método `subscribe()` logo em seguida. Como de costume, uma figura para explicar isso em partes: 103 | 104 | ![Ilustração da estrutura de callback de sucesso e erro](/assets/acessando-dados-api-exemplo-subscribe-sucesso-erro.png) 105 | 106 | A figura destaca, na cor verde, o código da callback de sucesso. Lembre-se que a callback de sucesso executa se a requisição para a API for bem sucedida \(código de retorno igual 200\). Nesse caso a **arrow function** tem o parâmetro disciplinas e o corpo da função faz: 107 | 108 | * atribui `disciplinas` para `this.disciplinas` \(a lista das disciplinas, que o Template utiliza\) 109 | * atribui `true` para `this.status_lista` \(um valor de controle utilizado no Template\) 110 | 111 | Ainda na figura, a cor vermelha destaca a callback de erro. A callback de erro executa se a requisição para a API não for bem sucedida \(código de retorno diferente de 200, como 404 ou 500\). A **arrow function** não tem parâmetro e seu corpo atribui `false` para `this.status_lista`. 112 | 113 | O método `salvar()` chama o método `salvar()` do `DisciplinasService`: 114 | 115 | ``` 116 | salvar() { 117 | this.disciplinasService.salvar(this.editando.id, 118 | this.editando.nome, this.editando.descricao) 119 | .subscribe(disciplina => { 120 | this.redefinir(); 121 | this.salvar_ok = true; 122 | this.atualizarLista(); 123 | }, 124 | () => { 125 | this.redefinir(); 126 | this.salvar_erro = true; 127 | } 128 | ); 129 | } 130 | ``` 131 | 132 | Nesse caso a callback de sucesso, que indica que os dados foram salvos na API, faz com que a tela apresente uma mensagem de sucesso e a lista de disciplinas é atualizada \(para mostrar os novos dados das disciplinas\). Note também que a callback recebe o parâmetro `disciplina`, que não é utilizado, mas que é retornado pela API contendo os dados do objeto. No caso da API que estamos utilizando, se for feita uma requisição POST o objeto retornado pela API contém um atributo id cujo valor é gerado automaticamente, de forma incremental \(lembra que já precisamos controlar isso manualmente?\). No caso da callback de erro, o código faz com que a tela apresente uma mensagem de erro. 133 | 134 | Os demais métodos, `excluir()` e `editar()`, utilizam, respectivamente, os métodos `excluir()` e `encontrar()` da classe `DisciplinasService`. Fica como exercício interpretar o código. 135 | 136 | A execução do software no browser não é muito diferente do capítulo anterior. Entretanto, como estamos utilizando uma API HTTP REST, é importante tomar certos cuidados. Por exemplo, se, por algum motivo, não for possível acessar a API, é interessante apresentar uma mensagem de erro na tela. A figura a seguir ilustra essa situação. 137 | 138 | ![Execução do software no browser demonstrando mensagem de erro de conexão com a API HTTP REST](/assets/software-dados-api-http-demonstracao-erro.png) 139 | 140 | Caso contrário, se a API estiver acessível e tudo estiver ocorrendo como esperado, a interface continua mostrando os dados e permitindo interação com o usuário normalmente. 141 | 142 | Para finalizar o capítulo, um diagrama de sequência demonstrando a interação entre `AppComponent`, `DisciplinasService` e o **Servidor HTTP da API** quando é executado o método `atualizarLista()` do `AppComponent`. 143 | 144 | ![Diagrama de sequência demonstrando a interação entre AppComponent, DisciplinasService e Servidor HTTP quando é chamado o método atualizarLista() do AppComponent](https://www.planttext.com/plantuml/img/fPFVIiCm5CRlynJdRYtCMcJUz48svj0x1NSfZ3Gvpc3QXFmPnTVn4No4lPWdLMQrJ1GBeIJvlY-_SqBcFd0NOgFPmjgbQQnfiGrmSW6NoWjbjMgvlqEtKm8h24Pod-Lil7VCyHY2lM-BBOPiSYe_1PDZ8KEC2cvgJrkyrZZYEoOiVAozSAh6J72jQowUDZuAzDvCuR22pfbybDbpIECsr-lrRGLNgpKCeLbH5B3OHkua1uV1kDQ0DE0_R91if65S1rZkNwNQ6ZWhImRqVSaU5w0LtGH8XE5voVTptTYXY6JyNYf3xH7wW26CL0_eo8Zf92A33BiAPkLi2kTbcVVwNy3k-BCo1_4V2LFhUs-FkA9PWX4ax_OlT4TH1ySjfo8Y1AGSwasZtf4TUcjIexAW6ZGnvF-dQ2LBWx4v_UWbl040) 145 | 146 | O diagrama de sequência demonstra as interações desde a chamada do método `todas\(\)` do `DisciplinasService` até o tratamento do retorno do Servidor HTTP \(ou erro\). 147 | 148 | > **\[info\] Resumo do capítulo:** 149 | > 150 | > * A classe `HttpClient` é muito útil para acessar dados via requisições HTTP 151 | > * A classe `HttpClient` pode ser utilizada para comunicação com uma API HTTP REST 152 | > * A utilização de serviços para organização do código é um recurso para melhoria da arquitetura do software 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /acessando-dados-externos-de-forma-assincrona.md: -------------------------------------------------------------------------------- 1 | # Acessando dados externos de forma assíncrona 2 | 3 | > **\[info\] Objetivos do capítulo:** 4 | > 5 | > * demonstrar a representação de dados em arquivos JSON 6 | > * demonstrar o uso do HttpClient para acessar dados externos \(arquivo JSON\) 7 | > * apresentar o padrão Observer e explicar seu funcionamento 8 | > * apresentar a comunicação entre componentes do software usando diagrama de sequência da UML 9 | > 10 | > **Branch**: [livro-dados-externos-json](https://github.com/jacksongomesbr/angular-escola/tree/livro-dados-externos-json) 11 | 12 | Até o capítulo anterior \([Serviços](/servicos.md)\) a estrutura do software já começa a ficar mais modular. Para prosseguir nessa direção uma modificação importante é trabalhar com fontes de dados. Por enquanto, vamos utilizar um mecanismo simples, mas muito eficiente. 13 | 14 | O serviço `DisciplinasService` possui um atributo `disciplinas`, que contém um array de objetos. Embora o software esteja funcional o array de objetos é definido diretamente no código-fonte, o que pode ser melhorado. A prática mais adequada é separar as coisas: de um lado o software, do outro a **fonte de dados**. 15 | 16 | Uma fonte de dados é um conceito abstrato que pode ser representado por um arquivo de texto, JSON ou um banco de dados, por exemplo. Aqui, usaremos um arquivo em formato JSON. 17 | 18 | O arquivo `./src/assets/dados/disciplinas.json` contém a lista das disciplinas: 19 | 20 | ```js 21 | [ 22 | { 23 | "id": 1, 24 | "nome": "Língua Portuguesa", 25 | "descricao": "..." 26 | }, 27 | { 28 | "id": 2, 29 | "nome": "Inglês", 30 | "descricao": "..." 31 | }, 32 | ... 33 | ] 34 | ``` 35 | 36 | Para acessar os dados de arquivos JSON é necessário realizar requisições HTTP para um servidor web. Se você estiver executando o projeto em modo de desenvolvimento, o servidor já disponibilizado pelo Angular CLI para desenvolvimento será utilizado tanto para servir o conteúdo do software quanto fornecer acesso ao arquivo JSON. Para isso o Angular disponibiliza o módulo `HttpClientModule`, que precisa, primeiro, ser importado no **root module**: 37 | 38 | ```typescript 39 | ... 40 | import {HttpClientModule} from '@angular/common/http'; 41 | 42 | @NgModule({ 43 | declarations: [ 44 | ... 45 | ], 46 | imports: [ 47 | NgbModule.forRoot(), 48 | BrowserModule, 49 | HttpClientModule, 50 | FormsModule 51 | ], 52 | ... 53 | }) 54 | export class AppModule { 55 | } 56 | ``` 57 | 58 | Depois, o serviço `DisciplinasService` precisa ser modificado: 59 | 60 | ```typescript 61 | ... 62 | import {HttpClient} from '@angular/common/http'; 63 | import {Observable} from 'rxjs/Observable'; 64 | 65 | @Injectable() 66 | export class DisciplinasService { 67 | private disciplinas = null; 68 | private novo_id = 1; 69 | 70 | constructor(private http: HttpClient) { 71 | } 72 | 73 | carregarDados(callback) { 74 | this.http.get('./assets/dados/disciplinas.json') 75 | .subscribe(disciplinas => this.disciplinas = disciplinas) 76 | .add(callback); 77 | } 78 | 79 | ... 80 | 81 | } 82 | ``` 83 | 84 | A primeira modificação no código do serviço é a injeção de dependência de um `HttpClient`, uma classe do módulo `HttpClientModule` que fornece métodos apropriados para realizar requisições HTTP assíncronas. A classe `HttpClient` fornece o método `get()`, que é utilizado para realizar uma requisição HTTP GET. O parâmetro informado para o método `get()` é uma URL que, nesse caso, corresponde ao caminho para o arquivo `disciplinas.json`. 85 | 86 | A classe `HttpClient` é usada efetivamente no método `carregarDados()`, que aceita como parâmetro uma **função callback** que deverá ser executada quando o carregamento dos dados for concluído. Esse comportamento é uma estratégia para carregar os dados de uma vez, como um array de disciplinas, mantendo uma referência para esse array no atributo `DisciplinasSservice.disciplinas` e também no atributo `AppComponent.disciplinas`. Isso acontece porque a forma de acesso à lista de disciplinas, até o momento, não permite alterar seu conteúdo de forma persistente, ou seja, não salva os dados. 87 | 88 | Antes de prosseguirmos com a leitura do código, é importante detalhar o conceito de **requisição assíncrona**. A figura a seguir ilustra esse processo. 89 | 90 | ![Ilustração do processo de requisição assíncrona](https://www.planttext.com/plantuml/img/VP0z3i8m38NtdC8Z35GLwiI0g13Y07HaiHeFGQaDJjg1wt0BLYw6GVmAYSZAak-zb-T5ogYvxw9ponY8Cy5a3XlI8NZH6QnN3VYGsh2FWJ4LkoJidi_VQ6DgX2Wjnd141G6bjjSaiELV3umPQZtqOH0WReMpeXSOJSjoxC3EPyZZQpEuSNGv6sY33_cFDyL4BtE-dBuJghBwav2eUwSuOk-Aee3Qj5R4atMoQu-AztvPb0MCS6vXhEtn2W00) 91 | 92 | A figura demonstra que o Cliente faz uma requisição HTTP para o Servidor \(usando o verbo GET do HTTP\) que busca pelo arquivo solicitado. Conforme a situação o Servidor retorna uma de duas respostas possíveis para o Cliente: 93 | 94 | * **código 200**: o arquivo foi encontrado, então retorna o conteúdo dele 95 | * **código 404**: o arquivo não foi encontrador, então não há conteúdo no retorno 96 | 97 | De qualquer forma, um comportamento característico desse cenário acontece: quando o Cliente faz a requisição ele continua em funcionamento enquanto aguarda uma resposta do Servidor. Quando a resposta chega o Cliente é notificado que isso aconteceu e executa uma ação específica para cada tipo de retorno \(conforme o código de retorno 200 ou 404, por exemplo\). É por isso que a requisição é assícrona. 98 | 99 | Em termos de código o método `get()` da classe `HttpClient` não realiza a requisição. Ele cria um objeto do tipo `Observable` \(do pacote `rxjs`\) que implementa um padrão de projeto chamado **Observer**, que é ideal para essa situação das requisições assíncronas. Por causa desse padrão de projeto, vamos a uma analogia demonstada pela figura a seguir. 100 | 101 | ![Ilustração do processo de assinatura de uma revista](https://www.planttext.com/plantuml/img/TP313OCm34NlcS8Bm02ega28Mm_j26wILfOWGKx2GZrKwXeiLWjeeHAzsk_PNxyC4JcchbMgRicwQ24xGcCeUiO2Bico1mo1738Wi1r83BK0FspD98n6Wo6AP3pe-UAMNfuKK4qtOvAnzkv6t8cOGtLFoCQ2ymE2DJG-nuTNUIwRo1Wyz2W6Gf-kBMcSrY1ywbOSp3SeYBaRzZpx_FjeO-w6Rjn0-5_vD7Z8LkKqlZQzgT8w8ss_0G00) 102 | 103 | A figura ilustra a comunicação entre Cliente e Editora. Depois de realizar a assinatura o cliente fica aguardando a Editora publicar uma nova edição da revista e enviar um exemplar para o Cliente, _observando_ esse processo. Enquanto isso acontece o Cliente não fica parado, mas vai realizando seus afazeres. Quando um exemplar da nova edição chega, então o Cliente inicia a sua leitura. 104 | 105 | O padrão **Observer** tem dois elementos importantes: o **publisher** \(no caso, a Editora\) e o **subscriber** \(no caso, o Cliente\). Um terceiro elemento é importante é a revista, que representa o **dado** entregue pela Editora para o Cliente. 106 | 107 | No contexto do código o método `get()` cria uma instância de `Observable`. Para realizar uma requisição e realizar uma ação quando a resposta chega do servidor é utilizado o método `subscribe()`. Por sua vez o método `subscribe()` pode receber dois parâmetros: 108 | 109 | * **o primeiro parâmetro é uma função callback executada quando a requisição for bem sucedida.** Nesse caso, o retorno da requisição é convertido para JSON e tratado no código como um objeto, representado pelo parâmetro da função callback 110 | * **o segundo parâmetro é uma função callback executada quando a requisição não for bem sucedida.** Nesse caso o retorno da requisição \(se houver\) também é convertido para JSON e tratado no código como um objeto, também representado pelo parâmetro da função callback. 111 | 112 | No caso do código do serviço `DisciplinasService` apenas o primeiro parâmetro está sendo utilizado, o que ilustrado pela figura a seguir. 113 | 114 | ![Ilustração da composição de uma função callback para o método subscribe\(\)](/assets/httpclient-observer-subscribe-callback.png) 115 | 116 | A sintaxe utilizada é conhecida como **arrow function** \(função seta\) e, por ser uma função, tem duas partes: 117 | 118 | * parâmetros; e 119 | * corpo. 120 | 121 | Se a lista de parâmetros for vazia, pode-se usar apenas `()`. 122 | 123 | Voltando ao código do serviço `DisciplinasService` a função callback de sucesso para a chamada de `subscribe()` atribui o parâmetro disciplinas \(a lista de disciplinas retornada pelo servidor já em formato de objetos\) para o atributo da classe de mesmo nome. 124 | 125 | Para finalizar a leitura do código do `DisciplinasService` há mais um recurso importante sendo utilizado. O método `carregarDados()` recebe como parâmetro uma função callback. O método cria uma instância de `Observable` e chama o método `subscribe()`. Como o objetivo desse método é executar a função callback informada como parâmetro quando a requisição HTTP for concluída e os dados estiverem disponíveis, é feita uma chamada para a função `add()`, informando como parâmetro a função callback. 126 | 127 | Para usar o serviço `DisciplinasService` o `constructor()` do Controller `AppComponent` comporta-se da seguinte forma: 128 | 129 | ```typescript 130 | constructor(private disciplinasService: DisciplinasService) { 131 | this.disciplinasService.carregarDados( 132 | () => this.disciplinas = this.disciplinasService.todas() 133 | ); 134 | } 135 | ``` 136 | 137 | Perceba que a chamada para `carregarDados()` informa uma **arrow function** como parâmetro \(a callback\). 138 | 139 | Para finalizar esse capítulo, a figura a seguir resume e ilustra os conceitos apresentados, indicando a sequência de passos da comunicação entre os componentes. 140 | 141 | ![Diagrama de sequência do software, demonstrando a interação entre AppComponent, DisciplinasService e Servidor HTTP](https://www.planttext.com/plantuml/img/bLFDJW913BxFK_G6c-Wy05424QD74pcIcB9Jbz5bkdQw9BwEZ-6L5tCMGLX-8jncfkttVVtQ6KH5qNfUcc5LtV6yua11uReF8nzpNvK-O7mcMVYSUf2Z21Ke8tGSkpcMvHJpzymSvfv2cAbMas0Bqcx7RUFsBNBeP2aIwsdCnKzf2vzUqRb_wLP7n_BoE1u_zU3XVWpx3CPQ2yEYHd48GieI61n3N9T2KvfoJ0lhf1iSb9RVRWM1yb7x1HzIdk_DJYdSza5dFjhMssw62Qm4uekPDdFvjMRLov-1gP4CC6aKptA1ZWrQDpj9qioZ1Nyr2HNAycjEAia4sbkLx62zTkyNjhuMQfKTnet8abxtDBOLjCquPzatyCUDQ-iVYk3dpJQpRyo0Wal_wWS0) 142 | 143 | Se necessário analise a sequência do diagrama mais de uma vez, acompanhado do código-fonte. 144 | 145 | -------------------------------------------------------------------------------- /assets/acessando-dados-api-exemplo-subscribe-sucesso-erro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/acessando-dados-api-exemplo-subscribe-sucesso-erro.png -------------------------------------------------------------------------------- /assets/angular-inicial-app-component-template-model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/angular-inicial-app-component-template-model.png -------------------------------------------------------------------------------- /assets/angular-inicial-estrutura-appmodule-appcomponent-index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/angular-inicial-estrutura-appmodule-appcomponent-index.png -------------------------------------------------------------------------------- /assets/browser-com-ferramentas-desenvolvedor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/browser-com-ferramentas-desenvolvedor.png -------------------------------------------------------------------------------- /assets/browser-debug-barra-de-ferramentas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/browser-debug-barra-de-ferramentas.png -------------------------------------------------------------------------------- /assets/browser-depuracao-ocorrendo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/browser-depuracao-ocorrendo.png -------------------------------------------------------------------------------- /assets/browser-listando-disciplinas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/browser-listando-disciplinas.png -------------------------------------------------------------------------------- /assets/depuracao-browser-sources-webpack-breakpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/depuracao-browser-sources-webpack-breakpoint.png -------------------------------------------------------------------------------- /assets/escola-estrutura-curricular-1-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/escola-estrutura-curricular-1-5.png -------------------------------------------------------------------------------- /assets/escola-estrutura-curricular-6-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/escola-estrutura-curricular-6-9.png -------------------------------------------------------------------------------- /assets/escola-estrutura-curricular-medio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/escola-estrutura-curricular-medio.png -------------------------------------------------------------------------------- /assets/escola-estrutura-ensino-infantil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/escola-estrutura-ensino-infantil.png -------------------------------------------------------------------------------- /assets/feature-modules-navegacao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/feature-modules-navegacao.png -------------------------------------------------------------------------------- /assets/formularios-two-way-data-binding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/formularios-two-way-data-binding.png -------------------------------------------------------------------------------- /assets/httpclient-observer-subscribe-callback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/httpclient-observer-subscribe-callback.png -------------------------------------------------------------------------------- /assets/ilustracao-procurando-rota.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/ilustracao-procurando-rota.png -------------------------------------------------------------------------------- /assets/ilustracao-rotas-router-outlet-combinacao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/ilustracao-rotas-router-outlet-combinacao.png -------------------------------------------------------------------------------- /assets/ilustracao-uri-url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/ilustracao-uri-url.png -------------------------------------------------------------------------------- /assets/images/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/images/README.md -------------------------------------------------------------------------------- /assets/images/uml/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/images/uml/README.md -------------------------------------------------------------------------------- /assets/interacao-componentes-app-listadedisciplinas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/interacao-componentes-app-listadedisciplinas.png -------------------------------------------------------------------------------- /assets/intermediario-componente-listadedisciplinas-browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/intermediario-componente-listadedisciplinas-browser.png -------------------------------------------------------------------------------- /assets/primeiro-teste-browser-inicial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/primeiro-teste-browser-inicial.png -------------------------------------------------------------------------------- /assets/primeiro-teste-com-falha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/primeiro-teste-com-falha.png -------------------------------------------------------------------------------- /assets/projeto-original-appcomponent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/projeto-original-appcomponent.png -------------------------------------------------------------------------------- /assets/rotas-estrutura-classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/rotas-estrutura-classes.png -------------------------------------------------------------------------------- /assets/software-arquitetura-modulos-api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/software-arquitetura-modulos-api.png -------------------------------------------------------------------------------- /assets/software-cadastrando-disciplina-browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/software-cadastrando-disciplina-browser.png -------------------------------------------------------------------------------- /assets/software-componentes-app-listadedisciplinas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/software-componentes-app-listadedisciplinas.png -------------------------------------------------------------------------------- /assets/software-crud-disciplinas-com-tables-browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/software-crud-disciplinas-com-tables-browser.png -------------------------------------------------------------------------------- /assets/software-dados-api-http-demonstracao-erro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/software-dados-api-http-demonstracao-erro.png -------------------------------------------------------------------------------- /assets/software-diagrama-de-classes-servicos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/software-diagrama-de-classes-servicos.png -------------------------------------------------------------------------------- /assets/software-excluindo-disciplina-browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/software-excluindo-disciplina-browser.png -------------------------------------------------------------------------------- /assets/software-inicial-crud-disciplinas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/software-inicial-crud-disciplinas.png -------------------------------------------------------------------------------- /assets/software-interface-bootstrap-browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/software-interface-bootstrap-browser.png -------------------------------------------------------------------------------- /assets/software-listando-disciplinas-interacao-browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/software-listando-disciplinas-interacao-browser.png -------------------------------------------------------------------------------- /assets/software-modelo-crud-template-component.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/software-modelo-crud-template-component.png -------------------------------------------------------------------------------- /assets/software-versao-inicial-browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/software-versao-inicial-browser.png -------------------------------------------------------------------------------- /assets/visao-geral-da-arquitetura-do-angular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/assets/visao-geral-da-arquitetura-do-angular.png -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["anchors", "url-embed", "image-captions", "page-numbering", "alerts", "plantuml-cloud"], 3 | "pluginsConfig": { 4 | "image-captions": { 5 | "variable_name": "pictures", 6 | "align": "left", 7 | "caption": "Figura _PAGE_LEVEL_._PAGE_IMAGE_NUMBER_ - _CAPTION_" 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /cadastrando-uma-disciplina.md: -------------------------------------------------------------------------------- 1 | # Cadastrando uma disciplina 2 | 3 | > **\[info\] Objetivos do capítulo:** 4 | > 5 | > * demonstrar o uso de formulários e _two-way data binding_ 6 | > * implementar o requisito "Cadastrar disciplina" 7 | > 8 | > **Branches**: [livro-cadastrando-disciplina](https://github.com/jacksongomesbr/angular-escola/tree/livro-cadastrando-disciplina) 9 | 10 | Neste capítulo vamos começar pelo final: primeiro, veja a figura que ilustra como o software estará ao final do capítulo. 11 | 12 | ![Tela do software apresentando a lista das disciplinas e o formulário de cadastro](/assets/software-cadastrando-disciplina-browser.png) 13 | 14 | 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. 15 | 16 | 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\). 17 | 18 | Para habilitar o suporte a formulários é necessário importar o módulo `FormsModule` no **root module**: 19 | 20 | ```typescript 21 | import {BrowserModule} from '@angular/platform-browser'; 22 | import {NgModule} from '@angular/core'; 23 | import {FormsModule} from '@angular/forms'; 24 | import {AppComponent} from './app.component'; 25 | 26 | @NgModule({ 27 | ... 28 | imports: [ 29 | BrowserModule, 30 | FormsModule 31 | ], 32 | ... 33 | }) 34 | export class AppModule { 35 | } 36 | ``` 37 | 38 | 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. 39 | 40 | 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. 41 | 42 | Para fazer isso, primeiro vamos ao Controller do `AppComponent`: 43 | 44 | ```typescript 45 | ... 46 | export class AppComponent { 47 | selecionado = null; 48 | nome = null; 49 | descricao = null; 50 | disciplinas = [ 51 | new Disciplina('Língua Portuguesa', 'O objetivo norteador da BNCC de ...'), 52 | ... 53 | ]; 54 | 55 | salvar() { 56 | const d = new Disciplina(this.nome, this.descricao); 57 | this.disciplinas.push(d); 58 | this.nome = null; 59 | this.descricao = null; 60 | } 61 | } 62 | ``` 63 | 64 | São adicionados dois atributos e um método: 65 | 66 | * `nome`: está ligado ao campo nome do formulário por meio de **data binding** 67 | * `descricao`: está ligado ao campo descricao do formulário por meio de **data binding** 68 | * `salvar()`: é executado quando o usuário clicar no botão "Salvar" 69 | 70 | 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`. 71 | 72 | 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: 73 | 74 | ```html 75 |

Disciplinas

76 | ... 77 |

Cadastrar

78 |

Use este formulário para cadastrar uma disciplina.

79 |
80 |
81 |
82 | 83 |
84 |
85 |
86 | 89 |
90 | 93 |
94 | ``` 95 | 96 | Como a parte da lista das disciplinas não muda muito, vamos omitir essa parte e apresentar o formulário, começando pela tag `
`. 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()`. 97 | 98 | 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: 99 | 100 | * se o valor do atributo `nome` for modificado no Controller, o Template será atualizado, mostrando esse valor no `input` 101 | * se o valor do atributo `nome` for modificado no Template, por uma alteração no `input`, esse valor estará disponível no Controller 102 | 103 | Por isso o **two-way: duas vias**. A figura a seguir ilustra esse processo. 104 | 105 | ![Ilustração do two-way data binding, ligando formulário e Controller](/assets/formularios-two-way-data-binding.png) 106 | 107 | O formulário tem mais uma característica: a entrada do usuário está passando por validação. O elemento `input` para o campo nome 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. 108 | 109 | Execute o software no browser para notar o comportamento citado no capítulo. 110 | 111 | > **\[info\] Resumo do capítulo:** 112 | > 113 | > * Implementamos o requisito "Cadastrar uma disciplina" 114 | > * Utilizamos o recurso two-way data binding 115 | > * Utilizamos formulário para receber entrada do usuário 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /conceitos-iniciais/angular-cli.md: -------------------------------------------------------------------------------- 1 | # Angular CLI 2 | 3 | > **\[info\] Objetivos do capítulo**: 4 | > 5 | > * apresentar o Angular CLI 6 | > * apresentar comandos para criação de projetos, módulos, componentes e outros elementos 7 | > * apresentar o arquivo `.angular-cli.json` 8 | 9 | O Angular CLI \(disponível em [https://cli.angular.io](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. 10 | 11 | > **\[info\] cê-éle-í?** 12 | > 13 | > 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. 14 | 15 | As seções a seguir vão apresentar como instalar e utilizar o Angular CLI para desenvolver software Angular. 16 | 17 | ## Instalando 18 | 19 | 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\). 20 | 21 | 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: 22 | 23 | ``` 24 | npm install -g @angular/cli 25 | ``` 26 | 27 | Isso fará uma instalação global do Angular CLI \(por isso o `-g` na linha de comando\) e disponibilizará o programa `ng`. 28 | 29 | > **\[warning\] Problemas na instalação?** 30 | > 31 | > 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. 32 | > 33 | > 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`. 34 | 35 | ## Comandos 36 | 37 | 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. 38 | 39 | Os principais comandos serão apresentados nas seções seguintes. 40 | 41 | ### help 42 | 43 | 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. 44 | 45 | **Exemplo: documentação completa** 46 | 47 | ``` 48 | ng help 49 | ``` 50 | 51 | Essa linha de comando apresenta todos os comandos e todas as suas opções. 52 | 53 | **Exemplo: documentação do comando **`new`**:** 54 | 55 | ``` 56 | ng help new 57 | ``` 58 | 59 | Essa linha de comando apresenta apenas as opções do comando `new`. 60 | 61 | 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. 62 | 63 | **Exemplo: comando new com duas opções** 64 | 65 | ``` 66 | ng new escola-app --skip-install --routing true --minimal true 67 | ``` 68 | 69 | 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`\). 70 | 71 | ### new 72 | 73 | O comando `new` é utilizado para criar um projeto \(um software Angular\). Exemplo: 74 | 75 | ``` 76 | ng new escola-app 77 | ``` 78 | 79 | 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. 80 | 81 | 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. 82 | 83 | Para cada comando do Angular CLI é possível informar opções. As opções mais usadas do comando `new` são: 84 | 85 | * `--skip-install`: faz com que as dependências não sejam instaladas 86 | * `--routing`: se for seguida de `true`, o comando cria um módulo de rotas \(isso será visto em outro capítulo 87 | 88 | ### generate 89 | 90 | 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: 91 | 92 | * class 93 | * component 94 | * directive 95 | * enum 96 | * guard 97 | * interface 98 | * module 99 | * pipe 100 | * service 101 | 102 | Como acontece com outros comandos, o `generate` também pode ser utilizado por meio do atalho `g`. 103 | 104 | **Exemplo: criar component ListaDeDisciplinas** 105 | 106 | ``` 107 | ng generate component ListaDeDisciplinas 108 | ``` 109 | 110 | Essa linha de comando criará o diretório `./src/app/lista-de-disciplinas` e outros quatro arquivos nesse mesmo local: 111 | 112 | * `lista-de-disciplinas.component.css` 113 | * `lista-de-disciplinas.component.html` 114 | * `lista-de-disciplinas.component.spec.ts` 115 | * `lista-de-disciplinas.component.ts` 116 | 117 | É 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\). 118 | 119 | ### serve 120 | 121 | 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. 122 | 123 | ### build 124 | 125 | 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. 126 | 127 | Veremos mais sobre esses e outros comando no decorrer do livro. 128 | 129 | > **\[info\] Espera, compilação?** 130 | > 131 | > 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. 132 | > 133 | > Por isso os comandos `serve` e `build` são tão importantes e -- por que não dizer? -- **browser-friendly** =\) 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /conceitos-iniciais/apresentando-a-lista-de-disciplinas.md: -------------------------------------------------------------------------------- 1 | # Apresentando a lista de disciplinas 2 | 3 | > **\[info\] Objetivos do capítulo:** 4 | > 5 | > * demonstrar a implementação do requisito "apresentar as disciplinas" de forma iterativa 6 | > * demonstrar o uso das diretivas `ngFor` e `ngIf` 7 | > * reforçar o entendimento do Data binding 8 | > 9 | > **Branches**: [livro-listando-disciplinas](https://github.com/jacksongomesbr/angular-escola/tree/livro-listando-disciplinas), [livro-listando-disciplinas-objetos](https://github.com/jacksongomesbr/angular-escola/tree/livro-listando-disciplinas-objetos), [livro-listando-disciplinas-classes](https://github.com/jacksongomesbr/angular-escola/tree/livro-listando-disciplinas-classes), [livro-listando-disciplinas-interacao](https://github.com/jacksongomesbr/angular-escola/tree/livro-listando-disciplinas-interacao) 10 | 11 | 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\): 12 | 13 | ```typescript 14 | export class AppComponent { 15 | disciplinas = [ 16 | 'Língua Portuguesa', 17 | 'Arte', 18 | 'Educação Física', 19 | 'Matemática', 20 | 'História', 21 | 'Geografia', 22 | 'Ciências', 23 | 'Redação', 24 | 'Língua Estrangeira Moderna - Inglês', 25 | 'Ensino Religioso' 26 | ]; 27 | } 28 | ``` 29 | 30 | Na sequência altere o Template do `AppComponent` para o seguinte conteúdo: 31 | 32 | ```html 33 |

Disciplinas

34 |
35 | 40 | ``` 41 | 42 | 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. 43 | 44 | > 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. 45 | 46 | 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: 47 | 48 | * a repetição sempre é baseada em uma coleção de itens \(ou em algo que possa ser interpretado como tal\) 49 | * o Angular cria uma variável de template \(existe apenas dentro do elemento `li`\) com o nome de `disciplina` 50 | * essa variável de template é usada como uma referência para cada item da repetição 51 | 52 | É por isso que a interpolação funciona dentro do elemento `li` para apresentar o nome de cada disciplina. 53 | 54 | > 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. 55 | 56 | Abra o aplicativo no browser. O resultado deve ser semelhante à figura a seguir. 57 | 58 | ![Apresentando a lista das disciplinas](/assets/browser-listando-disciplinas.png) 59 | 60 | ## Melhorando o modelo de dados usando objetos 61 | 62 | 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. 63 | 64 | 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. 65 | 66 | ```typescript 67 | ... 68 | export class AppComponent { 69 | disciplinas = [ 70 | { 71 | nome: 'Língua Portuguesa', 72 | descricao: 'O objetivo norteador da BNCC de Língua Portuguesa é ' + 73 | 'garantir a todos os alunos o acesso aos saberes ' + 74 | 'linguísticos necessários para a participação social e o exercício ' + 75 | 'da cidadania, pois é por meio da língua que ' + 76 | 'o ser humano pensa, comunica-se, tem acesso à informação, expressa ' + 77 | 'e defende pontos de vista, partilha ou ' + 78 | 'constrói visões de mundo e produz conhecimento.' 79 | }, 80 | ... 81 | ]; 82 | } 83 | ``` 84 | 85 | Essa notação do TypeScript para representar objetos de forma literal é muito útil quando não se tem uma classe para descrever objetos. 86 | 87 | Na sequência ocorre uma pequena alteração no Template: 88 | 89 | ```html 90 |

Disciplinas

91 |
92 | 97 | ``` 98 | 99 | A principal mudança está no fato de que as interpolações usam expressões baseadas nos atributos do objeto `disciplina`. 100 | 101 | Para ver o resultado, abra o aplicativo no browser. 102 | 103 | ## Melhorando o modelo de dados usando classes 104 | 105 | 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: 106 | 107 | ```typescript 108 | export class Disciplina { 109 | nome: string; 110 | descricao: string; 111 | 112 | constructor(nome: string, descricao?: string) { 113 | this.nome = nome; 114 | this.descricao = descricao; 115 | } 116 | } 117 | ``` 118 | 119 | 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`. 120 | 121 | Para usar a classe `Disciplina`, modifique o Controller do `AppComponent`: 122 | 123 | ```typescript 124 | import {Component} from '@angular/core'; 125 | import {Disciplina} from './disciplina.model'; 126 | ... 127 | export class AppComponent { 128 | disciplinas = [ 129 | new Disciplina('Língua Portuguesa', 'O objetivo norteador ...'), 130 | new Disciplina('Educação Física', 'A Educação Física é ...'), 131 | ... 132 | ]; 133 | } 134 | ``` 135 | 136 | 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`. 137 | 138 | ## Adicionando interação com o usuário 139 | 140 | 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. 141 | 142 | Para implementar esses requisitos vamos usar outros recursos do Angular. 143 | 144 | Começando pelo Controller do `AppComponent` são acrescentados: 145 | 146 | * o atributo `selecionado` 147 | * o método `selecionar()` 148 | 149 | ```typescript 150 | export class AppComponent { 151 | selecionado = null; 152 | disciplinas = [ 153 | new Disciplina('Língua Portuguesa', 'O objetivo norteador da BNCC ...'), 154 | ... 155 | ]; 156 | 157 | selecionar(disciplina) { 158 | this.selecionado = disciplina; 159 | } 160 | } 161 | ``` 162 | 163 | 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. 164 | 165 | Para utilizar mais recursos do desenvolvimento front-end, dessa vez também alteramos o arquivo `./src/app/app.component.css`: 166 | 167 | ```css 168 | .selecionado { 169 | font-weight: bold; 170 | } 171 | 172 | li p:nth-child(1) { 173 | cursor: pointer; 174 | font-size: 12pt; 175 | } 176 | 177 | li p:nth-child(1):hover { 178 | font-weight: bold; 179 | } 180 | 181 | li p:nth-child(2) { 182 | font-size: 10pt; 183 | } 184 | ``` 185 | 186 | O arquivo CSS do `AppComponent` possui regras de formatação: 187 | 188 | * seletor `.selecionado`: para deixar o nome da disciplina selecionada em negrito 189 | * 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 190 | * seletor `li p:nth-child(1):hover`: para que o nome da disciplina sob o mouse fique em negrito 191 | * seletor `li p:nth-child(2)`: para que a fonte da descrição seja de tamanho 10pt 192 | 193 | Agora, o Template: 194 | 195 | ```html 196 |

Disciplinas

197 |
198 | 204 | ``` 205 | 206 | 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. 207 | 208 | 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. 209 | 210 | 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 `

`: `[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. 211 | 212 | Para finalizar, o software deve apresentar a descrição da disciplina selecionada. Para isso vamos usar outra diretiva, a `NgIf`. Veja o trecho: 213 | 214 | ```html 215 |

  • 216 | ... 217 |

    {{disciplina.descricao}}

    218 |
  • 219 | ``` 220 | 221 | 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. 222 | 223 | Para ilustrar isso, veja um trecho da estrutura HTML no browser: 224 | 225 | ```html 226 | 239 | ``` 240 | 241 | 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: 242 | 243 | * para o elemento `li` correspondente à disciplina selecionada: 244 | * o elemento `p` que mostra o nome da disciplina possui `class="selecionado"` 245 | * o elemento `p` que mostra a descrição da disciplina está presente no HTML 246 | * para os demais elementos `li`: 247 | * o elemento `p` que mostra o nome da disciplina não possui o atributo `class` 248 | * o elemento `p` que mostra a descrição da disciplina não existe no HTML 249 | 250 | 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\). 251 | 252 | Por fim, a figura a seguir ilustra o funcionando do software no browser. 253 | 254 | ![Execução do software no browser, listando disciplinas e interagindo com o usuário](/assets/software-listando-disciplinas-interacao-browser.png) 255 | 256 | Pronto, esse capítulo mostrou a utilização de vários recursos do Angular para implementar o requisito "Apresentar a lista das disciplinas". 257 | 258 | > **\[info\] Para resumir:** 259 | > 260 | > * utilizar recursos da Programação Orientada a Objetos é muito bom para representação do modelo de dados 261 | > * a diretiva `NgForOf` é utilizada para repetir elementos no Template com base em uma coleção de itens \(um array\) 262 | > * a diretiva `NgIf` é utilizada para incluir ou excluir elementos do Template com base em um condicional 263 | > * `(click)` representa a sintaxe do Angular para criar tratadores de eventos 264 | > * `[class.selecionado]` representa a sintaxe do Angular para adicionar ou remover atributos no HTML 265 | 266 | 267 | 268 | -------------------------------------------------------------------------------- /conceitos-iniciais/criando-o-projeto.md: -------------------------------------------------------------------------------- 1 | # Criando o projeto 2 | 3 | > **\[info\] Objetivos do capítulo:** 4 | > 5 | > * demonstrar como utilizar o Angular CLI para criar o projeto **angular-escola** 6 | > * apresentar os requisitos de software que serão implementados no capítulo 7 | > 8 | > **Branch**: [iniciando](https://github.com/jacksongomesbr/angular-escola/tree/iniciando) 9 | 10 | O capítulo [Angular CLI](/conceitos-iniciais/angular-cli.md) apresentou essa ferramenta, comandos e opções. A primeira utilização da ferramenta está na criação do projeto com o comando: 11 | 12 | ``` 13 | ng new angular-escola 14 | ``` 15 | 16 | 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: 17 | 18 | ``` 19 | npm start 20 | ``` 21 | 22 | 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. 23 | 24 | ![Versão inicial do software em execução no Browser](/assets/software-versao-inicial-browser.png) 25 | 26 | Esse não é um comando do Angular CLI. Antes, é um comando \(script\) que está definido no arquivo `package.json`. 27 | 28 | ## O arquivo package.json 29 | 30 | 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: 31 | 32 | ```js 33 | { 34 | "name": "angular-escola", 35 | ... 36 | "scripts": { 37 | "ng": "ng", 38 | "start": "ng serve", 39 | "build": "ng build --prod", 40 | ... 41 | }, 42 | "private": true, 43 | "dependencies": { 44 | "@angular/animations": "^5.2.0", 45 | "@angular/common": "^5.2.0", 46 | "@angular/compiler": "^5.2.0", 47 | "@angular/core": "^5.2.0", 48 | "@angular/forms": "^5.2.0", 49 | "@angular/http": "^5.2.0", 50 | "@angular/platform-browser": "^5.2.0", 51 | "@angular/platform-browser-dynamic": "^5.2.0", 52 | "@angular/router": "^5.2.0", 53 | "core-js": "^2.4.1", 54 | "rxjs": "^5.5.6", 55 | "zone.js": "^0.8.19" 56 | }, 57 | "devDependencies": { 58 | "@angular/cli": "1.6.6", 59 | "@angular/compiler-cli": "^5.2.0", 60 | "@angular/language-service": "^5.2.0", 61 | ... 62 | } 63 | } 64 | ``` 65 | 66 | Por ser um conteúdo em formato JSON, o interpretamos da seguinte forma: 67 | 68 | * `name`: é o atributo que define o nome do projeto para o NodeJS \(nesse ponto, não tem a ver com o Angular\) 69 | * `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`\) 70 | * `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\) 71 | * `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 72 | 73 | ## Examinando o código-fonte\#0 74 | 75 | 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. 76 | 77 | ```html 78 |
    79 |

    80 | Welcome to {{ title }}! 81 |

    82 | Angular Logo 83 |
    84 | ... 85 | ``` 86 | 87 | 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. 88 | 89 | Agora, o Controller do mesmo componente, no arquivo `./src/app/app.component.ts`, com código-fonte a seguir: 90 | 91 | ```typescript 92 | import { Component } from '@angular/core'; 93 | 94 | @Component({ 95 | selector: 'app-root', 96 | templateUrl: './app.component.html', 97 | styleUrls: ['./app.component.css'] 98 | }) 99 | export class AppComponent { 100 | title = 'app'; 101 | } 102 | ``` 103 | 104 | Há pouco conteúdo, mas já são usados vários conceitos do Angular, vamos examinar um-a-um: 105 | 106 | 1. a primeira linha usa a instrução `import` para incluir algo chamado `Component` e que é disponibilizado pelo pacote `@angular/core`. 107 | 2. `Component` é uma **annotation function** e é utilizada pelo Angular para adicionar metadados à classe `AppComponent`. 108 | 3. Os metadados são fornecidos como parâmetro para a função `Component`: 109 | 1. `selector`: indica que o **seletor **é `app-root`. E o que é seletor? Veremos daqui a pouco 110 | 2. `templateUrl`: indica que o arquivo utilizado para o template do componente é `./app.component.html` \(que vimos há pouco\) 111 | 3. `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` 112 | 4. a instrução `export` é utilizada para que outros arquivos possam utilizar a classe `AppComponent` 113 | 114 | Antes de prosseguirmos, perceba que há um atributo da classe `AppComponent` chamado `title` e tem o valor `'app'` \(onde já vimos isso?\). 115 | 116 | A figura a seguir ilustra a estrutura do `AppComponent`. 117 | 118 | ![Estrutura do component AppComponent](/assets/projeto-original-appcomponent.png) 119 | 120 | 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'`. 121 | 122 | 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: 123 | 124 | ```html 125 |

    126 | Welcome to {{ title }}! 127 |

    128 | ``` 129 | 130 | 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**: 131 | 132 | * analisa o Controller e identifica métodos e atributos 133 | * interpreta e analisa o Template e procura por, por exemplo, conteúdo que esteja entre {% raw %} `{{` {% endraw %} e {% raw %} `}}` {% endraw %} 134 | * utiliza o recurso de **interpolação**, que faz com a expressão dentro de {% raw %} `{{` {% endraw %} e {% raw %} `}}` {% endraw %} 135 | seja interpretada e gere uma alteração no conteúdo do Template ao ser apresentado no Browser. Nesse caso {% raw %}`{{ title }}`{% endraw %} significa: mostre o valor de `title`. 136 | 137 | A figura a seguir ilustra os elementos desse processo. 138 | 139 | ![Relação entre Model-Template-Controller no AppComponent](/assets/angular-inicial-app-component-template-model.png) 140 | 141 | 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. 142 | 143 | Se você se lembrar da discussão sobre Angular 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. 144 | 145 | É 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\). 146 | 147 | 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`: 148 | 149 | ```html 150 | 151 | 152 | 153 | 154 | Angular Escola 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | ``` 165 | 166 | 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: 167 | 168 | ```html 169 | 170 | 171 | ... 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | ``` 183 | 184 | Esse é um trecho do código-fonte visualizado pelo Browser. Perceba as linhas com elementos `script` antes da tag ``. 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 **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 é: 185 | 186 | ``` 187 | import { enableProdMode } from '@angular/core'; 188 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 189 | import { AppModule } from './app/app.module'; 190 | ... 191 | platformBrowserDynamic().bootstrapModule(AppModule) 192 | .catch(err => console.log(err)); 193 | ``` 194 | 195 | 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](/conceitos-iniciais/iniciando-com-o-angular.md) 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\). 196 | 197 | 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`: 198 | 199 | ``` 200 | import { BrowserModule } from '@angular/platform-browser'; 201 | import { NgModule } from '@angular/core'; 202 | import { AppComponent } from './app.component'; 203 | 204 | @NgModule({ 205 | declarations: [ 206 | AppComponent 207 | ], 208 | imports: [ 209 | BrowserModule 210 | ], 211 | providers: [], 212 | bootstrap: [AppComponent] 213 | }) 214 | export class AppModule { } 215 | ``` 216 | 217 | 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: 218 | 219 | * `declarations`: é um vetor que contém componentes do módulo \(isso mesmo, `AppComponent` está contido no `AppModule`\); 220 | * `imports`: é um vetor que contém as dependências \(outros módulos\); 221 | * `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`\). 222 | 223 | As coisas estão ficando mais conectadas agora e a figura a seguir ilustra isso. 224 | 225 | ![](/assets/angular-inicial-estrutura-appmodule-appcomponent-index.png) 226 | 227 | É 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. 228 | 229 | **Viu? Não é mágica. É software!** 230 | 231 | ## O que vem a seguir? 232 | 233 | 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**. 234 | 235 | -------------------------------------------------------------------------------- /conceitos-iniciais/criando-o-projeto/depurando-codigo-no-browser.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/conceitos-iniciais/criando-o-projeto/depurando-codigo-no-browser.md -------------------------------------------------------------------------------- /conceitos-iniciais/depurando-codigo-no-browser.md: -------------------------------------------------------------------------------- 1 | # Depurando código no browser 2 | 3 | **\[info\] Objetivos do capítulo:** 4 | 5 | > * demonstrar como utilizar o recurso de depuração de código do Browser 6 | > 7 | > **Branch**: [iniciando](https://github.com/jacksongomesbr/angular-escola/tree/iniciando) 8 | 9 | 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. 10 | 11 | ![](/assets/browser-com-ferramentas-desenvolvedor.png) 12 | 13 | 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. 14 | 15 | 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. 16 | 17 | ![](/assets/depuracao-browser-sources-webpack-breakpoint.png) 18 | 19 | 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: 20 | 21 | ![](/assets/browser-debug-barra-de-ferramentas.png) 22 | 23 | Os botões representam, nesta ordem: 24 | 25 | * pausar/continuar a execução do código 26 | * executar a próxima linha e não entrar no código da função/método 27 | * executar a próxima linha e entrar no código da função/método 28 | * executar a próxima linha e sair do código da função/método 29 | * desativar os breakpoints 30 | * pausar em exceções 31 | 32 | A figura a seguir ilustra a tela durante o processo de depuração. 33 | 34 | ![](/assets/browser-depuracao-ocorrendo.png) 35 | 36 | 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**\). 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /conceitos-iniciais/editando-dados-de-uma-disciplina.md: -------------------------------------------------------------------------------- 1 | # Editando dados de uma disciplina 2 | 3 | > **\[info\] Objetivos do capítulo:** 4 | > 5 | > * demonstrar a implementação do requisito "Editar dados de uma disciplina" 6 | > * reforçar os conceitos anteriores 7 | > * demonstrar a implementação mais completa de um CRUD 8 | > 9 | > **Branch:** [livro-crud-disciplina](https://github.com/jacksongomesbr/angular-escola/tree/livro-crud-disciplina) 10 | 11 | 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: 12 | 13 | * **C**reate \(cadastrar\) 14 | * **R**etrieve \(recuperar, listar, consultar\) 15 | * **U**pdate \(atualizar, editar\) 16 | * **D**elete \(excluir\) 17 | 18 | A figura a seguir ilustra a interface do software como construída ao final do capítulo. 19 | 20 | ![Software com o CRUD de Disciplinas](/assets/software-inicial-crud-disciplinas.png) 21 | 22 | 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: 23 | 24 | * se o usuário clicar em "Salvar" os dados da disciplina serão atualizados 25 | * se o usuário clicar em "Cancelar" qualquer alteração não será armazenada 26 | 27 | O modo padrão do formulário é o cadastro. Nesse sentido se o usuário preencher os campos \(nome e descrição\) e: 28 | 29 | * clicar em "Salvar", os dados serão armazenados em uma nova disciplina, apresentando-a na lista 30 | * clicar em "Cancelar", os dados não serão armazenados 31 | 32 | Vamos ao código-fonte. Primeiro, um trecho do Controller: 33 | 34 | ```typescript 35 | ... 36 | export class AppComponent { 37 | editando = null; 38 | nome = null; 39 | descricao = null; 40 | disciplinas = [ 41 | new Disciplina('Língua Portuguesa', 'O objetivo norteador da BNCC...'), 42 | ... 43 | ]; 44 | 45 | salvar() { 46 | if (this.editando) { 47 | this.editando.nome = this.nome; 48 | this.editando.descricao = this.descricao; 49 | } else { 50 | const d = new Disciplina(this.nome, this.descricao); 51 | this.disciplinas.push(d); 52 | } 53 | this.nome = null; 54 | this.descricao = null; 55 | this.editando = null; 56 | } 57 | 58 | excluir(disciplina) { 59 | if (this.editando == disciplina) { 60 | alert('Você não pode excluir uma disciplina que está editando'); 61 | } else { 62 | if (confirm('Tem certeza que deseja excluir a disciplina "' 63 | + disciplina.nome + '"?')) { 64 | const i = this.disciplinas.indexOf(disciplina); 65 | this.disciplinas.splice(i, 1); 66 | } 67 | } 68 | } 69 | 70 | editar(disciplina) { 71 | this.nome = disciplina.nome; 72 | this.descricao = disciplina.descricao; 73 | this.editando = disciplina; 74 | } 75 | 76 | cancelar() { 77 | this.nome = null; 78 | this.descricao = null; 79 | this.editando = null; 80 | } 81 | } 82 | ``` 83 | 84 | As novidades são: 85 | 86 | * adição do atributo `editando`: é inicializado com null e manterá uma referência para a disciplina que está sendo editada \(quando houver alguma\) 87 | * atualização no método`salvar()`: para tratar a situação do formulário \(cadastrando ou editando disciplina\) 88 | * atualização no método `excluir()`: para tratar a situação em que o usuário quer excluir uma disciplina que está sendo editada 89 | * adição do método `editar()` 90 | 91 | 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`. 92 | 93 | 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. 94 | 95 | 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\). 96 | 97 | 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`. 98 | 99 | Para concluir, o Template: 100 | 101 | ```html 102 |

    Disciplinas

    103 |
    104 | 115 | 116 |

    Cadastrar

    117 |

    Use este formulário para cadastrar ou editar uma disciplina.

    118 | 119 |
    120 |
    121 | 122 |
    123 |
    124 |
    125 | 127 |
    128 | 129 | 130 |
    131 | ``` 132 | 133 | Para ilustrar a interface completa, a figura a seguir ilustra o processo da interface CRUD para Disciplina. 134 | 135 | ![Relações entre os elementos do Template e do Controller](/assets/software-modelo-crud-template-component.png) 136 | 137 | 138 | A figura demonstra as várias relações entre os elementos do Template e do Controller. Como ela resume os conceitos vistos neste e nos quatro capítulos anteriores, se você chegou aqui e está um pouco perdido, não se preocupe, pode acontecer. Reserve um pouco mais de tempo para estudar a figura e o código-fonte e, se necessário, volte nos capítulos anteriores. 139 | 140 | O conjunto de funcionalidades implementadas até aqui demonstram vários recursos do Angular, mas a interface gráfica não é das melhores. No próximo capítulo vamos aprender como melhorar o aspecto visual do software. 141 | 142 | -------------------------------------------------------------------------------- /conceitos-iniciais/iniciando-com-o-angular.md: -------------------------------------------------------------------------------- 1 | # Iniciando com o Angular 2 | 3 | > **\[info\] Objetivos do capítulo**: 4 | > 5 | > * demonstrar os elementos da Arquitetura do Angular 6 | > * demonstrar a relação entre os elementos da Arquitetura do Angular e arquivos do projeto 7 | > 8 | > **Branch**: [iniciando](https://github.com/jacksongomesbr/angular-escola/tree/iniciando) 9 | 10 | 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. 11 | 12 | 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. 13 | 14 | ![](/assets/visao-geral-da-arquitetura-do-angular.png) 15 | 16 | Essa arquitetura de software orientada a componentes implementa conceitos de dois padrões de arquitetura de software: 17 | 18 | * **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. 19 | * **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 depois\) para fazer com que a View seja atualizada automaticamente quando ocorrer uma modificação no Model. 20 | 21 | No contexto do Angular esses elementos são descritos conforme as seções a seguir. 22 | 23 | ## Elementos da Arquitetura do Angular 24 | 25 | ### Módulos 26 | 27 | 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. 28 | 29 | 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\). 30 | 31 | ### Bibliotecas 32 | 33 | 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\). 34 | 35 | Uma biblioteca pode conter módulos, componentes, diretivas e serviços. 36 | 37 | ### Componentes 38 | 39 | 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**\). 40 | 41 | 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. 42 | 43 | ### Metadados 44 | 45 | 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. 46 | 47 | ### Data binding 48 | 49 | 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: 50 | 51 | * um Model define dados que serão apresentados no Template 52 | * um Template apresenta os dados do Model 53 | * um Controller ou um View-Model determina o comportamento do Template 54 | 55 | 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. 56 | 57 | ### Diretivas 58 | 59 | 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: 60 | 61 | * **Diretivas Estruturais**: modificam o Template dinamicamente por meio de manipulação do DOM \(Document Object Model\), adicionando ou removendo elementos HTML 62 | * **Diretivas de Atributos**: também modificam o Template, mas operam sobre elementos HTML já existentes 63 | 64 | ### Serviços 65 | 66 | 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. 67 | 68 | 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. 69 | 70 | ## Estrutura padrão de um software desenvolvido em Angular 71 | 72 | Um software desenvolvido em Angular é representado por vários arquivos HTML, CSS, TypeScript e de configuração \(geralmente arquivos em formato JSON\). 73 | 74 | ``` 75 | + src 76 | + app 77 | - app.component.css 78 | - app.component.html 79 | - app.component.ts 80 | - app.module.ts 81 | + assets 82 | + environments 83 | - environment.prod.ts 84 | - environment.ts 85 | - index.html 86 | - maint.ts 87 | - polyfills.ts 88 | - styles.css 89 | - tsconfig.app.json 90 | - typings.d.ts 91 | - .angular-cli.json 92 | - package.json 93 | - tsconfig.json 94 | - tslint.json 95 | ``` 96 | 97 | No diretório raiz do software: 98 | 99 | * `src`: contém o código-fonte do software \(módulos, componentes etc.\) 100 | * `.angular-cli.json`: contém configurações do projeto Angular \(nome, scripts etc.\) 101 | * `package.json`: contém configurações do projeto NodeJS \(um projeto Angular utiliza NodeJS para gerenciamento de pacotes e bibliotecas, por exemplo\) 102 | * `tsconfig.json` e `tslint.json`: contêm configurações do processo de tradução de código TypeScript para JavaScript 103 | 104 | No diretório `src`: 105 | 106 | * `app`: contém o **root module** e os demais **feature modules** do projeto 107 | * `assets`: contém arquivos CSS, JSON e scripts, por exemplo 108 | * `environments`: contém arquivos de configuração do ambiente de desenvolvimento \(`environment.ts`\) e de produção \(`environment.prod.ts`\) 109 | * `index.html`: contém o código HTML inicial para o projeto e inclui o componente principal do **root module** 110 | * `main.ts`: contém o código TypeScript necessário para iniciar o software \(processo chamado de **Bootstrap**\) 111 | * `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 112 | * `style.css`: contém o código CSS para definir estilos globais para o software 113 | * `tsconfig.app.json` e `typings.d.ts`: complementam configurações do arquivo `../tsconfig.json` específicas para o software em questão 114 | 115 | No diretório `app`: 116 | 117 | * `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 118 | * `app.module.ts`: código TypeScript que define o **root module** 119 | 120 | 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. 121 | 122 | 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. 123 | 124 | -------------------------------------------------------------------------------- /conceitos-iniciais/melhorando-a-interface-com-bootstrap.md: -------------------------------------------------------------------------------- 1 | # Melhorando a interface gráfica do CRUD de Disciplinas 2 | 3 | > **\[info\] Objetivos do capítulo:** 4 | > 5 | > * demonstrar a melhoria da lista de disciplinas utilizando tabelas 6 | > * apresentar o Bootstrap como framework front-end 7 | > * demonstrar a utilização do Bootstrap para melhorar o aspecto visual geral do software 8 | > 9 | > **Branches**: [livro-crud-disciplinas-tabelas](https://github.com/jacksongomesbr/angular-escola/tree/livro-crud-disciplinas-tabelas) e [livro-crud-disciplinas-bootstrap](https://github.com/jacksongomesbr/angular-escola/tree/livro-crud-disciplinas-bootstrap) 10 | 11 | Eu concordo contigo. Um software pode até funcionar muito bem, mas o visual descuidado certamente tira alguns pontos de qualquer avaliação. Por isso, vamos começar a melhorar o aspecto visual do CRUD de disciplinas construído até aqui em dois passos. Primeiro, vamos usar tabelas para melhorar a apresentação da lista de disciplinas. 12 | 13 | O código-fonte do Controller não precisa ser alterado, apenas do Template. Vamos aos trechos mais importantes: 14 | 15 | ```html 16 |

    Disciplinas

    17 |
    18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 36 | 37 | 38 |
    DisciplinaAções
    {{disciplina.nome}} 29 | 32 | 35 |
    39 | 40 |

    Cadastrar

    41 | ... 42 | ``` 43 | 44 | O diferencial até aqui é a utilização de tabelas \(elemento `table`\) para a apresentação da lista de tabelas. Perceba que a diretiva `*ngFor` foi aplicada no elemento `tr` do `tbody`. O resultado da execução do software é ilustrado pela figura a seguir. 45 | 46 | ![Execução do software no browser demonstrando o uso de tabelas para apresentar a lista das disciplinas](/assets/software-crud-disciplinas-com-tables-browser.png) 47 | 48 | Perceba que o fato de utilizar tabelas torna a exibição da lista de disciplinas mais visualmente confortável, já que os elementos estão alinhados adequadamente. 49 | 50 | ## Bootstrap e amigos 51 | 52 | Se, no mínimo, você já tiver feito algum trabalho de desenvolvimento web muito provavelmente já ouviu falar do Bootstrap, framework CSS que se tornou muito popular nos últimos anos. Para melhorar a aparência visual da interface gráfica do software vamos utilizar um conjunto de ferramentas: 53 | 54 | * [**ng-bootstrap**](https://ng-bootstrap.github.io/#/home): pacote Angular que implementa comportamento do Bootstrap no formato do Angular 55 | * **bootstrap**: framework CSS e interface gráfica para front-end 56 | * **font-awesome**: biblioteca de ícones 57 | 58 | O primeiro passo é adicionar essas bibliotecas ao projeto, para fazer isso utilize o npm: 59 | 60 | ``` 61 | npm install --save @ng-bootstrap/ng-bootstrap bootstrap@4.0.0-beta.2 font-awesome 62 | ``` 63 | 64 | Quando a execução dessa linha de comando concluir o arquivo `./package.json` terá sido modificado para incluir essas dependências. 65 | 66 | A próxima etapa é configurar o arquivo `./.angular-cli.json` para incluir os dessas dependências: 67 | 68 | ```js 69 | { 70 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 71 | "project": { 72 | "name": "Angular Escola" 73 | }, 74 | "apps": [ 75 | { 76 | "root": "src", 77 | ... 78 | "styles": [ 79 | "styles.css", 80 | "../node_modules/bootstrap/dist/css/bootstrap.css", 81 | "../node_modules/font-awesome/css/font-awesome.css" 82 | ], 83 | ... 84 | } 85 | } 86 | ], 87 | ... 88 | } 89 | ``` 90 | 91 | Entendendo que esse arquivo representa um objeto JSON, a modificação está em incluir duas linhas no array `apps.styles` indicando os arquivos de estilos do **bootstrap** e do **font-awesome**, respectivamente. 92 | 93 | Na sequência, antes de mostrar algumas das alterações realizadas no Template, a figura a seguir ilustra o software em execução no browser. 94 | 95 | ![Resultado das melhorias na interface em execução no browser](/assets/software-interface-bootstrap-browser.png) 96 | 97 | As modificações estéticas são perceptíveis: utilização de fontes, cores, ícones, utilização de mais espaço de tela, distribuindo melhor os elementos visuais. 98 | 99 | São várias as modificações no Template, começando pela inclusão de todo o conteúdo dentro de um elemento `div` com a classe CSS `container`, que é uma das mais básicas do **bootstrap**. 100 | 101 | ```html 102 |
    103 | ... 104 |
    105 | ``` 106 | 107 | Na sequência, o elemento table também passa por melhorias: 108 | 109 | ```html 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 120 | 125 | 135 | 136 | 137 |
    DisciplinaAções
    121 | 123 | {{disciplina.nome}} 124 | 126 | 130 | 134 |
    138 | ``` 139 | 140 | As classes CSS `table`, `table-hover` e `table-bordered` são aplicadas no elemento `table`. 141 | 142 | A classe `thead-light` é aplicada no elemento `thead`. 143 | 144 | A classe `table-active` é aplicada no elemento `tr` filho de `tbody` quando a disciplina sendo editada \(atributo `editando`\) é igual à `disciplina` da iteração do `*ngFor`. 145 | 146 | São adicionados ícones aos botões utilizando classes CSS do **font-awesome**: 147 | 148 | * no botão "Excluir": o ícone da leixeira é adicionado por meio de `` 149 | * no botão "Editar": o ícone do lápis é adicionado por meio de `` 150 | 151 | Há outras melhorias, como a apresentação de notificações das operações de excluir, cadastrar e editar, que também usam recursos do bootstrap e requerem uma alteração na lógica do Controller, mas vou deixar como exercício para você mesmo perceber isso. 152 | 153 | Esse capítulo finaliza a seção "Conceitos iniciais". Para concluir, um resumão dos conceitos vistos até agora: 154 | 155 | * o Angular é um **Framework** front-end 156 | * há vários elementos na Arquitetura do Angular que determinam a forma de desenvolver software Angular, dentre eles os conceitos de **Model, View \(Template\) e Controller \(View-Model\)** 157 | * o **Angular CLI** é uma ferramenta de linha de comando que auxilia e torna mais rápido o desenvolvimento com Angular 158 | * o Angular possui uma sintaxe própria para interpretar os conteúdos dos Templates \(**é como um HTML++**\) 159 | * o recurso de **interpolação** em Templates é muito útil e provavelmente um dos mais utilizados 160 | * as diretivas `*ngFor` e `*ngIf` são fundamentais em Templates para gerar conteúdo para o browser 161 | * a integração entre Template e Controller fornecida pelo **Data binding** é um recurso essencial 162 | * o recurso de **eventos** permite interação com o usuário 163 | * utilizar **bootstrap e font-awesome** permite melhorar a estética da interface gráfica 164 | 165 | É isso aí! Até a próxima! 166 | 167 | -------------------------------------------------------------------------------- /conceitos-iniciais/primeiro-teste.md: -------------------------------------------------------------------------------- 1 | # Iniciando com teste de software 2 | 3 | > **\[info\] Objetivos do capítulo:** 4 | > 5 | > * apresentar o conceito de teste de software 6 | > * demonstrar como escrever testes com o Angular utilizando Karma e Jasmine 7 | > 8 | > **Branch**: [iniciando](https://github.com/jacksongomesbr/angular-escola/tree/iniciando) 9 | 10 | 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. 11 | 12 | Uma forma de avaliar a qualidade do software utilizando teste de software é utilizar mecanismos que permitam identificar se o software está operando como esperado, fazendo o que deveria fazer. 13 | 14 | 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](https://jasmine.github.io/index.html). 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: 15 | 16 | * [Karma](https://karma-runner.github.io/1.0/index.html), uma ferramenta utilizada para executar os testes escritos em Jasmine \(por definição, Karma é um **test runner**\); e 17 | * [Protractor](http://www.protractortest.org), um framework para testes **end-to-end** \(e2e\). 18 | 19 | Então, um trecho do arquivo `src/app/app.component.spec.ts`: 20 | 21 | ```typescript 22 | import { TestBed, async } from '@angular/core/testing'; 23 | import { AppComponent } from './app.component'; 24 | describe('AppComponent', () => { 25 | ... 26 | })); 27 | it('should create the app', async(() => { 28 | ... 29 | })); 30 | it(`should have as title 'Angular'`, async(() => { 31 | ... 32 | })); 33 | it('should render title in a h1 tag', async(() => { 34 | ... 35 | })); 36 | }); 37 | ``` 38 | 39 | 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`\). 40 | 41 | 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: 42 | 43 | * o nome da suíte de testes; e 44 | * uma função que define suítes internas e specs. 45 | 46 | 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. 47 | 48 | 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: 49 | 50 | * o nome da spec \(na prática, uma descrição do que a spec está testando\); 51 | * uma função que descreve o código do teste; e 52 | * um número que representa o tempo limite de execução do teste \(**timeout**\). 53 | 54 | No caso do exemplo há três specs: 55 | 56 | * `should create the app` \(deveria criar o app -- ou o app deveria ser criado\) 57 | * `should have title as 'Angular'` \(deveria ter o título como 'Angular' -- ou o título deveria ser 'Angular'\) 58 | * `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\) 59 | 60 | 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: 61 | 62 | ``` 63 | npm test 64 | ``` 65 | 66 | Esse comando executa o script **"test"** definido no arquivo `package.json`. Na prática, ele executa o comando `ng test`. 67 | 68 | Dentro de instantes você deveria ver uma janela do browser mostrando algo semelhante ao da figura a seguir. 69 | 70 | ![Janela do browser demonstrando o resultado dos testes iniciais](/assets/primeiro-teste-browser-inicial.png) 71 | 72 | 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: 73 | 74 | * versão do sistema operacional \(Windows 10\) 75 | * versão do browser \(Chrome 63\) 76 | * versão do Karma \(1.7.1\) 77 | * versão do Jasmine \(2.6.4\) 78 | * tempo total de execução dos testes \(0.187s\) 79 | * quantidade de specs \(3\) 80 | * quantidade de falhas é zero \(0\), o que indica que todos os testes passaram \(ou que não houve falha nos testes\) 81 | 82 | 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: 83 | 84 | ``` 85 | 15 12 2017 01:56:31.937:WARN [karma]: No captured browser, open http://localhost:9876/ 86 | 15 12 2017 01:56:32.381:INFO [Chrome 63.0.3239 (Windows 10 0.0.0)]: Connected on socket ... 87 | Chrome 63.0.3239 (Windows 10 0.0.0): Executed 0 of 3 SUCCESS (0 secs / 0 secs) 88 | Chrome 63.0.3239 (Windows 10 0.0.0): Executed 1 of 3 SUCCESS (0 secs / 0.101 secs) 89 | Chrome 63.0.3239 (Windows 10 0.0.0): Executed 2 of 3 SUCCESS (0 secs / 0.143 secs) 90 | Chrome 63.0.3239 (Windows 10 0.0.0): Executed 3 of 3 SUCCESS (0 secs / 0.184 secs) 91 | Chrome 63.0.3239 (Windows 10 0.0.0): Executed 3 of 3 SUCCESS (0.092 secs / 0.184 secs) 92 | ``` 93 | 94 | Essas informações indicam dados adicionais: 95 | 96 | * o primeiro teste executou em 0.101s 97 | * o segundo teste executou em 0.143 - 0.101 = 0.042s 98 | * o terceiro teste executou em 0.184 - 0.143 = 0.041s 99 | 100 | 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: 101 | 102 | ```typescript 103 | import { TestBed, async } from '@angular/core/testing'; 104 | import { AppComponent } from './app.component'; 105 | describe('AppComponent', () => { 106 | beforeEach(async(() => { 107 | TestBed.configureTestingModule({ 108 | declarations: [ 109 | AppComponent 110 | ], 111 | }).compileComponents(); 112 | })); 113 | ... 114 | ``` 115 | 116 | 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 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`. 117 | 118 | ## Primeiro teste 119 | 120 | O trecho de código a seguir apresenta o primeiro teste. 121 | 122 | ```typescript 123 | it('should create the app', async(() => { 124 | const fixture = TestBed.createComponent(AppComponent); 125 | const app = fixture.debugElement.componentInstance; 126 | expect(app).toBeTruthy(); 127 | })); 128 | ``` 129 | 130 | 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: 131 | 132 | 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 133 | 2. acessar a instância do componente \(`fixture.debugElement.componentInstance`\), armazenada na variável `app`; 134 | 3. criar uma expectativa para o teste 135 | 136 | 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**\). 137 | 138 | ## Segundo teste 139 | 140 | O trecho de código a seguir apresenta o segundo teste. 141 | 142 | ```typescript 143 | it(`should have as title 'Angular'`, async(() => { 144 | const fixture = TestBed.createComponent(AppComponent); 145 | const app = fixture.debugElement.componentInstance; 146 | expect(app.title).toEqual('Angular'); 147 | })); 148 | ``` 149 | 150 | 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()`. 151 | 152 | Com isso aprendemos que é possível testar membros de um componente \(atributos e métodos\), o que é muito valioso. 153 | 154 | ## Terceiro teste 155 | 156 | O trecho de código a seguir apresenta o terceiro teste. 157 | 158 | ```typescript 159 | it('should render title in a h1 tag', async(() => { 160 | const fixture = TestBed.createComponent(AppComponent); 161 | fixture.detectChanges(); 162 | const compiled = fixture.debugElement.nativeElement; 163 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to Angular!'); 164 | })); 165 | ``` 166 | 167 | 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()`. 168 | 169 | 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`\). 170 | 171 | Por fim, a expectativa é um pouco mais complexa, pois envolve lidar com o DOM. Para isso ocorre o seguinte: 172 | 173 | 1. encontrar o elemento `h1` no DOM: isso é feito por meio do método `querySelector()`; 174 | 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()`. 175 | 176 | ## You shall not pass! Ou provocando uma falha nos testes 177 | 178 | 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\). 179 | 180 | ![](/assets/primeiro-teste-com-falha.png) 181 | 182 | 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\): 183 | 184 | * o segundo teste falhou porque a string `'Web'` não igual a `'Angular'`; e 185 | * o terceiro teste falhou porque a string `'Welcome to Web!'` não contém `'Welcome to Angular!'`. 186 | 187 | 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`\): 188 | 189 | ```typescript 190 | ... 191 | "scripts": { 192 | ... 193 | "test": "ng test", 194 | ... 195 | }, 196 | ... 197 | ``` 198 | 199 | para \(`ng test --single-run`\): 200 | 201 | ```typescript 202 | ... 203 | "scripts": { 204 | ... 205 | "test": "ng test --single-run", 206 | ... 207 | }, 208 | ... 209 | ``` 210 | 211 | 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\). 212 | 213 | > **\[info\] Resumo do capítulo:** 214 | > 215 | > * testes de software permitem avaliar se o comportamento do software está como o esperado 216 | > * Jasmine é um framework utilizado para escrever testes usando a técnica BDD 217 | > * Karma e Protractor são ferramentas do Angular para executar testes 218 | > * o comando `npm test` é usado para executar testes 219 | > * testes são definidos dentro de suítes 220 | > * uma suíte é criada usando o método `describe()` 221 | > * um teste é criado usando o método `it()` 222 | > * 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 223 | > * testes do Angular envolvem verificar membros do componente \(atributos e método\) e o DOM compilado, que é visualizado pelo cliente no browser 224 | 225 | 226 | 227 | -------------------------------------------------------------------------------- /excluindo-uma-disciplina.md: -------------------------------------------------------------------------------- 1 | # Excluindo uma disciplina 2 | 3 | > **\[info\] Objetivos do capítulo:** 4 | > 5 | > * demonstrar a implementação do requisito "Excluir uma disciplina" 6 | > 7 | > **Branch**: [livro-excluindo-disciplina](https://github.com/jacksongomesbr/angular-escola/tree/livro-excluindo-disciplina) 8 | 9 | Este é um capítulo mais simples. Basicamente os recursos utilizados já foram apresentados, então ele é mais curto. 10 | 11 | Primeiro, um trecho do Controller do `AppComponent`: 12 | 13 | ```typescript 14 | ... 15 | export class AppComponent { 16 | selecionado = null; 17 | nome = null; 18 | descricao = null; 19 | disciplinas = [ 20 | new Disciplina('Língua Portuguesa', '...'), 21 | ... 22 | ]; 23 | 24 | salvar() { 25 | ... 26 | } 27 | 28 | excluir(disciplina) { 29 | if (confirm('Tem certeza que deseja excluir a disciplina "' 30 | + disciplina.nome + '"?')) { 31 | const i = this.disciplinas.indexOf(disciplina); 32 | this.disciplinas.splice(i, 1); 33 | } 34 | } 35 | } 36 | ``` 37 | 38 | 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: 39 | 40 | * encontra o índice da disciplina na lista usando o método `indexOf()` 41 | * remove um elemento da lista usando o método `splice()` 42 | 43 | Agora, então, um trecho do código do Template: 44 | 45 | ```html 46 |

    Disciplinas

    47 |
    48 | 56 | 57 |

    Cadastrar

    58 |

    Use este formulário para cadastrar uma disciplina.

    59 |
    60 | ... 61 |
    62 | ``` 63 | 64 | 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`. 65 | 66 | O resultado da execução do software no browser é ilustrado pela figura a seguir. 67 | 68 | ![Execução do software no browser ilustrando a confirmação do usuário para excluir uma disciplina](/assets/software-excluindo-disciplina-browser.png) 69 | 70 | 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. 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /feature-modules.md: -------------------------------------------------------------------------------- 1 | # Feature-modules 2 | 3 | > **\[info\] Objetivos do capítulo:** 4 | > 5 | > * demonstrar a modularização do software utilizando módulos 6 | > * criando componentes em módulos 7 | > * importando módulos 8 | > * descrevendo rotas em módulos 9 | > 10 | > **Branch**: [livro-modulos-admin-publico-shared](https://github.com/jacksongomesbr/angular-escola/tree/livro-modulos-admin-publico-shared) 11 | 12 | Todos os capítulos anteriores estão convergindo para um ponto: utilizar recursos do Angular para melhorar a arquitetura e estrutura do software. A figura a seguir dá uma ideia mais visual disso. 13 | 14 | ![Arquitetura do software demonstrando interações entre módulos e API](/assets/software-arquitetura-modulos-api.png) 15 | 16 | O funcionamento esperado é o seguinte: 17 | 18 | * a interface gráfica é representada pelo arquivo `src/index.html` 19 | * `AppModule`, que é o **root module**, é apresentado na interface gráfica 20 | * `AppModule` importa os módulos `AdminModule` e `PublicoModule`. 21 | * `AdminModule` e `PublicoModule` importam o `SharedModule` e comunicam-se com a API HTTP REST. 22 | 23 | Na prática, o `AppModule` funciona como um concentrador dos demais módulos, já que ele tem poucas funcionalidades específicas, por isso os demais módulos, que fornecem funcionalidades, são chamados **feature modules:** 24 | 25 | * `AdminModule` é responsável pelas funcionalidades administrativas \(requerem acesso por login e senha\); 26 | * `PublicoModule` contém funcionalidades que não requererem acesso por login e senha. 27 | * `SharedModule` contém funcionalidades que são utilizadas por outros módulos. 28 | 29 | Na sequência o texto demonstra os elementos dessa arquitetura. 30 | 31 | ## Estrutura do software 32 | 33 | Apresentar uma figura ilustrando o diagrama de classes do software até aqui seria inútil por causa da quantidade de arquivos e artefatos do software até então. Entretanto, para ter uma ideia do tamanho e da complexidade do software como criado neste capítulo, as tabelas a seguir apresentam a relação de componentes em cada **feature module **e uma breve descrição de cada um deles. 34 | 35 | **Módulo Admin** 36 | 37 | | Componente | Descrição | 38 | | :--- | :--- | 39 | | `AdminComponent` | o **shell component** do módulo `Admin` | 40 | | `CadastroDeDisciplinaComponent` | fornece um formulário para cadastro de uma disciplina | 41 | | `CadastroDeTurmaComponent` | fornece um formulário para cadastro de uma turma | 42 | | `DisciplinaComponent` | fornece a página inicial de uma disciplina, com dados para leitura ou consulta | 43 | | `HomeComponent` | fornece a página inicial do módulo `Admin` | 44 | | `ListaDeDisciplinasComponent` | fornece uma lista das disciplinas e dá acesso a outras funcionalidades: cadastrar, excluir e editar | 45 | | `ListaDeTurmasComponent` | fornece uma lista das turmas e dá acesso a outras funcionalidades: cadastrar, excluir e editar | 46 | | `PaginaNaoEncontradaComponent` | fornece uma página para ser apresentada quando uma URL não combinar com alguma das rotas do módulo `Admin` | 47 | | `TurmaComponent` | fornece a página inicial de uma turma, com dados para leitura ou consulta | 48 | | `AdminModule` | o módulo, em si | 49 | | `AdminRoutingModule` | o módulo de rotas | 50 | | `Disciplina` | o model de disciplina | 51 | | `DisciplinasService` | o serviço com a lógica de negócio para o gerenciamento de disciplinas | 52 | | `Turma` | o model de turma | 53 | | `TurmasService` | o serviço com a lógica de negócio para o gerenciamento de turmas | 54 | 55 | **Módulo Público** 56 | 57 | | Componente | Descrição | 58 | | :--- | :--- | 59 | | LoginComponent | fornece um formulário para autenticação e acesso ao sistema | 60 | | PublicoComponent | o **shell component** do módulo `Publico` | 61 | | PublicoModule | o módulo, em si | 62 | | PublicoRoutingModule | o módulo de rotas | 63 | 64 | **Módulo Shared** 65 | 66 | | Component | Descrição | 67 | | :--- | :--- | 68 | | AuthService | o serviço com a lógica para autenticação do usuário | 69 | | SharedModule | o módulo, em si | 70 | 71 | ## Estrutura de Navegação 72 | 73 | Conforme o software aumenta na quantidade de componentes e funcionalidades torna-se interessante uma visão geral da navegação -- até mesmo porque estamos utilizando rotas. A figura a seguir ilustra a navegação entre as telas \(ou páginas\) do software. 74 | 75 | ![Estrutura do site na forma de um mapa de navegação](/assets/feature-modules-navegacao.png) 76 | 77 | Essa figura permite compreender que a primeira tela acessada é a tela de login. A partir dela o usuário tem acesso à tela home e às demais telas, como disciplinas e turmas. A partir dessas telas outras podem ser acessdas, como a página da disciplina ou a página da turma. As setas são bidirecionais para indicar os caminhos são de ida e volta. 78 | 79 | ## Criando módulos 80 | 81 | Para criar os elementos da arquitetura vamos utilizar o Angular CLI. Para começar, vamos criar os **feature modules**. Para fazer isso execute a linha de comando a seguir. 82 | 83 | ``` 84 | ng g m Admin --routing true 85 | ``` 86 | 87 | O comando cria o módulo `Admin`, na pasta `src/app/admin`, e os arquivos: 88 | 89 | * `src/app/admin/admin.module.ts`: representa o módulo em si 90 | * `src/app/admin/admin-routing.module.ts`: representa o **módulo de rotas** 91 | 92 | O capítulo anterior demonstrou como trabalhar com o recurso de rotas no Angular. Entretanto o capítulo utilizou o formato de descrever as rotas no próprio módulo `AppModule`. Aqui neste capítulo, como há mais módulos, cada módulo possui, também, um **módulo de rotas**. Um módulo de rotas é um módulo que tem uma única responsabilidade: definir as rotas do módulo. Esse módulo será importado no **feature module** para que o Angular identifique corretamente as rotas e os seus componentes associados. O comando anterior especifica que deve ser criado um módulo de rotas por meio da opção `--routing true`. 93 | 94 | ## Criando componentes nos módulos 95 | 96 | Para criar componentes em um módulo utilizamos o Angular CLI, usando a linha de comando a seguir: 97 | 98 | ``` 99 | ng g m Admin/Admin -m Admin --spec false 100 | ``` 101 | 102 | O comando cria o componente `Admin`, na pasta `src/app/admin/admin` e os arquivos do componente. Usar o `Admin/Admin` é uma forma de indicar para o Angular CLI que o componente `Admin` pertence ao módulo `Admin`. A opção `-m` é uma forma de garantir isso ainda mais. Além disso o comando também modifica o `AdminModule`, inserindo o AdminComponent no array `declarations`. 103 | 104 | No padrão adotado neste livro todo **feature module** possui um componente de mesmo nome que é usado como **shell component** -- porque continuamos a usar o recurso de rotas. 105 | 106 | Os demais componentes são criados de forma semelhante. Por exemplo, a criação do componente CadastroDeDisciplina: 107 | 108 | ``` 109 | ng g m Admin/CadastroDeDisciplina -m Admin --spec false 110 | ``` 111 | 112 | A linha de comando cria o componente `CadastroDeDisciplina` na pasta `src/app/admin/cadastro-de-disciplina` e modifica o `AppModule` para incluir esse componente no array `declarations`. 113 | 114 | ## Definindo rotas 115 | 116 | Com a utilização de feature modules o recurso de rotas ganha uma utilidade ainda mais marcante e evidente. O fundamento adotado pelo Angular é que cada **feature module** pode possuir seu **módulo de rotas**. Posteriormente, o **root module** poderá importar o **feature module** que desejar. 117 | 118 | O **módulo de rotas** para o módulo `Admin`, chamado `AdminRouting`, contém um conteúdo semelhante ao seguinte \(omitidas linhas com `import`\): 119 | 120 | ```typescript 121 | ... 122 | const routes: Routes = [ 123 | { 124 | path: 'admin', component: AdminComponent, children: [ 125 | {path: 'disciplinas', component: ListaDeDisciplinasComponent}, 126 | {path: 'disciplinas/:id', component: DisciplinaComponent}, 127 | {path: 'disciplinas/:id/novo', component: CadastroDeDisciplinaComponent}, 128 | {path: 'disciplinas/:id/editar', component: CadastroDeDisciplinaComponent}, 129 | {path: 'cadastrar-turma', component: CadastroDeTurmaComponent}, 130 | {path: 'turmas', component: ListaDeTurmasComponent}, 131 | {path: 'turmas/:id', component: TurmaComponent}, 132 | {path: '', component: HomeComponent}, 133 | {path: '**', component: PaginaNaoEncontradaComponent} 134 | ]} 135 | ]; 136 | 137 | @NgModule({ 138 | imports: [RouterModule.forChild(routes)], 139 | exports: [RouterModule] 140 | }) 141 | export class AdminRoutingModule { 142 | } 143 | ``` 144 | 145 | Aqui o array `routes` ganha uma característica diferenciada: o primeiro elemento, cujo atributo `path` tem valor `'admin'`, está relacionado ao componente `AdminComponent` e possui o atributo `children`, que é um array de rotas. O recurso que permite definir esses tipos de rotas é chamado de **rotas filhas**. Assim, a rota `'admin'` possui várias **rotas filhas**. Além disso essa é a forma de indicar para o Angular que o `AdminComponent` deve ser usado como **shell component**, ou seja, o Template dele contém o elemento `router-outlet`. 146 | 147 | Note também que há uma mudança na forma de indicar as rotas para os metadados do módulo: o atributo `imports` contém uma chamada para `RouterModule.forChild()`, que recebe como parâmetro o array `routes`. Esse é uma diferença marcante para a forma de indicar as rotas para o **root module**, quando é utilizado o método `RouterModule.forRoot()`. 148 | 149 | Outro passo necessário é importar o módulo de rotas no feature module. A seguir, um trecho do AdminModule: 150 | 151 | ```typescript 152 | ... 153 | @NgModule({ 154 | imports: [ 155 | CommonModule, 156 | FormsModule, 157 | HttpClientModule, 158 | NgbModule, 159 | AdminRoutingModule, 160 | SharedModule 161 | ], 162 | ... 163 | }) 164 | export class AdminModule { 165 | } 166 | ``` 167 | 168 | O penúltimo elemento do array `imports` é o módulo de rotas \(`AdminRoutingModule`\). 169 | 170 | O módulo de rotas para o módulo `Publico`, chamado `PublicoRouting`, contém um conteúdo semelhante ao seguinte: 171 | 172 | ```typescript 173 | ... 174 | const routes: Routes = [ 175 | { 176 | path: '', component: PublicoComponent, children: [ 177 | {path: '', component: LoginComponent} 178 | ] 179 | } 180 | ]; 181 | 182 | @NgModule({ 183 | imports: [RouterModule.forChild(routes)], 184 | exports: [RouterModule] 185 | }) 186 | export class PublicoRoutingModule { 187 | } 188 | ``` 189 | 190 | O recurso de rotas filhas também é utilizado aqui. `PublicoComponent` é o shell component. O fato interessante é que o atributo `path` contém o mesmo valor `''` nas duas rotas. Como o Angular considera `''` a rota padrão, isso quer dizer que o `PublicoModule` tem maior prioridade no momento em o Angular procurar encontrar uma rota com base na URL. 191 | 192 | ## Incluindo as rotas dos feature modules no root module 193 | 194 | O último passo desse processo é importar os **feature modules** no **root module**. A seguir, um trecho do `AppModule`: 195 | 196 | ```typescript 197 | ... 198 | @NgModule({ 199 | imports: [ 200 | BrowserModule, 201 | NgbModule.forRoot(), 202 | FormsModule, 203 | HttpClientModule, 204 | AdminModule, 205 | PublicoModule, 206 | AppRoutingModule 207 | ], 208 | ... 209 | }) 210 | export class AppModule { 211 | } 212 | ``` 213 | 214 | Os módulos `AdminModule`, `PublicoModule` e `AppRoutingModule` são importados por meio do atributo `imports` dos metadados da classe `AppModule`. Como os dois primeiros importam seus respectivos módulos de rotas, o root module também tem conhecimento delas e, assim, consegue aplicar o mesmo processo anterior de procura de rotas quando o browser fornece uma URL. 215 | 216 | Um aspecto importante é que a importação segue uma ordem. Veja que, primeiro, é importado o módulo `AdminModule`, depois o `PublicoModule` e, por fim, o `AppRoutingModule`. Como você viu no capítulo anterior o processo de procura de rotas percorre uma lista de rotas do início até encontrar uma rota correspondente. Nesse caso a ordem das importações dos módulos quer dizer para o Angular considerar, primeiro, as rotas do `AdminModule`. Isso é muito importante porque as rotas do `PublicoModule`, por serem a rota padrão, devem estar no final do processo de procura de rotas. 217 | 218 | ## Shared modules 219 | 220 | O Angular utiliza o conceito de **shared modules** para indicar módulos que fornecem recursos que são usados por outros. Conforme a arquitetura do software até o momento o serviço `AuthService` implementa a lógica de negócio de autenticação. Para ele poder ser usado nos módulos `AdminModule` e `PublicoModule` esse elemento do software deve estar em um módulo compartilhado. Se ele estivesse, por exemplo, no módulo `PublicoModule` e o `AdminModule` o importasse isso geraria problemas de ordem ou ciclos de importação. 221 | 222 | Assim, a solução é isolar o AuthService em outro módulo, no caso o **SharedModule**. Na prática, um **shared module** é também um **feature module**, mas, provavelmente, ele apenas fornecerá recursos para serem utilizados em outros **feature modules**. 223 | 224 | Posteriormente, cada **feature module** importa o **shared module** quando precisar utilizar seus recursos. 225 | 226 | ## Fluxo de trabalho 227 | 228 | O fluxo de trabalho do capítulo anterior acaba de ser atualizado -- e também aumentou em complexidade. Da mesma forma como antes, seguir os passos do processo também ajuda a compreendê-lo: 229 | 230 | 1. **criar feature module**: essa agora é a primeira coisa a fazer; 231 | 2. **criar componente**: crie os componentes do **feature module**. Continue conduzido esse processo de forma iterativa, não se preocupe em construir o componente por inteiro, deixe-o como criado pelo Angular CLI; 232 | 3. **definir rotas**: depois, defina as rotas no **módulo de routas**; 233 | 4. **implementar a lógica de negócio em um serviço**: utilize serviços para implementar a lógica de negócio; e 234 | 5. **implementar lógica do componente e usar o serviço**: volte a trabalhar com cada compomente. 235 | 236 | 237 | 238 | -------------------------------------------------------------------------------- /interacao-entre-componentes.md: -------------------------------------------------------------------------------- 1 | # Interação entre componentes 2 | 3 | > Objetivos do capítulo: 4 | > 5 | > * demonstrar a modularização do código utilizando componentes 6 | > * demonstrar a integração entre componentes usando input e output 7 | > 8 | > **Branches**: [livro-componentes-lista](https://github.com/jacksongomesbr/angular-escola/tree/livro-componentes-lista) e [livro-componentes-editor](https://github.com/jacksongomesbr/angular-escola/tree/livro-componentes-editor). 9 | 10 | Uma vez que a quantidade de funcionalidades em um componente começa a aumentar, prejudicando, por exemplo, a legibilidade e a organização do código-fonte, o componente torna-se um candidato a passar por um processo de modularização. 11 | 12 | O `AppComponent`, no momento, fornece as funcionalidades de CRUD de disciplinas, ou seja: 13 | 14 | * Listar disciplinas 15 | * Cadastrar disciplinas 16 | * Editar disciplinas 17 | * Excluir disciplinas 18 | 19 | Como alternativa ao fato de haver um só componente realizando todas as essas funcionalidades vamos utilizar o conceito de componentes do Angular e criar níveis de interações entre eles. 20 | 21 | ## Criando componentes 22 | 23 | O **Angular CLI** é a ferramenta ideal para criar conteúdo para o software, já vimos isso quando iniciamos o próprio projeto a partir de uma linha de código. Outro comando importante é o **generate** \(cujo atalho é **g**\). Ele é usado para criar conteúdo como componentes, módulos e serviços. Para cada tipo de comando há uma opção, como **c**, atalho para **component**. 24 | 25 | Vamos começar pelo componente `ListaDeDisciplinas`, que implementa a funcionalidade de listar disciplinas. Para criá-lo use a linha de comando: 26 | 27 | ``` 28 | ng g c ListaDeDisciplinas --spec false 29 | ``` 30 | 31 | O comando cria o diretório `./src/app/lista-de-disciplinas` e, dentro dele, os arquivos necessários para o componente, ou seja CSS, Template e Controller. 32 | 33 | ## Componente lista de disciplinas 34 | 35 | O componente **ListaDeDisciplinas** é um tipo de componente diferente do que vimos até agora: ele não funciona sozinho. Isso quer dizer que ele precisa ser inserido em outro componente para funcionar. No nosso contexto, o componente **App** inclui e utiliza o componente **ListaDeDisciplinas**. Vamos chamar o componente **App** de **host** \(hospedeiro\) e o componente **ListaDeDisciplinas** de **guest** \(hóspede\). Nessa comunicação o **host** deve fornecer recursos necessários para o **guest** funcionar, o que chamamos **input** \(entrada\). Por outro lado, quando o **guest** notifica o **host** sempre que fizer qualquer coisa importante, o que chamamos **output **\(saída ou evento\). 36 | 37 | Pode ser que não tenha te ocorrido, mas é uma analogia bem interessante. Imagine que você está hospedando um convidado na sua casa. Você é o host, enquanto seu convidado é o guest. Sua casa é onde você irá receber seu convidado. O convidado precisa de um lugar para dormir, então você fornece para ele uma cama \(input\). Seu convidado te notifica quer ir embora \(output\), então você se despede dele e o encaminha até a saída. Pensou naquele convidado exigente \(chato\)? Com um componente guest é a mesma coisa: muita necessidade e muita notificação! 38 | 39 | A figura a seguir ilustra o componente **ListaDeDisciplinas**. 40 | 41 | ![Ilustração do componente ListaDeDisciplinas no browser](/assets/intermediario-componente-listadedisciplinas-browser.png) 42 | 43 | O componente apresenta uma lista de disciplinas. Cada item da lista contém: 44 | 45 | * o nome da disciplina 46 | * um botão para excluir a disciplina 47 | * um botão para editar a disciplina 48 | 49 | Os dois botões não executam as ações correspondentes \(excluir ou editar\) porque isso não é responsabilidade desse componente. O que o componente faz é **notificar o** **host** que o usuário deseja excluir ou editar determinada disciplina. Além disso o componente, ao apresentar a lista das disciplinas, precisa saber qual disciplina está sendo editada, para que destaque essa disciplina na lista \(perceba, na figura, o ícone do lápis e o fundo da linha com a cor cinza na disciplina "Língua Portuguesa"\). Assim: 50 | 51 | * **input**: a lista das disciplinas e a disciplina que está sendo editada; e 52 | * **output**: a disciplina que o usuário deseja excluir ou editar. 53 | 54 | O **host** utiliza \(ou recebe\) **guest**, então ele deve fornecer os inputs requeridos. Além disso, o host também pode realizar alguma ação quando for notificado de que o usuário deseja realizar uma exclusão ou uma edição de uma disciplina. 55 | 56 | A figura a seguir ilustra essa relação entre os dois componentes. 57 | 58 | ![Diagrama demonstrando a interação entre os componentes App e ListaDeDisciplinas](/assets/interacao-componentes-app-listadedisciplinas.png) 59 | 60 | O diagrama apresentado pela figura demonstra as conexões entre os componentes: 61 | 62 | * **App **fornece as entradas para **ListaDeDisciplinas** 63 | * os atributos `disciplinas` e `editando` de `AppComponent` são vinculados aos **input** `disciplinas` e `editando` do `ListaDeDisciplinasComponent` 64 | * **ListaDeDisciplinas** notifica **App** quando o usuário desejar excluir ou editar uma disciplina 65 | * o método `editar()` do `AppComponent` é executado quando ocorrer o output `onEditar` 66 | * o método `excluir()` do `AppComponent` é executado quando ocorrer o output `onExcluir` 67 | 68 | > **\[info\] O diagrama dos componentes** 69 | > 70 | > A UML possui um diagrama para representar componentes de um software e as relações entre eles. Entretanto, por ser uma representação gráfica mais formal, requer que entradas e saídas sejam interfaces. Aqui adotamos uma simplificação desse diagrama para suportar o conceito de Input e Output do Angular. 71 | 72 | Uma observação importante: os nomes dos atributos das classes \(host e guest\) não precisam ser os mesmos. 73 | 74 | Antes de utilizar o componente **ListaDeDisciplinas** no **App**, vamos ver detalhes da sua composição. Primeiro, o Controller: 75 | 76 | ```typescript 77 | import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; 78 | 79 | @Component({ 80 | selector: 'app-lista-de-disciplinas', 81 | ... 82 | }) 83 | export class ListaDeDisciplinasComponent implements OnInit { 84 | @Input() 85 | disciplinas = null; 86 | 87 | @Input() 88 | editando = null; 89 | 90 | @Output() 91 | onEditar = new EventEmitter(); 92 | 93 | @Output() 94 | onExcluir = new EventEmitter(); 95 | 96 | constructor() { 97 | } 98 | 99 | ngOnInit() { 100 | } 101 | 102 | excluir(disciplina) { 103 | this.onExcluir.emit(disciplina); 104 | } 105 | 106 | editar(disciplina) { 107 | this.onEditar.emit(disciplina); 108 | } 109 | } 110 | ``` 111 | 112 | Atenção para os atributos `disciplinas` e `editando`. Ambos são anotados com `@Input()`, o que indica para o Angular tratá-los como **input**. Os atributos `onEditar` e `onExcluir` são anotados com `@Output()`, o que indica para o Angular tratá-los como **output**. Além disso, perceba também mudanças na linha do `import`. 113 | 114 | Na prática, os atributos `onEditar` e `onExcluir` definem eventos, por isso são inicializados com uma instância de `EventEmitter`, a classe do Angular para representar eventos. Note, na instanciação de `EventEmitter`, que o código define o tipo de dados do evento utilizando a notação de diamante: ``. Isso é melhor interpretado assim: um evento pode carregar consigo alguma informação \(dados\) e, para isso, é importante indicar o tipo desses dados. 115 | 116 | Os métodos `excluir(disciplina)` e `editar(disciplina)` são utilizados, respectivamente, como tratadores de eventos para quando o usuário clicar nos botões correspondentes, escolhendo excluir ou editar uma disciplina. O importante aqui é que, como estamos utilizando eventos \(**output**\), não queremos que seja responsabilidade desse componente **ListaDeDisciplinas** realizar essas tarefas. Pelo contrário, o esperado é notificar o **host** de que isso aconteceu. Para indicar para o **host** que uma disciplina deve ser excluída é usada a chamada para o método `this.onExcluir.emit(disciplina)`. Isso fará um "disparo" do evento, notificando o **host** de que ele ocorreu, e incluirá, como dado do evento, a disciplina em questão \(o objeto `disciplina`\). Da mesma forma, para notificar o host que deve editar uma disciplina é usada uma chamada para o método `this.onEditar.emit(disciplina)`. 117 | 118 | O Template do **ListaDeDisciplinas** contém: 119 | 120 | ```html 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 131 | 136 | 146 | 147 | 148 |
    DisciplinaAções
    132 | 134 | {{disciplina.nome}} 135 | 137 | 141 | 145 |
    149 | ``` 150 | 151 | Percebeu semelhança com o Template do capítulo anterior? Isso não é coincidência. Foi realmente isso que aconteceu. Tanto é que a melhor dica para se trabalhar com múltiplos componentes em Angular, pelo menos enquanto você for um dev iniciante, é usar apenas um componente e implementar nele todas as funcionalidades desejadas. Depois, a tarefa é "recortar" partes desse componente e usar em outros componentes. 152 | 153 | A forma pela qual o host incorpora o guest e informa o vínculo com input e output é por meio do Template. Veja o trecho a seguir, do Template do componente **App**: 154 | 155 | ```html 156 | 161 | 162 | ``` 163 | 164 | Primeiro, o elemento `app-lista-de-disciplinas` é utilizado para indicar para o Angular que nesse local deve ser apresentado o componente **ListaDeDisciplinas**. Isso acontece por causa do `selector` do componente **ListaDeDisciplinas** \(veja o Controller desse componente\). 165 | 166 | Segundo, a sintaxe que tem um atributo HTML com nome entre colchetes você já conhece. É a mesma que já utilizamos para definir, por exemplo, a classe CSS de um elemento do HTML com base em uma condição \(ex: o fundo da linha da tabela na cor cinza quando a disciplina está sendo editada\). Então o trecho `[disciplinas]="disciplinas"` indica o uso de Data Binding: o input chamado `disciplinas` está vinculado ao atributo `disciplinas`. Da mesma forma o trecho `[editando]="editando"` usa Data binding para vincular o input `editando` ao atributo `editando`. 167 | 168 | Por fim, a sintaxe que tem um atributo HTML com nome entre parênteses você também já conhece. É a mesma que já utilizamos para tratar eventos \(ex: excluir uma disciplina quando o usuário clicar em um botão associado a ela\). Então o trecho `(onExcluir)="excluir($event)"` indica o uso de um tratador de eventos: o método `excluir()` deve ser chamado passando `$event` como parâmetro quando ocorrer o evento `onExcluir` do componente **ListaDeDisciplinas**. É importante observar que `$event` é uma forma de acessar os dados do evento quando disparado no componente **ListaDeDisciplinas**. Nesse caso, do evento `onEditar`, o dado é a disciplina que deve ser excluída. O mesmo comportamento acontece para o evento `onEditar`. 169 | 170 | A figura a seguir procura resumir todas as informações apresentadas até aqui e procura demonstrar a interação entre os componentes **App** e **ListaDeDisciplinas**. 171 | 172 | ![Interações entre os componentes App e ListaDeDisciplinas](/assets/software-componentes-app-listadedisciplinas.png) 173 | 174 | O repositório [livro-componentes-editor](#) ainda fornece mais um nível de criação de componentes, demonstrando o código para o componente EditorDeDisciplina, que fornece um formulário para cadastro e para edição de uma disciplina. Fica como exercício para fixação da aprendizagem o estudo do código desse repositório. 175 | 176 | > **\[info\] Resumo do capítulo:** 177 | > 178 | > * Utilizamos Angular CLI para criar um componente 179 | > * Um **host** é um componente que utiliza ou inclui um outro, chamado de **guest** 180 | > * Um **guest** não funciona sem ser utilizado em um host 181 | > * O conceito de **Input** é utilizado para o host fornecer entrada para o guest 182 | > * O conceito de **Output** é utilizado para o guest notificar o host de que algo \(um evento\) aconteceu 183 | > * A interação entre componentes é mais um recurso que usa Data binding para vincular entradas do guest a atributos do host 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /nivel-intermediario/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacksongomesbr/desenvolvimento-web-front-end-com-angular/dfb13bb8e141c1accbbf03edabb8a8ac9265f3cc/nivel-intermediario/README.md -------------------------------------------------------------------------------- /rotas.md: -------------------------------------------------------------------------------- 1 | # Rotas 2 | 3 | > **\[info\] Objetivos do capítulo:** 4 | > 5 | > * apresentar o conceito de rotas 6 | > * demonstrar a utilização de rotas para apresentação de componentes 7 | > * demonstrar a diferença da estrutura de software desse capítulo para os capítulos anteriores 8 | > * demonstrar como o Angular implementa e utiliza o conceito de rotas 9 | > * demonstrar a utilização de parâmetros de rota 10 | > 11 | > **Branch**: [livro-rotas](https://github.com/jacksongomesbr/angular-escola/tree/livro-rotas) 12 | 13 | Os capítulos anteriores demonstraram como trabalhar com elementos principais da arquitetura do Angular, principalmente componentes e serviços. Esses elementos têm o objetivo de compor a arquitetura de um software desenvolvido em Angular e, como visto, há vários recursos que permitem a interação entre eles. 14 | 15 | Este capítulo apresenta outro recurso de arquitetura de software desenvolvido em Angular que, na verdade, aumenta a usabilidade do software. 16 | 17 | Antes de apresentar a parte técnica é importante lidar com alguns conceitos. 18 | 19 | ## URI, URL e URN 20 | 21 | **URI** \(_Uniform Resource Identifier_\) é um identificador de um recurso. A sintaxe desse identificador é: 22 | 23 | ``` 24 | scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment] 25 | ``` 26 | 27 | Onde os elementos entre **\[\]** são opcionais e: 28 | 29 | * **scheme**: representa o protocolo ou o contexto; 30 | * **user** e **password**: representam as credenciais do usuário; 31 | * **host**: representa o identificador do dispositivo que contém o recurso; 32 | * **port**: representa o número da porta do dispositivo; 33 | * **path**: representa o caminho do recurso; 34 | * **query**: uma cadeia de caracteres que representa parâmetros de URL, um conjunto de pares `chave=valor` separados por `&`; e 35 | * **fragment**: uma cadeia de caracteres com formato dependente do contexto. 36 | 37 | A figura a seguir ilustra um URI e a sua composição: 38 | 39 | ![](/assets/ilustracao-uri-url.png) 40 | 41 | O URI identifica um recurso disponível na internet, mais especificamente um repositório do Github. Provavelmente você já conhece isso e pode ser que use os termos **endereço** e **URL**. Você não está errado. URL é a forma mais comum de URI na internet. **URL** \(_Uniform Resource Locator_\) é um endereço de um recurso na internet. Além disso, URL é a forma mais comum de criar \_links \_entre páginas web e incorporar uma imagem em uma página web, por exemplo. 42 | 43 | **URN** \(_Uniform Resource Name_\) é o nome de um recurso em um contexto específico. Por exemplo: o ISBN \(_International Standard Book Number_\) de uma edição de "Romeu e Julieta", de William Shakespeare, é 0-486-27557-4; seu URN poderia ser **urn:isbn:0-486-2557-4**. 44 | 45 | Voltando para URL e o contexto da internet, os browsers geralmente fornecem uma **barra de endereço**, por meio da qual o usuário indica qual URL deseja acessar, por exemplo a URL de uma página web. A partir do momento que o browser acessa uma página web ele passa a armazenar o primeiro endereço acessado e os demais endereços que forem acessados por meio de cliques em _links_. 46 | 47 | Esse é, provavelmente, o formato mais intuitivo e o mais utilizado para acessar páginas web. Justamente por isso é necessário repensar a forma como o aplicativo desenvolvido em Angular não apenas entrega conteúdo para o usuário mas também como permite que o usuário o acesse. 48 | 49 | ## Rotas 50 | 51 | Uma rota está diretamente relacionada a URL, ou seja, também funciona como um localizador de um recurso. A diferença é que acrescenta a possibilidade de utilizar **parâmetros de rota**. Por exemplo: considere um site de notícias **noticias.to** que permite acessar a notícia "Governo paga salarios de servidores", cujo identificador é 7899, está na categoria "política" e foi publicada em 20/12/2017, por meio do URL: 52 | 53 | ``` 54 | https://noticias.to/noticias/politica/2017/12/20/governo-paga-salarios-de-servidores/7899 55 | ``` 56 | 57 | Há informações no URL que pertencem à notícia e mudam de uma notíca para outra: 58 | 59 | * **categoria:** politica 60 | * **ano:** 2017 61 | * **mês:** 12 62 | * **dia:** 20 63 | * **titulo:** governo-paga-salarios-de-servidores 64 | * **identificador:** 7899 65 | 66 | Analisando URLs de outras notícias alguém poderia chegar à conclusão de que há um padrão: 67 | 68 | ``` 69 | /noticias/categoria/ano/mes/dia/titulo/identificador 70 | ``` 71 | 72 | Independentemente de possuir parâmetros de rota, uma rota é um **padrão**. Cada uma dessas informações \(categoria, ano, mes, dia, titulo, identificador\), que muda de uma notícia para outra, pode ser representada como um **parâmetro de rota**. 73 | 74 | A implementação desse conceito pode variar entre frameworks, mas provavelmente as mesmas funcionalidades estão disponíveis: 75 | 76 | * definir uma rota \(e, opcionalmente, usar parâmetros de rota\) 77 | * identificar valores dos parâmetros de rota 78 | 79 | Além disso, como URLs são localizadores de recursos, rotas também servem para esse propósito, ou seja, uma rota está associada algum recurso e é uma forma de acessá-lo. 80 | 81 | ## Estrutura do software 82 | 83 | Aqui fazemos uma pequena pausa no assunto de rotas para entender a estrutura do software construído nesse capítulo, ilustrada pela figura a seguir. 84 | 85 | ![Estrutura de classes do software](/assets/rotas-estrutura-classes.png) 86 | 87 | Como mostra a figura a estrutura do software contém: 88 | 89 | * um módulo: `AppModule` 90 | * quatro componentes: `AppComponent`, `HomeComponent`, `ListaDeDisciplinasComponent` e `EditorDeDisciplinasComponent` 91 | * um serviço: `DisciplinasService` 92 | 93 | `AppComponent` é utilizado como componente do **bootstrap** do módulo `AppModule`. 94 | 95 | `HomeComponent` é o primeiro componente visualizado pelo usuário, representando a tela inicial do aplicativo. 96 | 97 | `ListaDeDisciplinasComponent` apresenta a lista de disciplinas e também permite que o usuário exclua uma disciplina e acesse a tela de cadastro e edição de uma disciplina. 98 | 99 | `EditorDeDisciplinaComponent` funciona como um editor de disciplinas, servindo tanto para cadastrar quanto para editar uma disciplina. 100 | 101 | Até o capítulo anterior também havia uma estrutura de classes semelhante a essa. Entretanto, o `AppComponent` continha a lógica de gerenciamento dos dados e incluía os componentes `ListaDeDisciplinasComponent` e `EditorDeDisciplinaComponent`, utilizando **input e output** para entregar e receber dados deles. Aqui neste capítulo, com o recurso de rotas, o resultado é diferente: 102 | 103 | * `AppComponent` não tem a lógica para gerenciar dados de disciplinas 104 | * `ListaDeDisciplinasComponent` não tem input ou output, pois interage diretamente com o `DisciplinasService` 105 | * `EditorDeDisciplinaComponent` não tem input ou ouptut, interage diretamente com o `DisciplinasService` e usa o recurso parâmetros de rota para saber quando é necessário editar uma disciplina \(e qual deve ser editada\) ou cadastrar uma disciplina. 106 | 107 | ## Rotas no Angular 108 | 109 | Controlar a visibilidade de componentes é uma tarefa simples. Isso pode ser consideguido, por exemplo, utilizando as diretivas `ngIf` e `hidden`. Entretanto, a complexidade aumenta na proporção da quantidade de componentes ou da complexidade das interações com o usuário. Por isso o Angular implementa o conceito de rotas de uma maneira bastante útil. 110 | 111 | Para usar rotas é possível definí-las das seguintes formas: 112 | 113 | * no **root module** 114 | * no **feature module** 115 | * no **módulo de rotas** 116 | 117 | Nesse momento vamos usar a primeira abordagem. Modifique o arquivo `./src/app/app.module.ts`: 118 | 119 | ```typescript 120 | ... 121 | import {RouterModule, Routes} from '@angular/router'; 122 | 123 | ... (imports dos componentes) 124 | 125 | const appRoutes: Routes = [ 126 | {path: 'disciplinas', component: ListaDeDisciplinasComponent}, 127 | {path: 'disciplinas/:id', component: EditorDeDisciplinaComponent}, 128 | {path: '', component: HomeComponent,}, 129 | {path: '**', component: PaginaNaoEncontradaComponent} 130 | ]; 131 | 132 | @NgModule({ 133 | declarations: [ 134 | AppComponent, 135 | ListaDeDisciplinasComponent, 136 | EditorDeDisciplinaComponent, 137 | HomeComponent, 138 | PaginaNaoEncontradaComponent 139 | ], 140 | imports: [ 141 | RouterModule.forRoot( 142 | appRoutes, 143 | {enableTracing: true} 144 | ), 145 | ... 146 | ], 147 | providers: [DisciplinasService], 148 | bootstrap: [AppComponent] 149 | }) 150 | export class AppModule { 151 | } 152 | ``` 153 | 154 | A primeira coisa a destacar é a variável `appRoutes`, do tipo `Routes`. O pacote `@angular/router` fornece o módulo `RouterModule` e a classe `Routes`. Na prática a variável `appRoutes` recebe um array no qual cada item é um objeto com dois atributos: 155 | 156 | * `path`: representa a rota para o componente; e 157 | * `component`: representa o componente associado à rota. 158 | 159 | A primeira rota, `disciplinas`, está associada ao componente `ListaDeDisciplinasComponent`. 160 | 161 | A segunda rota, `disciplinas/:id`, está usando um **parâmetro de rota**. A sintaxe do Angular para parâmetro de rotas é usar o sinal de dois pontos seguido do nome do parâmetro. Nesse caso o parâmetro chama-se `id`. A rota está associada ao componente `EditorDeDisciplinaComponent`. 162 | 163 | A terceira e a quarta rotas têm um comportamento especial. A terceira rota é uma string vazia e está associada ao componente `HomeComponent`. Isso significa que essa é a **rota padrão**. A quarta rota é `**`, associada ao componente `PaginaNaoEncontradaComponent`. Isso significa um atalho para o seguinte comportamento: se o usuário fornecer uma rota não definida, leve até o componente `PaginaNaoEncontradaComponent`. 164 | 165 | Por fim, as rotas são inseridas no módulo por meio de um elemento do array `imports` resultante de uma chamada para o método `RouterModule.forRoot()`, passando como parâmetro o array de rotas, o a variável `appRoutes`. 166 | 167 | Quando o software é executado o Angular busca uma combinação entre a URL fornecida no browser e as rotas definidas no módulo. Isso é feito de cima para baixo, procurando no array `appRoutes`. Ao encontrar uma rota correspondente, o Angular cria uma instância do componente e a apresenta. A figura a seguir ilustra esse processo. 168 | 169 | ![Processo de interpretação da URL em busca em uma rota e do seu componente](/assets/ilustracao-procurando-rota.png) 170 | 171 | O modo de comparação entre a URL no browser e o caminho da rota é muito interessante. A forma de comparação considera se o caminho da rota combina com o final da URL do browser. A figura ilustra a busca por uma rota correspondente à URL `http://localhost:4200` e interrompe o processo quando encontra a primeira correspondência. Nesse mesmo sentido se a URL no browser for `http://localhost:4200/disciplinas` o Angular encontra a rota `disciplinas`. E assim por diante. 172 | 173 | Voltando à figura a URL indica o que é conhecido como **raiz do site**. É opcional que a URL termine com `/`. 174 | 175 | Outra característica importante desse processo é a ordem das rotas. Como disse, o Angular procura pela rota que combina com o final da URL, assim, se a rota padrão estiver no início da lista das rotas, então o Angular sempre a encontrará. Da mesma forma, se não encontrar uma rota correspondente o Angular vai até a rota `**`. 176 | 177 | ### Shell component 178 | 179 | Quando o Angular identifica a rota e o componente associado a ela é necessário apresentá-lo \(lembre-se que o componente é, provavelmente, visual\). Assim, ao utilizar rotas o Angular requer que o módulo \(nesse caso o `AppModule`\) utilize um componente como shell, o que significa que o Template do componente precisa indicar onde outros componentes serão apresentados. Para isso o Angular utiliza duas informações: 180 | 181 | * qual o componente o módulo declara como `bootstrap` \(o `AppComponent` é o **shell component**\) 182 | * onde está o elemento `router-outlet` no Template do `AppComponent` 183 | 184 | A figura a seguir ilustra esse processo, considerando que a URL no browser é `http://localhost:4200`. 185 | 186 | ![Utilização do shell component e combinação do seu Template com o Template do componente associado à rota](/assets/ilustracao-rotas-router-outlet-combinacao.png) 187 | 188 | Como mostra a figura, o Angular combina o Template do `AppComponent` com o Template do `HomeComponent` para gerar uma saída HTML \[unificada\] para o browser. Importante perceber que tudo o que está antes e depois do elemento `router-outlet` é mantido na saída. Por isso o Angular usa, em conjunto, a descoberta da rota e do componente associado e o conteúdo do **shell component**. 189 | 190 | ### Parâmetros de rota 191 | 192 | No caso do `EditorDeDisciplinaComponent` há uma lógica no funcionamento do componente que depende de identificar, conforme a rota, se é necessário editar uma disciplina existente \(e qual é essa disciplina\) ou se é necessário cadastrar uma disciplina. Obviamente, nesse caso, o componente executa duas funções \(cadastrar e editar disciplina\). Uma alternativa é utilizar dois componentes, cada qual com uma função diferente. 193 | 194 | A rota `disciplinas/:id`, como já visto, possui um **parâmetro de rota** chamado `id`. Obter o valor desse identificador da disciplina na URL é uma tarefa importante desse processo de lidar com rotas no Angular. Para fazer isso, o componente `EditorDeDisciplinaComponent` possui o atributo `route`, uma instância de `ActivatedRoute`, como mostra o trecho de código a seguir. 195 | 196 | ```typescript 197 | import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; 198 | import {ActivatedRoute, Router} from '@angular/router'; 199 | ... 200 | export class EditorDeDisciplinaComponent implements OnInit { 201 | ... 202 | constructor(private route: ActivatedRoute, 203 | private router: Router, 204 | private disciplinasService: DisciplinasService) { 205 | } 206 | 207 | ngOnInit() { 208 | const id = this.route.snapshot.paramMap.get('id'); 209 | if (id == 'cadastrar') { 210 | this.disciplina = new Disciplina(null, null, null); 211 | this.status = null; 212 | } else { 213 | this.disciplinasService.encontrar(parseInt(id)) 214 | .subscribe(...); 215 | } 216 | } 217 | ... 218 | } 219 | ``` 220 | 221 | Como o componente implementa a interface `OnInit`, o código do método `ngOnInit()` é responsável por: 222 | 223 | * **identificar o valor do parâmetro de rota**: isso é feito por meio de uma chamada para o método `route.snapshot.paramMap.get()`. O parâmetro para o método `get()` é o nome do parâmetro de rota desejado 224 | * **tratar o valor do parâmetro de rota**: o valor retornado por `get()` é do tipo `string`. Se o valor do parâmetro de rota for igual a `'cadastrar'` então o componente opera no modo de cadastro de disciplina; caso contrário, o código faz uma chamada ao método `disciplinasService.encontrar()`, passando como parâmetro o valor do parâmetro de rota convertido para o tipo `number`, usando a função `parseInt()`, então o componente entra no modo de edição de uma disciplina. 225 | 226 | O método `ngOnInit()` é executado pelo Angular no início do processo de instanciar o componente e apresentá-lo na tela. 227 | 228 | ### Navegação 229 | 230 | A navegação é o recurso que permite mudar de uma rota para outra. Isso pode ser feito por meio de uma ação do usuário \(por exemplo, quando ele clica em um link\) ou via código. 231 | 232 | A navegação por meio de link utiliza o elemento `a` e o atributo `routerLink`, como mostra o Template do `HomeComponent`: 233 | 234 | ```html 235 |

    Gerenciador de dados escolares

    236 |

    237 | 238 | Gerenciar disciplinas 239 | 240 |

    241 | ``` 242 | 243 | O atributo `routerLink` tem o valor `/disciplinas`, o que significa que quando o usuário clicar no link ocorrerá uma navegação para a rota `disciplinas`. É importante destacar que, embora a definição da rota não inclua uma barra no início do caminho, o mesmo não acontece aqui, com o link. É como determinar que o link sempre considera a raiz do site \(usando um caminho absoluto\). 244 | 245 | Na lista de disciplinas há um botão que permite editar uma disciplina. No clique do botão há uma chamada para a função `editar()`, cujo trecho de código é apresentado a seguir. 246 | 247 | ```typescript 248 | ... 249 | export class ListaDeDisciplinasComponent implements OnInit { 250 | ... 251 | constructor(private disciplinasService: DisciplinasService, 252 | private router: Router) { 253 | } 254 | editar(disciplina) { 255 | this.router.navigate(['disciplinas', disciplina.id]); 256 | } 257 | } 258 | ``` 259 | 260 | O `ListaDeDisciplinasComponent` possui o atributo `router`, do tipo `Router`. A classe `Router` fornece o método `navigate()`, que é utilizado para fazer a navegação via código. O parâmetro para o método `navigate()` é um array cujos elementos representam partes de uma URL. No caso do método `editar()` os parâmetros são: `'disciplinas'` e `disciplina.id`. O Angular utiliza esses elementos para gerar, por exemplo, a URL `/disciplinas/1` para editar a disciplina com identificador 1. 261 | 262 | ## Padrão de trabalho \(workflow\) 263 | 264 | Lidar com rotas no Angular é um processo simples, mas que aumenta de complexidade na proporção da quantidade de componentes, módulos ou na complexidade da arquitetura do software. Entretanto, lidar com esse processo contém alguns passos padrão \(supondo que o projeto já tenha iniciado e adote a mesma estrutura do Angular CLI\): 265 | 266 | 1. **criar componente**: a primeira coisa a fazer é criar o componente desejado. Como é importante conduzir esse processo de forma iterativa, não se preocupe em construir o componente por inteiro, deixe-o como criado pelo Angular CLI; 267 | 2. **definir rotas**: depois, defina as rotas \(utilizando, por exemplo, o **root module**, como visto neste capítulo\); 268 | 3. **implementar a lógica de negócio em um serviço**: ao utilizar serviços, implemente a lógica de negócio do serviço; e 269 | 4. **implementar lógica do componente e usar o serviço**: por fim, melhore a lógica do componente, fornecendo código para a lógica da interface e da lógica de negócio, e utilize o serviço. 270 | 271 | Adotar esse padrão de trabalho pode tornar o desenvolvimento mais simples e rápido, bem como reduzir a quantidade de erros e permitir a identificação dos erros de forma mais ágil. 272 | 273 | > **\[info\] Resumo do capítulo** 274 | > 275 | > * Rotas servem como recurso para integrar componentes ao mecanismo de navegação do browser, que já é conhecido pelos usuários 276 | > * É possível identificar valores dos parâmetros de rota utilizando a classe `ActivatedRoute` 277 | > * É possível fazer navegação entre as rotas usando o atributo `routerLink` e a classe `Router` 278 | > * O **shell component** é um componente utilizado para fornecer uma estrutura padrão para os componentes de um módulo 279 | > * O **shell component** utiliza o elemento `router-outlet` 280 | > * O Angular combina o Template do shell component e do componente associado a uma rota para gerar uma saída comum para o browser 281 | 282 | 283 | 284 | -------------------------------------------------------------------------------- /servicos.md: -------------------------------------------------------------------------------- 1 | # Serviços 2 | 3 | > Objetivos do capítulo: 4 | > 5 | > * demonstrar a modularização de código utilizando serviços 6 | > 7 | > **Branch**: [livro-services](https://github.com/jacksongomesbr/angular-escola/tree/livro-services) 8 | 9 | Conforme a Arquitetura do Angular a utilização de **Serviços **tem o propósito de organizar o projeto de software Angular, isolando lógica de negócio e separando-a dos Controllers. Não é possível afirmar que seja obrigatório utilizar serviços, mas é muito desejável. 10 | 11 | Na prática não há diferença para o usuário porque, provavelmente, utilizar serviços não afetará diretamente o comportamento da interface. Assim, os benefícios ficam por conta da melhor organização do projeto e da Engenharia de Software para o projeto que está sendo desenvolvido. 12 | 13 | Um serviço é uma classe que pode ser utilizada por outros componentes do projeto. Dois detalhes importantes são: o que diferencia um serviço de outro tipo de componente e como um componente utiliza um serviço. Veremos isso a seguir. 14 | 15 | Até o momento a lógica do CRUD de disciplinas está toda nos Controllers dos componentes. Isso não é uma boa prática, porque é indicado que a lógica dos Controllers seja voltada para gerenciar o comportamento da interface, ou seja, para controlar a interação entre o usuário e o software. Vamos começar criando o serviço DisciplinasService usando Angular CLI e o comando **generate** \(abreviado por **g**\) e a opção **service** \(abreviada por **s**\): 16 | 17 | ``` 18 | ng g s Disciplinas --spec false --module App 19 | ``` 20 | 21 | A linha de comando cria o arquivo `./src/app/disciplinas.service.ts` \(a opção **--spec false** é utilizada para não criar um arquivo de testes, que é o padrão\). A opção **--module App** é utilizada para que o módulo **App** seja modificado para prover o serviço. Um módulo precisa prover ou fornecer um serviço para que ele possa ser utilizado pelos seus componentes. Para isso ocorre uma alteração na declaração do módulo App, como mostra o trecho de código a seguir: 22 | 23 | ```typescript 24 | ... 25 | import { DisciplinasService } from './disciplinas.service'; 26 | 27 | @NgModule({ 28 | declarations: [ 29 | ... 30 | ], 31 | imports: [ 32 | ... 33 | ], 34 | providers: [DisciplinasService], 35 | bootstrap: [AppComponent] 36 | }) 37 | export class AppModule { 38 | } 39 | ``` 40 | 41 | O atributo `providers` está informando para os demais componentes do módulo **App** que o serviço `DisciplinasService` está disponível para uso. 42 | 43 | O Angular CLI cria um serviço operacional, mas não funcional, ou seja, vazio. Entretanto, é bom dar uma olhada no código: 44 | 45 | ```typescript 46 | import { Injectable } from '@angular/core'; 47 | 48 | @Injectable() 49 | export class DisciplinasService { 50 | constructor() { } 51 | } 52 | ``` 53 | 54 | Novamente, um serviço é uma classe. A diferença está na anotação `@Injectable()`. É ela que instrui o Angular a considerar a classe `DisciplinasService` como um serviço -- pois é, não há uma anotação com nome parecido com _@Service\(\)_ =. 55 | 56 | Então, vamos adicionar funcionalidades nesse serviço: 57 | 58 | * retornar a lista de disciplinas 59 | * salvar uma disciplina \(cadastrar ou excluir\) 60 | * excluir uma disciplina 61 | * encontrar uma disciplina 62 | 63 | Na verdade, essas funcionalidades já estão disponíveis no Controller do `AppComponent`, então o trabalho agora é realocar código. Dessa forma, o código do `DisciplinasServices` fica mais ou menos assim: 64 | 65 | ```typescript 66 | import {Injectable} from '@angular/core'; 67 | import {Disciplina} from './disciplina.model'; 68 | 69 | @Injectable() 70 | export class DisciplinasService { 71 | private disciplinas = null; 72 | private novo_id = 1; 73 | 74 | constructor() { 75 | this.disciplinas = [ 76 | new Disciplina(1, 'Língua Portuguesa', 'O ...'), 77 | ... 78 | ]; 79 | } 80 | 81 | todas(): Array { 82 | return this.disciplinas; 83 | } 84 | 85 | salvar(id: number, nome: string, descricao: string): Disciplina { 86 | if (id) { 87 | let d = this.encontrar(id); 88 | if (d) { 89 | d.nome = nome; 90 | d.descricao = descricao; 91 | } else { 92 | throw new Error('Não foi possível encontrar a disciplina'); 93 | } 94 | return d; 95 | } else { 96 | const d = new Disciplina(this.novo_id, nome, descricao); 97 | this.disciplinas.push(d); 98 | this.novo_id++; 99 | return d; 100 | } 101 | } 102 | 103 | excluir(disciplina: number | Disciplina) { 104 | let d = null; 105 | if (typeof(disciplina) === 'number') { 106 | d = this.encontrar(disciplina); 107 | } else { 108 | d = this.encontrar(disciplina.id); 109 | } 110 | if (d) { 111 | const i = this.disciplinas.indexOf(d); 112 | this.disciplinas.splice(i, 1); 113 | } else { 114 | throw new Error('Não foi possível encontrar a disciplina'); 115 | } 116 | } 117 | 118 | encontrar(arg: number | string) { 119 | let d = null; 120 | if (typeof(arg) === 'number') { 121 | d = this.disciplinas.filter(d => d.id === arg); 122 | } else { 123 | d = this.disciplinas.filter(d => d.nome === arg); 124 | } 125 | if (d != null && d.length > 0) { 126 | return d[0]; 127 | } else { 128 | return null; 129 | } 130 | } 131 | } 132 | ``` 133 | 134 | O método `todas()` retorna todas as disciplinas. 135 | 136 | O método `encontrar()` recebe o id da disciplina ou o seu nome, procura o array `disciplinas` e, se encontrar, retorna a disciplina correspondente. Caso contrário retorna `null` para indicar que a disciplina não foi encontrada. Note a expressão usada para definir o tipo do parâmetro `arg`: `number | string`. Isso significa que o método aceita um parâmetro do tipo `number` ou `string`. Note também a utilização do método `filter()` para aplicar um predicado \(representado por uma função seta\) para encontrar um item do array. 137 | 138 | A lógica dos métodos `salvar()` e `excluir()` é semelhante à utilizada anteriormente no `AppComponente`, com a diferença de que não há interação com o usuário. É importante ressaltar também que esses métodos estão disparando exceções \(veja as linhas com `throw new Error(...)`\) que devem ser devidamente tratadas nos componentes que utilizarem esse serviço. 139 | 140 | Já no Controller do `AppComponent` a mudança começa no método construtor: 141 | 142 | ```typescript 143 | constructor(private disciplinasService: DisciplinasService) { 144 | this.disciplinas = this.disciplinasService.todas(); 145 | } 146 | ``` 147 | 148 | Essa sintaxe `private disciplinasService: DisciplinasService` utiliza um recurso importante do Angular chamado _**Dependency Injection**_ \(DI\). Por meio de DI o Angular cria automaticamente uma instância de `DisciplinasService` e a atribui ao atributo privado `disciplinasService`, tornando o serviço disponível no componente. Com isso o código desse Controller fica como o seguinte: 149 | 150 | ```typescript 151 | ... 152 | import {DisciplinasService} from "./disciplinas.service"; 153 | ... 154 | export class AppComponent { 155 | editando = {id: 0, nome: '', descricao: ''}; 156 | ... 157 | disciplinas = null; 158 | 159 | constructor(private disciplinasService: DisciplinasService) { 160 | this.disciplinas = this.disciplinasService.todas(); 161 | } 162 | 163 | salvar() { 164 | try { 165 | const d = this.disciplinasService.salvar(this.editando.id, 166 | this.editando.nome, this.editando.descricao); 167 | this.salvar_ok = true; 168 | this.editando = {id: 0, nome: '', descricao: ''}; 169 | } catch (e) { 170 | this.salvar_erro = true; 171 | } 172 | } 173 | 174 | excluir(disciplina) { 175 | if (this.editando === disciplina) { 176 | alert('Você não pode excluir uma disciplina que está editando'); 177 | } else { 178 | if (confirm('Tem certeza que deseja excluir a disciplina "' 179 | + disciplina.nome + '"?')) { 180 | try { 181 | this.disciplinasService.excluir(disciplina); 182 | this.excluir_ok = true; 183 | this.redefinir(); 184 | } catch (e) { 185 | this.excluir_erro = true; 186 | } 187 | } 188 | } 189 | } 190 | ... 191 | } 192 | ``` 193 | 194 | Perceba como a lógica da interface foi mantida e o código não possui a lógica de manipular o array de disciplinas \(consultar, inserir, excluir e encontrar elementos\), já que isso fica a cargo do serviço `DisciplinasService`. Assim, o componente `AppComponent` utiliza o serviço `DisciplinasService` e vimos como utilizar esse curso para organizar melhor o código e utilizar os padrões de arquitetura do Angular. 195 | 196 | Como o código-fonte do software está crescendo, tanto em termos de volume quanto de arquivos, é interessante adotar uma abordagem que auxilia ainda mais na organização de código: diagramas da UML. A figura a seguir apresenta o diagrama de classes do software até então. 197 | 198 | ![Diagrama de classes do software](/assets/software-diagrama-de-classes-servicos.png) 199 | 200 | O diagrama de classes apresentado pela figura demonstra relações entre alguns elementos do software até então, principalmente com foco nos controllers dos componentes, no serviço `DisciplinasService` e nas relações \(usa\) entre eles. 201 | 202 | -------------------------------------------------------------------------------- /software-de-gerenciamento-escolar.md: -------------------------------------------------------------------------------- 1 | # Software de gerenciamento escolar 2 | 3 | > **\[info\] Objetivo do capítulo:** 4 | > 5 | > * apresentar informações sobre gerenciamento escolar 6 | > * apresentar requisitos do software de gerenciamento escolar 7 | 8 | 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. 9 | 10 | ## Contexto da escola 11 | 12 | No contexto desse livro o gerenciamento escolar é aplicado em uma escola fictícia com as seguintes características: 13 | 14 | * a **estrutura de pessoal \(recursos humanos\)** tem as seguintes características: 15 | * funcionários são pessoas contratadas para ocupar cargos e realizar funções 16 | * um funcionário tem um cargo 17 | * um funcionário tem uma ou mais funções 18 | * um funcionário pode ser: administrativo ou docente 19 | * a **estrutura acadêmica** da escola tem as seguintes características: 20 | * há três níveis de ensino: ensino infantil, ensino fundamental, ensino médio 21 | * cada nível de ensino pode ser organizado em anos de ensino e cada um possui disciplinas 22 | * 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 23 | * uma turma é quando um item do componente curricular é ministrado durante um ano letivo e tem as seguintes características: 24 | * pode ter um ou mais professores por disciplina 25 | * pode ter um ou mais alunos 26 | * matrícula é quando um aluno é matriculado em uma turma e cursa todas as disciplinas do ano letivo 27 | * fisicamente, está presente em uma sala de aula \(sala padrão\) 28 | * 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 29 | * uma aula pode ter participação de um ou mais professores \[da turma\] 30 | * uma aula pode ser realizada na sala de aula padrão da turma ou em instalação física 31 | * uma aula é realizada em um dia da semana, em um horário específico \(informação utilizada para compor a grade horária\) 32 | * em cada aula o professor registra a frequência do aluno \(presença ou falta\) 33 | * cada aula tem a duração de 50 minutos e conta como uma hora \(na Carga Horária\) 34 | * 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 35 | * ao final do ano letivo: 36 | * as quatro notas são utilizadas para compor a nota final, que é uma média aritmética das notas dos bimestres 37 | * o aluno é considerado "APROVADO" na disciplina se tiver média aritmética igual ou superior a 6,0 e frequência superior a 75% 38 | * 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" 39 | * 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\) 40 | * 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 41 | * a **estrutura física** da escola tem as seguintes características: 42 | * uma instalação física pode ser: prédio, sala admnistrativa, laboratório ou sala de aula 43 | 44 | Para ilustrar esse contexto, alguns exemplos de estruturas curriculares: 45 | 46 | ![Exemplo de estrutura curricular do Ensino Médio - 1º ao 5º ano](/assets/escola-estrutura-curricular-1-5.png) 47 | 48 | ![Exemplo de estrutura do Ensino Fundamental - 6º ao 9º ano](/assets/escola-estrutura-curricular-6-9.png) 49 | 50 | ![Exemplo de Estrutura curricular do Ensino Médio](/assets/escola-estrutura-curricular-medio.png) 51 | 52 | ![Exemplo de Estrutura curricular do Ensino Infantil](/assets/escola-estrutura-ensino-infantil.png) 53 | 54 | ## Gerenciamento escolar 55 | 56 | Com base nas características da escola usada nesse contexto, o software de gerenciamento escolar compreende os seguintes requisitos conforme os atores: 57 | 58 | * **gerente do sistema** 59 | * gerenciar o cadastro de usuários e permissões de acesso 60 | * **gerente administrativo** 61 | * gerenciar o cadastro de cargos 62 | * gerenciar o cadastro de funções 63 | * gerenciar o cadastro de funcionários 64 | * gerenciar o cadastro de instalações físicas 65 | * **gerente de ensino** 66 | * gerenciar o cadastro de disciplinas 67 | * gerenciar o cadastro de níveis de ensino 68 | * gerenciar o cadastro de estruturas curriculares 69 | * gerenciar o cadastro de turmas \(bem como professores nas turmas e horários\) 70 | * gerenciar o cadastro de alunos \(geral\) 71 | * gerenciar o cadastro de matrículas \(alunos nas turmas\) 72 | * registrar aprovação ou reprovação excepcional em conselho de classe 73 | * **professor** 74 | * ver as turmas em que ministra disciplinas 75 | * ver os alunos das turmas em que ministra disciplinas 76 | * ver a agenda de aula das turmas em que ministra disciplinas \(turmas, disciplinas, salas de aula, dias da semana e horários de aula\) 77 | * ver a agenda de aula da escola \(todas as turmas do ano letivo\) 78 | * gerenciar o cadastro de frequência dos alunos \(nas suas turmas e disciplinas\) 79 | * gerenciar as notas das avaliações bimestrais dos alunos \(nas suas turmas e disciplinas\) 80 | * **aluno \(e/ou pais ou responsáveis\)** 81 | * ver a estrutura curricular do nível e ano de ensino em que está matriculado 82 | * ver a estrutura curricular de todos os níveis e anos de ensino 83 | * ver a agenda de aula da turma em que está matriculado \(disciplinas, professores, salas de aula, dias da semana e horários de aula\) 84 | * ver o histórico acadêmico \(notas e situação final para as turmas cursadas\) 85 | 86 | Uma descrição mais completa dos requisitos pode ser encontrada aqui: [http://200.199.229.99/reqman/projects/3/documentation.pdf](http://200.199.229.99/reqman/projects/3/documentation.pdf). 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /testando-a-funcionalidade-de-listar-disciplinas.md: -------------------------------------------------------------------------------- 1 | # Testando a funcionalidade de listar disciplinas 2 | 3 | 4 | 5 | 6 | 7 | --------------------------------------------------------------------------------