├── .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 |  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 |  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 |  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 |  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 |  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 |  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 |  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 |  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 |
Use este formulário para cadastrar uma disciplina.
79 | 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 `