Professional Documents
Culture Documents
This is a Leanpub book. Leanpub empowers authors and publishers with the Lean
Publishing process. Lean Publishing is the act of publishing an in-progress ebook using
lightweight tools and many iterations to get reader feedback, pivot until you have the right
book and build traction once you do.
2015 - 2016 Daniel Schmitz e Daniel Pedrinha Georgii
Contedo
1.
Introduo . . . . . . . . . . . . . . . . . .
1.1 Pr requisitos . . . . . . . . . . . . .
1.1.1 Node . . . . . . . . . . . . .
1.1.2 Servidor web . . . . . . . . .
1.1.3 Arquivo package.json . . . .
1.1.4 Erros ao instalar o Angular 2
1.1.5 Arquivo package.json . . . .
1.1.6 Editores de texto e IDEs . . .
1.2 Alm do Javascript . . . . . . . . . .
1.3 TypeScript . . . . . . . . . . . . . . .
1.4 Cdigo fonte . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2
2
2
3
3
5
6
8
9
10
10
2.
TypeScript . . . . . . . . . . . . . . . . . . . . . . .
2.1 Instalando TypeScript . . . . . . . . . . . . .
2.2 Uso do Visual Studio Code . . . . . . . . . . .
2.2.1 Detectando alteraes . . . . . . . . .
2.2.2 Debug no Visual Studio Code . . . . .
2.2.3 Debug no navegador . . . . . . . . . .
2.3 Tipos . . . . . . . . . . . . . . . . . . . . . . .
2.3.1 Tipos Bsicos . . . . . . . . . . . . . .
2.3.2 Arrays . . . . . . . . . . . . . . . . . .
2.3.3 Enum . . . . . . . . . . . . . . . . . .
2.3.4 Any . . . . . . . . . . . . . . . . . . .
2.3.5 Void . . . . . . . . . . . . . . . . . . .
2.4 Classes . . . . . . . . . . . . . . . . . . . . . .
2.4.1 Construtor . . . . . . . . . . . . . . .
2.4.2 Visibilidade de mtodos e propriedades
2.5 Herana . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
11
12
13
25
26
28
29
29
30
30
31
31
31
31
32
32
CONTEDO
2.6
2.7
2.8
2.9
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
34
34
35
36
36
36
36
37
38
38
41
42
46
48
Um pouco de prtica . . . . . . . . . . . . . . . . . . . . . . .
3.1 Projeto AngularBase . . . . . . . . . . . . . . . . . . . .
3.1.1 Configurando o projeto . . . . . . . . . . . . . .
3.1.2 Erros ao instalar o Angular 2 . . . . . . . . . . .
3.1.3 Configurando a compilao do TypeScript . . . .
3.1.4 Criando o primeiro componente Angular 2 . . . .
3.1.5 Criando o bootstrap . . . . . . . . . . . . . . . .
3.1.6 Criando o arquivo html . . . . . . . . . . . . . .
3.2 Criando uma pequena playlist . . . . . . . . . . . . . . .
3.2.1 Estrutura inicial dos arquivos . . . . . . . . . . .
3.2.2 Criando um arquivo de configurao da aplicao
3.2.3 Adicionando bootstrap . . . . . . . . . . . . . . .
3.2.4 Criando a classe Video . . . . . . . . . . . . . . .
3.2.5 Criando uma lista simples de vdeos . . . . . . .
3.2.6 Criando sub-componentes . . . . . . . . . . . . .
3.2.7 Formatando o template . . . . . . . . . . . . . . .
3.2.8 Repassando valores entre componentes . . . . . .
3.2.9 Selecionando um vdeo . . . . . . . . . . . . . . .
3.2.10 Eventos . . . . . . . . . . . . . . . . . . . . . . .
3.2.11 Propagando eventos . . . . . . . . . . . . . . . .
3.2.12 Exibindo os detalhes do vdeo . . . . . . . . . . .
3.2.13 Editando os dados do video selecionado . . . . .
3.2.14 Editando o ttulo . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
49
49
49
49
50
52
53
53
55
56
57
59
61
62
63
65
67
70
70
72
74
78
81
2.10
2.11
2.12
2.13
2.14
3.
Accessors (get/set) . . . . . . . . . . . . . . . .
Mtodos Estticos . . . . . . . . . . . . . . . . .
Interfaces . . . . . . . . . . . . . . . . . . . . .
Funes . . . . . . . . . . . . . . . . . . . . . .
2.9.1 Valor padro . . . . . . . . . . . . . . .
2.9.2 Valor opcional . . . . . . . . . . . . . .
Parmetros Rest . . . . . . . . . . . . . . . . . .
Parmetros no formato JSON . . . . . . . . . .
Mdulos . . . . . . . . . . . . . . . . . . . . . .
2.12.1 Exemplo com Systemjs . . . . . . . . . .
2.12.2 Omitindo arquivos js e map no VSCode .
2.12.3 Uso do SystemJS . . . . . . . . . . . . .
Decorators (ou annotation) . . . . . . . . . . . .
Concluso . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
CONTEDO
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
84
85
86
93
4.
Um pouco de teoria . . . . . . . . . . .
4.1 Viso Geral . . . . . . . . . . . .
4.2 Mdulo (module) . . . . . . . . .
4.2.1 Library Module . . . . . .
4.3 Componente (component) . . . .
4.4 Template . . . . . . . . . . . . .
4.4.1 Interpolation (Uso de {{ }})
4.4.2 Template Expressions . .
4.5 Property Bind . . . . . . . . . . .
4.5.1 Laos . . . . . . . . . . .
4.5.2 Pipes (Operador |) . . . .
4.6 Metadata (annotation) . . . . . .
4.7 Servio (Service) . . . . . . . . .
4.8 Injeo de dependncia . . . . . .
4.8.1 Uso do @Injectable() . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
96
96
97
98
99
100
101
101
101
102
103
103
104
104
106
5.
Formulrios . . . . . . . . . . . . . . . . . . . . . .
5.1 Criando o projeto inicial . . . . . . . . . . . .
5.2 Uso do ngControl . . . . . . . . . . . . . . . .
5.3 Exibindo uma mensagem de erro . . . . . . .
5.4 Desabilitando o boto de submit do formulrio
5.5 Submit do formulrio . . . . . . . . . . . . . .
5.6 Controlando a visibilidade do formulrio . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
108
108
114
116
117
118
119
6.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
122
122
123
128
128
130
131
132
133
3.3
3.4
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
CONTEDO
7.
Routes . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.1 Aplicao AngularRoutes . . . . . . . . . . . . . . .
7.2 Dividindo a aplicao em partes . . . . . . . . . . . .
7.3 Criando a rea onde os componentes sero carregados
7.4 Configurando o router . . . . . . . . . . . . . . . . .
7.5 Criando links para as rotas . . . . . . . . . . . . . . .
7.6 Repassando parmetros . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
135
135
137
138
138
139
140
8.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
142
142
142
146
147
148
149
158
159
9.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
165
165
170
172
172
174
175
176
176
180
183
183
184
185
186
189
191
192
196
CONTEDO
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
201
201
202
204
206
208
209
209
211
211
212
212
216
216
218
CONTEDO
1. Introduo
Esta obra tem como objetivo apresentar o framework Angular, em sua nova verso, na qual foi
totalmente reescrita. Quase todos os conceitos da verso 1 ficaram obsoletos e novas tcnicas
esto sendo utilizadas no Angular 2, com o objetivo de prover um framework mais dinmico
e moderno.
Neste contexto, existem duas situaes em que o leitor se encontra. A primeira definida para
aqueles que j conhecem e usam o Angular, desde a sua verso 1.0, e que esto buscando uma
forma de continuarem o desenvolvimento de seus programas neste framework. A segunda
definida por aqueles que no conhecem o angular, e esto comeando agora o seu estudo.
Para queles que nunca trabalharam com o framework Angular, aprender a verso 2 logo
no incio ser uma tarefa simples, j que conceitos antigos no iro perturbar os novos
conceitos. Para quem conhece Angular 1, preciso de um pouco de pacincia para entender
as extensas mudanas no framework. O ideal que o desenvolvedor pense no Angular
2 como um novo framework, porque quase nenhum dos conceitos da verso anterior so
aproveitados.
1.1 Pr requisitos
Antes de abordarmos o Angular 2 necessrio rever algumas tecnologias que so vitais para
o desenvolvimento de qualquer biblioteca utilizando HTML/Javascript. Primeiro, usaremos
extensivamente o Node, que uma forma de executar Javascript no servidor. O uso do Node
ser vital para que possamos manipular bibliotecas em nosso projeto, que sero instaladas
atravs do gerenciador de pacotes do Node chamado npm.
1.1.1 Node
Se voc utiliza Windows, instale o node atravs do http://www.nodejs.org. Faa o download
e instale o Node e deixe selecionado a opo npm, que o gerenciador de pacotes do node.
Alm do node, til instalar tambm o GIT, disponvel em https://git-scm.com/.
No Linux, podemos usar o gerenciador de pacotes apt-get para instalar tudo que precisamos,
bastando apenas executar o seguinte comando:
http://www.nodejs.org
https://git-scm.com/
Introduo
Aps instalar todos os pacotes necessrios, necessrio criar um link simblico para a palavra
node, conforme o comando a seguir:
$ sudo ln -s /usr/bin/nodejs /usr/bin/node
Neste comando, o parmetro -g indica que o pacote ser instalado globalmente ao sistema.
Desta forma, pode-se utiliz-lo em qualquer parte do sistema de arquivos.
Introduo
$ npm init
Uma srie de perguntas sero feitas sobre o projeto. Pode-se, neste momento, deixar o valor
padro e pressionar enter at o processo terminar. Aps o trmino, um novo arquivo ser
criado no diretrio, com o seguinte texto.
{
"name": "test",
"version": "0.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
Introduo
Introduo
At que a instalao de dependncias pelo npm volte a funcionar corretamente, voc ter que
resolver este problema instalando manualmente as bibliotecas que resultaram em erro. Copie
o nome da biblioteca seguido da sua verso e use o npm novamente para a instalao:
$
$
$
$
$
npm
npm
npm
npm
npm
i es6-promise@^3.0.2 --S
i es6-shim@^0.33.3 --S
i reflect-metadata@0.1.2 --S
i rxjs@5.0.0-beta.2 --S
i zone.js@0.5.15 --S
Perceba que as verses de cada biblioteca podem mudar de acordo com a verso beta-x do
Angular 2. Ento no copie do livro os comandos de instalao, copie dos erros gerados pelo
npm no seu sistema.
Introduo
{
"name": "test",
"version": "0.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"angular2": "^2.0.0-beta.0",
"es6-promise": "^3.0.2",
"es6-shim": "^0.33.13",
"reflect-metadata": "^0.1.2",
"rxjs": "^5.0.0-beta.0",
"zone.js": "^0.5.10"
}
}
Alm da alterao no arquivo package.json, um novo diretrio foi criado chamado node_modules, que contm as bibliotecas do projeto e suas dependncias.
O arquivo package.json tem diversas outras funcionalidades. Por exemplo, na entrada
scripts temos um item chamado test, que a princpio exibe uma mensagem de erro simples.
Poderamos adicionar uma nova entrada chamada start, e iniciarmos o servidor web a partir
dela, conforme o exemplo a seguir:
{
"name": "test",
"version": "0.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "live-server --port=12345"
},
"author": "",
"license": "ISC",
Introduo
"dependencies": {
"angular2": "^2.0.0-beta.0",
"es6-promise": "^3.0.2",
"es6-shim": "^0.33.13",
"reflect-metadata": "^0.1.2",
"rxjs": "^5.0.0-beta.0",
"zone.js": "^0.5.10"
}
}
Para saber mais sobre os tipos de scripts disponveis no arquivo package.json, acesse este
link.
Introduo
Este um exemplo simples de componente no Angular 2. Mesmo sem entender muito o que
est acontecendo, perceba que usamos o comando import para importar o Angular, e que
depois usamos a notao @Component para adicionar caractersticas a classe AppComponent,
criado atravs do comando export class.
Caso deseje escrever este componente utilizando Dart, o cdigo fica:
import 'package:angular2/angular2.dart';
import 'package:angular2/bootstrap.dart';
@Component(selector: 'my-app', template: '<h1>My First Angular 2 App</h1>')
class AppComponent {}
main() {
bootstrap(AppComponent);
}
Introduo
10
(function(app) {
app.AppComponent = ng.core
.Component({
selector: 'my-app',
template: '<h1>My First Angular 2 App</h1>'
})
.Class({
constructor: function() {}
});
})(window.app || (window.app = {}));
1.3 TypeScript
Nesta obra estaremos utilizando formalmente o TypeScript, por ser uma linguagem tipada
e com diversas funcionalidades. Basicamente, o TypeScript uma linguagem com suporte
ao ES6 (ECMAScript 2015, nova especificao do JavaScript), com suporte a annotations e
suporte a tipos (definio de tipos em variveis, parmetros e retorno de mtodos).
Estaremos abordando TypeScript no captulo 2, para que possamos detalhar todas as suas
funcionalidades bsicas. necessrio conhecer o bsico do TypeScript antes de utilizarmos
o Angular 2.
2. TypeScript
Antes de iniciarmos o estudo sobre o Angular 2, preciso abordar a linguagem TypeScript,
que ser extensivamente utilizada nesta obra. O TypeScript pode ser considerado uma
linguagem de programao que contm as seguintes particularidades:
Este cdigo, quando traduzido para o JavaScript, torna-se algo semelhante ao cdigo a seguir:
11
TypeScript
12
TypeScript
13
class User {
fullname : string;
constructor(firstname:string,lastname:string) {
this.fullname = firstname + " " + lastname;
}
hello():string{
return "Hello, " + this.fullname;
}
}
var user = new User("Mary", "Jane");
alert(user.hello());
Aps adicionar o cdigo TypeScript no arquivo com a extenso .ts, execute o seguinte
comando:
$ tsc user.ts
Aps executar o comando tsc ser gerado o arquivo user.js com o seguinte cdigo
javascript:
var User = (function () {
function User(firstname, lastname) {
this.fullname = firstname + " " + lastname;
}
User.prototype.hello = function () {
return "Hello, " + this.fullname;
};
return User;
})();
var user = new User("Mary", "Jane");
alert(user.hello());
TypeScript
14
uma maior compatibilidade com TypeScript. Vamos criar um pequeno projeto chamado
HelloWorldVSCode demonstrando como o uso do Visual Studio Code pode acelerar nosso
desenvolvimento em Angular 2.
Como abordamos no captulo anterior, o VSCode pode ser instalado neste link. O VSCode
um editor do tipo folder based, ou seja, quando ele abre uma pasta ele tenta ler naquela
pasta algum arquivo de projeto (package.json, project.json, tsconfig.json) para adicionar as
funcionalidades de acordo com projeto em questo.
Ou seja, antes de abrir o VSCode devemos, no mnimo, criar uma pasta e criar um arquivo
de projeto. Isto feito facilmente com npm, de acordo com com a imagem a seguir:
https://code.visualstudio.com/
TypeScript
15
Aps a criao do diretrio, abra o Visual Studio Code, acesse File > Open Folder, e escolha
o diretrio recm criado. O VSCode fica semelhante a imagem a seguir:
TypeScript
16
TypeScript
17
Crie o arquivo user.ts com o mesmo cdigo TypeScript anterior, conforme a imagem a
seguir:
TypeScript
18
Precisamos agora usar o conversor do TypeScript para compilar o arquivo .ts em .js. Ao
invs de usar a linha de comando, podemos configurar o prprio VSCode para isso. Primeiro,
crie o arquivo tsconfig.json com o seguinte texto:
/HelloWorldVSCode/tsconfig.json
{
"compilerOptions": {
"target": "ES5",
"module": "amd",
"sourceMap": true
}
}
TypeScript
19
TypeScript
20
Quando selecionamos Configure Task Runner, uma pasta chamada .vscode criada, juntamente com o arquivo tasks.json, conforme a imagem a seguir:
O arquivo tasks.json possui diversas configuraes para diversos tipos de tarefa, mas vamos
TypeScript
21
adicionar somente o bsico para compilar o TypeScript. Apague todo o contedo do arquivo
e adicione a seguinte configurao:
/HelloWorldVSCode/.vscode/tasks.json
{
"version": "0.1.0",
"command": "tsc",
"isShellCommand": true,
"showOutput": "never"
}
Neste arquivo, criamos a tarefa cujo o comando tsc (command). Para executar esta tarefa no
VSCode, pressione CTRL+SHIFT+B, ou ento aperte F1 e digite run task build. O comando
tsc ser executado, o arquivo tsconfig.json ser lido e os arquivos js e map sero criados.
Aps alguns segundos os arquivos user.js e user.js.map sero criados, semelhante a
imagem a seguir:
22
TypeScript
Se os arquivos no forem criados, altere o atributo showOutput para always e verifique o erro em questo. Verifique tambm se o comando tsc est funcionando
na linha de comando. Este comando adicionado ao sistema quando executamos
npm i typescript -g.
TypeScript
23
Esta gerao de cdigo realizada pela integrao do Visual Studio Code com o emmet, que
pode ser consulta neste link.
Agora coloque o cursor antes do </body> e digite script:src:
http://docs.emmet.io/cheat-sheet/
TypeScript
24
Pressione tab e veja que o cdigo gerado foi <script src=""></script>, onde devemos
adicionar o arquivo user.js no atributo src. Aps adicion-lo, podemos finalmente testar a
aplicao no navegador. Selecione o arquivo index.html na lista de arquivos e pressione o
boto direito do mouse, escolha o item Open in terminal:
TypeScript
25
Com o terminal aberto no diretrio HelloWorldVSCode, digite live-server e aguarde a pgina abrir. Quando a pgina abrir, uma mensagem de alert ser exibida. No preciso fechar
o navegador ou reiniciar o live-server para alterar o cdigo TypeScript, bastando apenas
recompilar a aplicao. Por exemplo, no arquivo user.ts, onde est alert(user.hello());
altere para document.body.innerHTML = user.hello(); e depois aperte ctrl+shift+b para
recompilar o arquivo user.ts e alterar a mensagem na tela.
Aps realizar estas configuraes iniciais, o uso do TypeScript torna-se mais dinmico, pois
no ser preciso usar a linha de comando para compilao a cada alterao dos arquivos.
TypeScript
26
{
"compilerOptions": {
"target": "es5",
"module": "amd",
"sourceMap": true,
"watch": true
}
}
Aps a primeira compilao (ctrl+shift+b), a mensagem Watching for file changes surgir
na janela de output. Altere o arquivo user.ts e, ao salvar, uma nova compilao ser
realizada sem a necessidade de pressionar ctrl+shift+b. Para terminar o processo, aperte
F1, digite task e selecione Terminate running task.
TypeScript
27
Clique na seta verde para iniciar a primeira configurao do depurador. O arquivo .vscode/launch.json ser criado, de acordo com a imagem a seguir:
Altere o parmetro program para user.js e o parmetro sourceMaps para true. Lembre que,
sempre que for depurar cdigo TypeScript, deve-se ter o sourceMap referente.
Acesse o arquivo user.ts e adicione o breakpoint no mtodo construtor, na linha 4, conforme
a imagem a seguir:
TypeScript
28
TypeScript
29
2.3 Tipos
Agora que temos um ambiente de trabalho capaz de compilar os arquivos TypeScript em
JavaScript podemos iniciar o nosso estudo nesta linguagem. fundamental aprend-la para
que possamos ter xito no desenvolvimento Angular 2. Felizmente TypeScript semelhante
s linguagens derivadas da linguagem c, como java ou c#. Diferentemente do JavaScript, no
TypeScript podemos criar classes, interfaces, definir tipos de variveis e tipos de retorno de
mtodos.
TypeScript
30
2.3.2 Arrays
J os Arrays no TS podem ser criados atravs de duas formas. A primeira delas, usa-se [] na
definio do tipo da varivel, veja:
var list:number[] = [1,2,3];
A segunda mais conhecida como generics e usa <> para definir o tipo, veja:
var list:Array<number> = [1,2,3];
Pode-se criar arrays de tipos complexos, como por exemplo a seguir, onde a classe Point
uma classe TypeScript criada previamente.
var arr:Array<Point> = [
new Point(10,10),
new Point(20,30),
new Point(30,10)
]
2.3.3 Enum
Tambm podemos criar Enums, que so enumeraes que podem definir um status ou um
conjunto de valores, como no exemplo a seguir:
enum Color {Red, Green, Blue};
var c: Color = Color.Green;
Enuns so facilmente manipulados atravs do Visual Studio Code, como na imagem a seguir,
com a complementao de cdigo (ctrl+espao):
TypeScript
31
2.3.4 Any
O tipo any assume qualquer tipo, sendo string, number, boolean etc.
2.3.5 Void
O void usado para determinar que um mtodo no retorna nenhum valor, conforme o
exemplo a seguir.
function warnUser(): void {
alert("This is my warning message");
}
2.4 Classes
O conceito de classes no TypeScript o mesmo de uma classe em qualquer linguagem
orientada a objetos. As classes no TypeScript seguem o padro ECMAScript 6. A classe possui
uma sintaxe muito familiar com c#, veja:
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
var greeter = new Greeter("world");
2.4.1 Construtor
O construtor definido pela palavra constructor. Mtodos no necessitam da palavra
function, bastando apenas usar nomedometodo().
TypeScript
32
2.5 Herana
A herana entre uma classe e outra definida pela palavra extends. Pode-se sobrecarregar
mtodos e usar a palavra super para chamar o mtodo da classe pai, conforme o exemplo a
seguir.
TypeScript
33
class Animal {
name:string;
constructor(theName: string) { this.name = theName; }
move(meters: number = 0) {
alert(this.name + " moved " + meters + "m.");
}
}
class Snake extends Animal {
constructor(name: string) { super(name); }
move(meters = 5) {
alert("Slithering...");
super.move(meters);
}
}
class Horse extends Animal {
constructor(name: string) { super(name); }
move(meters = 45) {
alert("Galloping...");
super.move(meters);
}
}
var sam = new Snake("Sammy the Python");
var tom: Animal = new Horse("Tommy the Palomino");
sam.move();
tom.move(34);
Neste exemplo, usamos o super da classe Snake para chamar o mtodo construtor da classe
pai Animal. Se isso no for claro para voc, estude orientao a objetos para que possa
compreender melhor, pois estas caractersticas so da Orientao em Objetos como um todo,
e no do TypeScript.
TypeScript
34
35
TypeScript
class SystemAlert{
static alert(message:string):void{
alert(message);
}
static warm (message:string):void{
alert("Ateno: " + message);
}
static error(message:string):void{
alert("Erro: " + message);
}
}
SystemAlert.alert("Oi");
SystemAlert.error("No foi possvel conectar na base de dados");
2.8 Interfaces
Uma interface define um contrato para a classe. A interface criada da seguinte forma:
interface Point{
x: number;
y: number;
x: number;
}
36
TypeScript
2.9 Funes
Vamos exemplificar algumas particularidades de uma funo em TypeScript. A funo pode
ser criada fora de uma classe ou dentro, sendo as observaes que faremos a seguir podem
ser aplicadas em ambas.
Em uma classe no precisamos usar a palavra function para definir uma funo,
mas fora da classe precisamos.
37
TypeScript
class Foo{
static alertName(firstName: string, ...restOfName: string[]) {
alert(firstName + " " + restOfName.join(" "));
}
}
Foo.alertName("Fulano","de","Tal");
TypeScript
38
2.12 Mdulos
Na aprendizagem do Angular 2 pode-se escrever cdigo em um nico arquivo, criando vrias
classes em um mesmo bloco de cdigo, mas no desenvolvimento real cada classe da aplicao
deve ser um arquivo TypeScript diferente.
A forma como carregamos os mdulos, classes e arquivos no javascript depende de vrios
fatores, inclusive da escolha de uma biblioteca para tal. Nesta obra, optamos pelo systemjs,
pois a forma utilizada pelo Angular 2.
A classe foo contm um modificador chamado export, inserido antes do class. Este
modificador diz ao module.ts que a classe poder ser exportada para outras classes da
aplicao. Com o mdulo pronto, criamos o arquivo index.ts, com o seguinte cdigo:
index.js
Na primeira linha do arquivo index.ts usamos a diretiva import para importar a classe
foo do mdulo module. Nesta sintaxe, o que inserido entre chaves so as classes a serem
TypeScript
39
importadas, e o aps a palavra from inserimos o nome do arquivo do mdulo. Como o arquivo
module.js, usamos module.
Neste primeiro momento ainda no temos as configuraes necessrias para que o Visual
Studio Code compreenda o cdigo TypeScript apresentado e possivelmente sua tela ser
semelhante imagem a seguir:
Perceba o erro em module, pois o editor Visual Studio Code no foi capaz de encontra o
mdulo. Na verdade o VSCode ainda nem conseguiu determinar a forma de carregamento
de mdulos utilizada. Conforme foi visto anteriormente, necessrio criar o arquivo
tsconfig.json com estas configuraes, de acordo com o cdigo a seguir:
tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "system",
"sourceMap": true
},
"files": [
"index.ts"
]
}
40
TypeScript
{
"version": "0.1.0",
"command": "tsc",
"isShellCommand": true,
"showOutput": "silent",
"problemMatcher": "$tsc"
}
41
TypeScript
Com a task configurada, basta pressionar ctrl+shift+b para executar a tarefa. Os arquivos
js e js.map sero criados, e o erro inicial no module no acontecer novamente.
Perceba que a relao de arquivos est um pouco confusa pois existem trs arquivos
semelhantes, como por exemplo: index.js, index.ts e index.js.map. Os arquivos js e map
so os arquivos compilados do TypeScript, e no h a necessidade que eles apaream na lista
de arquivos do editor.
Para resolver este problema, acesse File > Preferences > User Settings, onde o arquivo
global de preferncias do usurio ser aberto. Para que possamos omitir os arquivos js e map
do editor, devemos configurar a propriedade files.exlude, conforme o cdigo a seguir:
TypeScript
42
"files.exclude": {
"**/.git": true,
"**/.DS_Store": true,
"**/node_modules": true,
"**/*.js": { "when": "$(basename).ts"},
"**/*.js.map": true
}
Neste exemplo, as pastas .git, .DS_Store, node_modules sero omitidas. Na penltima configurao: "**/*.js": { "when": "$(basename).ts"} estamos omitindo todos os arquivos
com a extenso .js que possuem o seu correspondente .ts e na ltima configurao estamos
omitindo todos os arquivos com a extenso .js.map.
Aps inserir estas informaes, o VSCode fica semelhante figura a seguir:
TypeScript
43
Inicialmente vamos inicializar o npm no diretrio atravs do comando npm init, conforme
a imagem a seguir:
Como esperado o diretrio node_modules ser criado e o systemjs ser adicionado ela,
conforme a figura a seguir:
TypeScript
44
Com a biblioteca systemjs pronta, podemos criar o arquivo index.html, com o seguinte
contedo:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Module Test</title>
<script src="node_modules/systemjs/dist/system.js"></script>
<script>
System.defaultJSExtensions = true;
System.import('index');
</script>
</head>
<body>
</body>
</html>
TypeScript
45
O arquivo index.js, por sua vez, importa o arquivo modules.js. Este procedimento pode ser
analisado no Google Chrome (aperte F12 aps carregar a pgina), na aba Network, conforme
a figura a seguir:
Para habilitar o Debug no Google Chrome, navegue at a aba Sources, selecione o arquivo
TypeScript que deseja debugar, e adicione um breakpoint, conforme a figura a seguir:
46
TypeScript
Para acessar o arquivo index.html no navegador, use o liveserver que foi instalado globalmente. Basta acessar o diretrio
testModules e digitar live-server, conforme a figura a seguir:
TypeScript
47
import javax.persistence.*;
@Table(name = "EMPLOYEE")
public class Employee {
@Id @GeneratedValue
@Column(name = "id")
private int id;
}
TypeScript
48
2.14 Concluso
Neste captulo tivemos uma ampla abordagem ao TypeScript, que a linguagem que pode ser
usada no Angular 2. O TypeScript est ganhando mercado por trazer uma alternativa mais
slida ao desenvolvimento JavaScript, prova disso o prprio framework Angular 2. Tambm
vimos o Visual Studio Code que um editor de textos muito poderoso e com ferramentas na
medida certa para o desenvolvimento JavaScript.
3. Um pouco de prtica
Neste capitulo iremos introduzir alguns conceitos sobre o Angular 2. A primeira tarefa ser
criar um projeto bsico, no qual poderemos Copiar/Colar para criar novos projetos.
49
Um pouco de prtica
50
At que a instalao de dependncias pelo npm volte a funcionar corretamente, voc ter que
resolver este problema instalando manualmente as bibliotecas que resultaram em erro. Copie
o nome da biblioteca seguido da sua verso e use o npm novamente para a instalao:
$
$
$
$
$
npm
npm
npm
npm
npm
i es6-promise@^3.0.2 --S
i es6-shim@^0.33.3 --S
i reflect-metadata@0.1.2 --S
i rxjs@5.0.0-beta.2 --S
i zone.js@0.5.15 --S
Perceba que as verses de cada biblioteca podem mudar de acordo com a verso beta-x do
Angular 2. Ento no copie do livro os comandos de instalao, copie dos erros gerados pelo
npm no seu sistema.
Um pouco de prtica
51
tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "system",
"sourceMap": true,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false
},
"exclude": [
"node_modules"
]
}
Um pouco de prtica
52
.vscode/tasks.json
{
"version": "0.1.0",
"command": "tsc",
"isShellCommand": true,
"showOutput": "silent",
"problemMatcher": "$tsc"
}
Se estiver utilizando outro editor de textos, ao invs de configurar uma task, abra
o terminal, navegue at o diretrio e digite: tsc
Um pouco de prtica
53
Na primeira linha, usamos o comando import para importar a classe Component, diretamente do Angular 2. Perceba que angular2/core est definido em node_modules/angular2/core.js. Aps o import, possvel usar o decorator @Component e repassar dois
parmetros: selector e template. O parmetro selector define qual a tag ser usada para
a instanciao do componente na pgina html. Com o valor my-app, subtende-se que ao
usarmos <my-app></my-app> o componente ser carregado. J template define o cdigo html
que ser usado como template no componente.
Aps o decorator @Component ser criado podemos criar a classe AppComponent, que usa o
export para dizer que a classe publica ao mdulo.
Na primeira linha do arquivo app/boot.ts importamos o mtodo bootstrap de angular2/platform/browser. Na segunda linha, importamos o componente AppComponent. Perceba que o caminho usado no from ./app.component, onde o ./ indica a raiz do arquivo,
neste caso app. Aps os imports, chamamos o mtodo bootstrap(AppComponent); que ir
inicializar a aplicao Angular.
Um pouco de prtica
54
index.html
<html>
<head>
<title>Angular 2 QuickStart</title>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></scri\
pt>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('app/boot')
.then(null, console.error.bind(console));
</script>
</head>
<body>
<my-app>Loading...</my-app>
</body>
</html>
Um pouco de prtica
55
Ateno Em alguns cdigos do arquivo PDF voc ver o fim de uma linha com uma contra barra \, conforme o detalhe da imagem a seguir:
Isso indica que a linha no acabou, e continua na linha seguinte. Se voc for
copiar e colar o cdigo, retire a contra barra do cdigo tambm. Neste exemplo,
ao invs de </scri\pt>, o correto seria </script>
Este arquivo html pode ser separado em trs blocos. O primeiro contm o carregamento das
bibliotecas padro do Angular 2, juntamente com o SystemJS que foi abordado no captulo
anterior. Depois usamos a configurao da biblioteca SystemJS para definir o formato da
aplicao, repassando a propriedade format como register. Neste caso o register o
mesmo que system.register. Tambm usamos a propriedade defaultExtension com o
valor js, ou seja, todo o arquivo requisitado para o SystemJS ter a extenso adicionada.
Na tag <body> temos a criao da tag <my-app></my-app> onde a aplicao ser renderizada.
O comando System.import ir importar app/boot que o arquivo de bootstrap que criamos.
A partir do app/boot outras classes sero criadas.
Para testar o projeto, recompile-o com ctrl+shit+b no Visual Studio Code (ou digite tsc no
terminal, no diretrio AngularBase) e use o live-server para iniciar o servidor web.
Com o projeto finalizado, pode-se us-lo para copiar e colar em um novo projeto (Copiar a
pasta e colar em uma nova pasta com outro nome).
Um pouco de prtica
56
Nossa prxima aplicao uma playlist, pode ser de vdeos contendo o ttulo, a url e uma
descrio. A princpio a playlist mostra somente o ttulo, e quando o usurio clicar no item
da lista, poder acessar a Url e a descrio. Tambm pode-se editar e criar novos vdeos, mas
ao atualizar a pgina estas modificaes sero perdidas, pois em um primeiro momento no
estaremos comunicando com o servidor ou com o banco de dados.
Resultado final da playlist:
Um pouco de prtica
57
A classe Config possui inicialmente a varivel TITLE_PAGE que esttica e possui o valor My
Playlist. Para podermos usar esta varivel no componente App, precisamos primeiro alterar
o arquivo app.component.ts para:
Um pouco de prtica
58
app/app.component.ts
Ou seja, estamos no template usando um recurso chamado Databind, e atravs de {{ }} podemos referenciar uma varivel da classe. O valor da varivel title da classe AppComponent
ser atribuda ao {{title}} do template. Existem muitos tipos de databind que veremos ao
longo desta obra.
Agora precisamos ligar a varivel title da classe AppComponent a varivel TITLE_PAGE da
classe Config. Para isso, basta importar a classe e us-la. Como a varivel esttica, o ttulo
da pgina acessado diretamente pela classe, sem a necessidade de instanciar a classe Config.
app/app.component.ts
Veja que o editor de textos (Visual Studio Code) pode lhe ajudar com a complementao de
cdigo:
Um pouco de prtica
59
<html>
<head>
<title>Angular 2 QuickStart</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></scri\
pt>
<script src="node_modules/systemjs/dist/system.src.js"></script>
Um pouco de prtica
60
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.m\
in.css">
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('app/boot')
.then(null, console.error.bind(console));
</script>
</head>
<body>
<div class="container">
<my-app>Loading...</my-app>
</div>
</body>
</html>
Um pouco de prtica
61
app/app.component.ts
Um pouco de prtica
62
app/video.ts
Um pouco de prtica
63
]
}
}
Para uma lista de vdeos, podemos criar o componente VideoListComponent. Crie o arquivo
app/videolist.component.ts com o seguinte cdigo inicial:
import {Component} from 'angular2/core'
@Component({
selector: 'video-list',
templateUrl: 'app/videolist.component.html'
})
export class VideoListComponent {
}
Nesta classe usamos a propriedade templateUrl ao invs de template, apenas para demonstrar que podemos ter um arquivo html de template separado do componente, o que uma
http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod
Um pouco de prtica
64
boa prtica de programao para o Angular 2. O caminho para o template deve ser completo
a partir da base da aplicao, por isso o app/ foi inserido. Nada impede de criar um diretrio
chamado templates na raiz da aplicao, ou dentro do diretrio app.
Uma outra boa prtica de programao separar cada componente em diretrios, principalmente em projetos com muitos componentes. Como exemplo o
componente VideoList estaria no diretrio app/components/video, onde este
diretrio teria os componentes relativos a informaes sobre os vdeos (Lista
de vdeos, tela de edio e visualizao). O template para VideoList estaria em
app/components/video/templates.
Crie o template do componente VideoList, que a princpio possui somente uma mensagem
simples:
app/videolist.component.html
Um pouco de prtica
65
app/app.component.ts
1
2
3
4
5
6
7
8
9
10
11
12
import
import
import
import
@Component({
selector: 'my-app',
template: '<h1 class="jumbotron">{{title}}</h1><video-list></video-list>',
directives: [VideoListComponent]
})
export class AppComponent {
...
A propriedade directives foi adicionada na linha 9, informando a classe VideoListComponent com o relativo import na linha 4.
import
import
import
import
@Component({
selector: 'my-app',
template: `
<h1 class="jumbotron">
{{title}}
</h1>
Um pouco de prtica
66
<video-list></video-list>
`,
directives: [VideoListComponent]
})
export class AppComponent {
...
import
import
import
import
@Component({
selector: 'my-app',
templateUrl: 'app/app.component.html',
directives: [VideoListComponent]
})
export class AppComponent {
...
app/app.component.html
<h1 class="jumbotron">
{{title}}
</h1>
<video-list></video-list>
Aps criar o template do AppComponent, podemos testar a aplicao, que deve ter a interface
semelhante a figura a seguir:
Um pouco de prtica
67
<h1 class="jumbotron">
{{title}}
</h1>
<video-list [videos]="videos"></video-list>
Ao adicionarmos [videos]="videos", dizemos que a varivel [videos] do VideoListComponent igual ao valor da varivel videos do AppComponent (que o array preenchido no
seu construtor). Para criar a varivel videos no componente VideoListComponent, usamos a
propriedade inputs na configurao do @Component, veja:
Um pouco de prtica
68
app/videolist.component.ts
Outra alternativa criar a varivel videos na classe e usar o metadata @input, por exemplo:
import {Component} from 'angular2/core'
@Component({
selector: 'video-list',
templateUrl: 'app/videolist.component.html'
})
export class VideoListComponent {
@input() videos;
}
Um pouco de prtica
69
Ao invs de exibirmos a quantidade de itens do array, vamos alterar o template para desenhar
uma tabela com a ajuda do bootstrap:
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Title</th>
</tr>
</thead>
<tbody>
<tr *ngFor="#v of videos">
<td>{{v.id}}</td>
<td>{{v.title}}</td>
</tr>
</tbody>
</table>
Para exibir a lista de videos, usamos a diretiva *ngFor no qual o asterisco indica que a tag
li faz parte de um elemento mestre/detalhe, ou seja, ela vai se repetir de acordo com a
Um pouco de prtica
70
quantidade de itens. O loop feito de acordo com #v of videos, o que significa que cada
item do array videos vai ser armazenado na varivel v. A tralha usada antes da varivel
v indica que ela poder ser usada como uma varivel dentro do loop. Neste caso, usamos
{{v.id}} e {{v.title}}.
O resultado com o novo template utilizando o *ngFor apresentado a seguir:
3.2.10 Eventos
Para adicionar um evento ao <li> que foi gerado, usamos a diretiva (click), da seguinte
forma:
Um pouco de prtica
71
Um pouco de prtica
72
Um pouco de prtica
73
}
}
Ao invs de outputs: ['selectVideo'] no @Component, pode-se usar diretamente @output() selectVideo = new EventEmitter()
Perceba que adicionamos no import a classe EventEmitter. Tambm adicionamos uma nova
diretiva ao @Component que o outputs, indicando que SelectVideo um evento de sada do
componente. No mtodo onSelect, ao invs do console.log usamos a varivel selectVideo
para disparar o evento, atravs do mtodo next, repassando o vdeo que o usurio selecionou
no evento.
Com o VideoListComponent disparando o evento, podemos voltar ao AppComponent para
captur-lo. Isso ser realizado em duas etapas. Primeiro, no template:
<h1 class="jumbotron">
{{title}}
</h1>
<video-list [videos]="videos"
(selectVideo)="onSelectVideo($event)">
</video-list>
No template usamos o evento (selectVideo) chamando o callback onSelectVideo, e repassando $event. Agora precisamos criar o mtodo onSelectVideo na classe AppComponent:
import
import
import
import
@Component({
selector: 'my-app',
templateUrl: 'app/app.component.html',
directives: [VideoListComponent]
})
export class AppComponent {
Um pouco de prtica
74
title = Config.TITLE_PAGE;
videos : Array<Video>;
constructor(){
this.videos = [
new Video(1,"Test","www.test.com","Test Description"),
new Video(2,"Test 2","www.test2.com","Test Description")
]
}
onSelectVideo(video){
console.log(JSON.stringify(video));
}
}
Um pouco de prtica
75
app/videodetail.component.ts
<h2>{{video.title}}</h2>
@Component({
selector: 'my-app',
templateUrl: 'app/app.component.html',
directives: [VideoListComponent]
})
export class AppComponent {
Um pouco de prtica
76
title = Config.TITLE_PAGE;
videos : Array<Video>;
selectedVideo: Video;
constructor(){
this.videos = [
new Video(1,"Test","www.test.com","Test Description"),
new Video(2,"Test 2","www.test2.com","Test Description")
]
}
onSelectVideo(video){
//console.log(JSON.stringify(video));
this.selectedVideo = video;
}
}
Com a propriedade selectedVideo preenchida, podemos controlar a visualizao do componente VideoDetailComponent, atravs do template da aplicao:
app/app.component.html
<h1 class="jumbotron">
{{title}}
</h1>
<video-list [videos]="videos"
(selectVideo)="onSelectVideo($event)">
</video-list>
<video-detail
*ngIf="selectedVideo"
[video]="selectedVideo">
</video-detail>
Neste template, temos a incluso do <video-detail>. Usamos a diretiva *ngIf para controlar
se o componente exibido ou no na pgina. O asterisco necessrio para indicar ao angular
Um pouco de prtica
77
que este controle ir alterar a DOM de alguma forma. Tanto ngIf e ngFor possuem esta
caracterstica.
Em [video]="selectedVideo" temos a propriedade video do VideoComponentDetail recebendo o valor de selectedVideo do componente AppComponent.
Antes de testar a aplicao, necessrio voltar ao app.component.ts e inserir o VideoDetailComponent na propriedade directives do @Component, j que este controle est sendo
adicionando pelo template e o Angular precisa que a classe esteja carregada:
import
import
import
import
import
@Component({
selector: 'my-app',
templateUrl: 'app/app.component.html',
directives: [VideoListComponent,VideoDetailComponent]
})
export class AppComponent {
....
Um pouco de prtica
78
Um pouco de prtica
79
Para implementar esta tela, usaremos alguns conceitos do Bootstrap, a biblioteca css que
estiliza a aplicao. Tambm usaremos a diretiva [(model)]="field" que implementa o
Databind entre o campo de texto e o valor da propriedade.
veideodetail.component.html
<div class="row">
<div class="col-md-4">
<iframe width="100%" height="300" src="{{video.url}}">
</iframe>
</div>
<div class="col-md-8">
<form>
<h3>{{video.title}}</h3>
<div class="form-group">
<input type="input"
class="form-control"
id="url"
80
Um pouco de prtica
required
placeholder="url"
[(ngModel)]="video.url"
>
</div>
<div class="form-group">
<textarea class="form-control" rows="5" [(ngModel)]="video.desc">
</textarea>
</div>
<button type="button"
class="btn btn-default"
(click)="onButtonOkClick()"
>Ok</button>
</form>
</div>
</div>
Neste formulrio os campos url e desc esto relacionados ao modelo atravs do recurso de
databind do angular, provido pelo uso do [()]. Este recurso chamado de Two Way DataBind
pois ao alterar o valor da caixa de texto, o mesmo alterado na propriedade selectedVideo,
que por sua vez altera o array de vdeos.
O resultado at este momento exibido a seguir:
Um pouco de prtica
81
<div class="row">
<div class="col-md-4">
<iframe width="100%" height="300" src="{{video.url}}">
</iframe>
</div>
<div class="col-md-8">
<form>
<h3 *ngIf="!editTitle" (click)="onTitleClick()">
{{video.title}}</h3>
Um pouco de prtica
82
A varivel editTitle ir controlar qual elemento estar visvel. Por padro, o elemento <h3>
aparece primeiro, e se o usurio clicar no elemento, o evento onTitleClick ser disparado.
No componente, temos:
videodetail.component.ts
Um pouco de prtica
83
onTitleClick(){
this.editTitle=true;
}
onButtonOkClick(){
//todo
}
ngOnChanges(){
this.editTitle=false;
}
}
No componente, quando onTitleClick executado, alteramos o valor da propriedade editTable, e desta forma o <h3> escondido e o campo <input> do ttulo aparece. Adicionamos
tambm o evento ngOnChanges que executado sempre que os dados do componente alteram,
ou seja, quando a propriedade video alterada, o evento ser disparado e a varivel
editTitle retorna para false.
A ltima implementao do componente o boto Ok, que deve fechar o formulrio. Na
verdade, o boto Ok vai disparar o evento close e a aplicao ir tratar o evento.
videodetail.component.ts
Um pouco de prtica
84
}
ngOnChanges(){
this.editTitle=false;
}
}
imports....
export class AppComponent {
....
onCloseDetailForm(event){
this.selectedVideo = null;
}
....
}
Ao alterar o valor da varivel selectedVideo para null, o componente VidelDetailComponent ficar invisvel, graas ao *ngIf="selectedVideo".
Um pouco de prtica
85
app/app.component.html
<h1 class="jumbotron">
{{title}}
</h1>
<video-list [videos]="videos"
(selectVideo)="onSelectVideo($event)">
</video-list>
<video-detail
*ngIf="selectedVideo"
[video]="selectedVideo"
(closeForm)="onCloseDetailForm($event)"
>
</video-detail>
<button type="button"
class="btn btn-default"
(click)="newVideo()">New</button>
Um pouco de prtica
86
<html>
<head>
<title>Angular 2 QuickStart</title>
<!-- 1. Load libraries -->
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></scri\
pt>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.m\
in.css">
Um pouco de prtica
87
Um pouco de prtica
88
<br/>
<div class="panel panel-default">
<div class="panel-heading">Panel Heading</div>
<div class="panel-body">Panel Content</div>
</div>
Agora vamos criar o componente Panel, no qual ir pertencer ao diretrio container,e que
ser uma library.
Um pouco de prtica
89
app/container/panel.ts
O template para o Panel , por enquanto, o mesmo cdigo html de um Panel do bootstrap:
<div class="panel panel-default">
<div class="panel-heading">Panel Heading</div>
<div class="panel-body">Panel Content</div>
</div>
<br/>
<panel></panel>
Um pouco de prtica
90
app/app.component.ts
Isso significa que temos uma library configurada no arquivo container.ts, da seguinte
forma:
app/container.ts
Perceba que inclumos na diretiva inputs do @Component o valor title, configurando assim
uma varivel de entrada no componente. Podemos us-lo no template, da seguinte forma:
Um pouco de prtica
91
app/container/panel.html
Aqui usamos a diretiva *ngIf="title" configurando que a <div> somente ser inserida se
houver algum valor para a varivel title. Nesta div, inclumos a varivel atravs do databind
{{title}}.
Ao recompilarmos a aplicao, temos a seguinte interface:
Se, por exemplo, o *ngIf no estivesse presente na div, teramos a seguinte sada:
Um pouco de prtica
<br/>
<panel title="Hello World"></panel>
92
Um pouco de prtica
93
Um pouco de prtica
94
app/app.component.html
<br/>
<panel title="Hello World">
Hello World from panel !!!!
</panel>
Um pouco de prtica
app/app.component.html
<br/>
<panel title="Hello World">
<panel title="Step 1">
Open a terminal
</panel>
<panel title="Step 2">
Say hello world!
</panel>
</panel>
95
4. Um pouco de teoria
Neste captulo estaremos revendo um pouco da teoria sobre o Angular 2. J tivemos um
primeiro contato com o framework atravs do exemplo do captulo anterior, ento agora
iremos fixar o que foi aprendido at este momento conhecendo os principais conceitos do
framework.
Atravs desta imagem podemos destacar alguns pontos chave que o leitor deve conhecer para
que possa dominar por completo o framework. Estes pontos so:
Modulo
Componente
Template
Metadata
Servio
Diretivas
Injeo de dependncia
96
Um pouco de teoria
97
e
config.service.ts
A palavra export configura que a classe ser pblica ao mdulo. Aps criar a classe pblica
ao mdulo, podemos import-la, como fizemos em diversos pontos da aplicao:
import {Config} from './config.service'
Um pouco de teoria
98
e
app/model/video.ts
Um pouco de teoria
99
app/app.component.ts
At este momento o cdigo est 100% correto, mas suponha que voc deseja agrupar estas
classes em uma Library. Ento criamos o arquivo model.ts e usamos o seguinte cdigo:
app/model.ts
Com a library model criada, a importao das classes Video e Playlist se resume a:
app/app.component.ts
Um pouco de teoria
100
<video-detail
*ngIf="selectedVideo"
>
</video-detail>
Componentes so a base das aplicaes em Angular 2. Sempre que estivermos criando uma
aplicao, devemos pensar em como quebr-la em componentes e fazer com que comuniquem
entre si.
4.4 Template
Uma parte importante do componente o template, que define como ele ser desenhado na
aplicao. Um template possui cdigo HTML, diretivas, chamada a eventos e tambm outros
templates. Um exemplo completo de template foi visto no captulo anterior:
<h1 class="jumbotron">
{{title}}
</h1>
<video-list [videos]="videos"
(selectVideo)="onSelectVideo($event)">
</video-list>
<video-detail
*ngIf="selectedVideo"
[video]="selectedVideo"
(closeForm)="onCloseDetailForm($event)"
>
</video-detail>
<button type="button"
class="btn btn-default"
(click)="newVideo()">New</button>
Perceba que existem diversas notaes no template que definem um comportamento especfico. Vamos listar cada um deles a seguir:
Um pouco de teoria
101
Aqui temos trs exemplos de property bind que podem ser usados para trs situaes
diferentes:
O uso o *ngIf ir determinar se o componente estar presente ou no na aplicao.
O uso do asterisco * indica ao Angular que esta propriedade pode alterar a DOM da
pgina. Isso vai alterar a forma como o Angular trata este componente, para que ele
seja otimizado.
Um pouco de teoria
102
4.5.1 Laos
Outro template expression que merece destaque so os laos, no qual usamos a diretiva
*ngFor. Perceba o uso do asterisco novamente indicando que esta diretiva altera a DOM
No elemento <tr> criamos o lao e usamos a expresso #v of videos, o que significa: pegue
cada elemento de videos, armazene na varivel v e renderize <tr>. Caso deseje utilizar o
ndice do array, altere a expresso para: *ngFor="#v of videos, #i=index".
Um pouco de teoria
103
A sada seria:
{ "firstName": "Hercules",
"lastName": "Son of Zeus",
"birthdate": "1970-02-25T08:00:00.000Z",
"url": "http://www.imdb.com/title/tt0065832/",
"rate": 325, "id": 1 }
Neste exemplo, temos o uso do selector que define o seletor que ser usado no documento
html, ou seja, ao inserirmos <my-app></my-app> no documento html, o componente AppComponentser renderizado.
A propriedade templateUrl define uma url que indica o caminho do template daquele
componente. Pode-se tambm usar template diretamente como metadata, como no exemplo
a seguir:
Um pouco de teoria
104
@Component({
selector: 'my-app',
template: '<h1>My First Angular 2 App</h1>'
})
export class AppComponent { }
Aps a configurao, pode-se usar a seguinte sintaxe para injetar a instncia da classe:
Um pouco de teoria
105
Ento alteramos o bootstrap indicando que a classe Config pode ser injetada em qualquer
classe da aplicao:
boot.ts
Aps adicionar a classe Config no bootstrap, alteramos a classe AppComponent, que ao invs
de referenciar o ttulo diretamente, ser referenciado no seu construtor, utilizando a classe
Config injetada.
Um pouco de teoria
106
app/app.component.ts
import
import
import
import
import
@Component({
selector: 'my-app',
templateUrl: 'app/app.component.html',
directives: [VideoListComponent,VideoDetailComponent]
})
export class AppComponent {
title:string;
videos : Array<Video>;
selectedVideo: Video;
constructor(_config:Config){
this.title = _config.TITLE_PAGE;
....
}
...
}
Um pouco de teoria
Para que este problema seja resolvido, basta inserir o metadata @Injectable():
import {Http} from 'angular2/http'
import {Injectable} from 'angular2/core'
@Injectable()
export class TestService{
constructor(http:Http){
http.get(.......);
}
}
107
5. Formulrios
Neste captulo tratamos as funcionalidades que o Angular 2 prov no desenvolvimento de
um formulrio web. Formulrios so usado para entrada de dados, e necessrio controlar
como esta entrada realizada e se os dados informados pelo usurio so vlidos.
Com Angular 2, possvel prover as seguintes funcionalidades:
<html>
<head>
<title>Angular 2 QuickStart</title>
<!-- 1. Load libraries -->
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></scri\
pt>
<script src="node_modules/systemjs/dist/system.src.js"></script>
108
Formulrios
109
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.m\
in.css">
<!-- 2. Configure SystemJS -->
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('app/boot')
.then(null, console.error.bind(console));
</script>
</head>
<!-- 3. Display the application -->
<body>
<div class="container">
<my-app>Loading...</my-app>
</div>
</body>
</html>
Antes de criarmos o primeiro formulrio, vamos criar a classe Person que ficar disponvel
na Library model. Para isso, devemos criar o arquivo app/model/person.ts e o arquivo
app/model.ts, da seguinte forma:
Formulrios
110
app/model/person.ts
Esta uma classe simples que define quatro campos, sendo que o campo birthdate
opcional. A classe model.ts exporta a classe Person, formando assim uma library:
app/model.ts
Agora criamos um simples service chamado Mock, que conter a informao que ser
exibida no formulrio, veja:
app/mock.ts
E vamos preparar a classe Mock para ser injetada em outras classes. Isso realizado no
bootstrap:
Formulrios
111
app/boot.ts
Com o service Mock pronto para ser usado, podemos retornar a classe AppComponent e us-lo
da seguinte forma:
app/app.component.ts
Formulrios
112
app/app.component.html
<form>
<input type="hidden" id="id" name="id"/>
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" required>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="text" class="form-control" required>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
At este momento os dados do Mock ainda no foram exibidos, isso porque no configuramos o Two-Way Databind. Para configur-lo, devemos usar a seguinte sintaxe: [(ngModel)]="model.name". No formulrio, temos:
app/app.component.html
<form>
<input type="hidden" id="id" name="id" [(ngModel)]="user.id"/>
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control"
required [(ngModel)]="user.name">
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="text" class="form-control"
required [(ngModel)]="user.email">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
Formulrios
113
Uma simples forma de analisar a varivel user da classe AppComponent usar o seguinte
template acima do formulrio:
app/app.component.html
{{user | json}}
<form>
<input type="hidden" id="id" name="id" [(ngModel)]="user.id"/>
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control"
required [(ngModel)]="user.name">
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="text" class="form-control"
required [(ngModel)]="user.email">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
Formulrios
114
Perceba que, ao alterar o valor da caixa de texto, o valor do objeto user altera automaticamente.
{{user | json}}
<form>
<input type="hidden" id="id" name="id" [(ngModel)]="user.id"/>
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control"
required [(ngModel)]="user.name"
ngControl="name"
>
</div>
<div class="form-group">
<label for="email">Email</label>
Formulrios
115
.ng-invalid {
border-left: 5px solid #a94442;
}
Aps adicionar o estilo, recompile a aplicao e acesse o formulrio, retire o texto do campo
Name e acesse o campo Email. Perceba que o estilo foi aplicado, com uma borda vermelha,
conforme a imagem a seguir:
Formulrios
116
Perceba que criamos #name="ngForm", ou seja, criamos a varivel name no campo input
apontando para ngForm, que o formulrio em questo. Depois criamos uma div e usamos
a diretiva [hidden] para controlar a sua visibilidade. O resultado desta implementao
exibida a seguir:
Formulrios
117
No boto submit podemos usar a diretiva [disabled] em conjunto com a varivel #f criada:
<button [disabled]="!f.valid"
type="submit"
class="btn btn-default">Submit</button>
Quando o formulrio estiver invlido por algum motivo, f.valid ser falso e !f.valid
verdadeiro, desabilitando o boto, conforme a imagem a seguir:
Formulrios
118
No componente, temos:
app/app.component.ts
Formulrios
119
user:Person;
constructor(_mock:Mock){
this.user = _mock.mike;
}
onSubmit(f){
console.log("sending..." + JSON.stringfy(user));
}
}
Formulrios
120
usar [hidden] para definir o que estar visvel ao usurio. O exemplo a seguir ilustra este
processo:
app/app.component.html
<div [hidden]="submitted">
<form>
....
</form>
</div>
<div [hidden]="!submitted">
Sending... {{user|json}}
</div>
Formulrios
}
}
121
[
{"id":1,"name":"Mike"},
{"id":2,"name":"Joe"},
{"id":3,"name":"Adam"}
]
122
123
<!-- 1.
<script
<script
<script
<script
<script
124
@Component({
...
providers: [HTTP_PROVIDERS],
...
})
125
ou somente:
export class AppComponent {
constructor(private _http:Http){
this._http = _http;
}
}
Ou seja, usamos _http.get(url) para acesso a url e quando o servidor retorna com as
informaes, chamamos dois mtodos que tem funcionalidades distintas. O primeiro o
mtodo map que ir formatar o texto de retorno do servidor e o segundo o subscribe que
ir atribuir o texto formatado a uma varivel qualquer. Resumindo, temos:
126
Neste exemplo, o mtodo map formata a resposta res em json, isso possvel porque o servidor
retorna um texto no formato json. O mtodo subscribe atribuiu a resposta j formatada em
json para a varivel this.users, criada na classe.
Pode-se, ao invs de atribuir o valor varivel, chamar um mtodo da prpria
classe, por exemplo: .subscribe(users => this.onGetUsers(users));
Com a varivel this.users devidamente preenchida, podemos usar o template para exibir
os dados. No exemplo a seguir, temos a classe AppComponent completa:
import {Component} from 'angular2/core'
import {Http, HTTP_PROVIDERS} from 'angular2/http'
import 'rxjs/add/operator/map'
@Component({
selector: 'my-app',
providers: [HTTP_PROVIDERS],
template: `
<ul>
<li *ngFor="#u of users">
{{u.id}} - {{u.name}}
</li>
</ul>
`
})
export class AppComponent {
users:Array<any>;
127
constructor(_http:Http){
_http.get("./users.json")
.map(res => res.json())
.subscribe(users => this.users = users);
}
}
A novidade na classe o template, que realiza um ngFor no elemento <li> atravs da varivel
users, que preenchida pelo Http. O resultado deste componente exibido a seguir:
128
129
|- employee.html
||-services
|- user.ts
|- product.ts
|- employee.ts
||-styles
|- app.css
|- user.css
|- product.css
|- employee.css
|-services.ts
|-app.component.ts
|-boot.ts
....
Perceba que nesta organizao os arquivos que representam as entidades da aplicao esto
misturados. Ou seja, para que voc trabalhe com products dever abrir diversos arquivos
em vrios diretrios. Isso se torna custoso quando temos muitos arquivos em um mesmo
diretrio. A segunda opo melhor que a primeira, mas funciona bem apenas em projetos
pequenos.
A 3a opo consiste em separar as entidades da aplicao em diretrios distintos, da seguinte
forma:
app
|-user
|- user.service.ts
|- user.component.ts
|- user.template.html
|- user.style.css
|-product
|- product.service.ts
|- product.component.ts
|- product.template.html
|- product.style.css
130
|-employee
|- employee.service.ts
|- employee.component.ts
|- employee.template.html
|- employee.style.css
|-services.ts
|-app.component.ts
|-boot.ts
Nesta classe usamos public id dentro do constructor criando assim uma varivel pblica.
Em projetos maiores possvel encapsular estas variveis. Ao criar o model User, podemos
adicion-la a library model, da seguinte forma:
131
app/model.ts
import
import
import
import
@Injectable()
export class UserService {
constructor(private http: Http) { }
public getUsers() {
return this.http
.get('./users.json')
.map(res => res.json());
}
}
Nesta classe, temos os imports que j vimos no incio do captulo. Uma novidade o import
do User que aplica um caminho diferente: ../model. O uso do ../ sobe um nvel de diretrio
para encontrar a classe model.ts.
Usamos @Injectable() para configurar a injeo de dependncia na classe. Com isso, ser
possvel injetar a classe Http na classe UserService. Quando criamos o construtor, injetamos
a instncia da classe Http na varivel http, que pode ser usada em outros mtodos da classe.
Para que possamos injetar a classe Http, devemos usar o metadata providers, ou alterar o
bootstrap da aplicao. Como Http uma classe que ser injetada em diversas outras classes,
vamos adicion-la no bootstrap da aplicao:
132
bootstrap(AppComponent,[HTTP_PROVIDERS]);
Este mtodo usa a classe Http para fazer uma requisio GET ao endereo ./users.json, e na
resposta do servidor o mtodo map chamado, onde usamos o mtodo .json() para formatar
a reposta que o texto [{id:1,name:'mike'}.....] em Json.
Perceba que estamos retornando o this.http.get(...).map(...) para quem chamou o
mtodo getUsers(). O cdigo que chama getUsers() que fica responsvel em tratar este
retorno. Isso realizado no AppComponent.
Para criar uma library de services, criamos o seguinte arquivo:
app/service.ts
133
app/app.component.ts
134
@Injectable()
export class UserService {
...
public addUser(u:User){
return this.http
.post('./addUser',JSON.stringify(u))
.map(res => res.json());
}
...
}
7. Routes
A definio de rotas no Angular 2 segue o conceito de diviso da sua aplicao em partes
menores que podem ser carregas via Ajax, pelo prprio framework.
<html>
<head>
<title>Angular 2 QuickStart</title>
<!-- 1. Load libraries -->
<script src="node_modules/angular2/bundles/angular2-polyfills.js">
</script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<script src="node_modules/angular2/bundles/router.dev.js"></script>
<base href="/">
<!-- 2. Configure SystemJS -->
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
135
136
Routes
}
}
});
System.import('app/boot')
.then(null, console.error.bind(console));
</script>
</head>
<!-- 3. Display the application -->
<body>
<my-app>Loading...</my-app>
</body>
</html>
Importante: Inclua tambm <base href="/"> dentro da seo <head> do documento html.
Routes
137
app/boot.ts
Routes
138
139
Routes
import
import
import
import
import
@Component({
selector: 'my-app',
directives: [ROUTER_DIRECTIVES],
template: `
<h1>My First Angular 2 App</h1>
<div><router-outlet></router-outlet></div>
`
})
@RouteConfig([
{ path: '/home', name: 'Home', component: HomeComponent,
useAsDefault: true },
{ path: '/login', name: 'Login', component: LoginComponent },
{ path: '/dashboard', name: 'Dashboard', component: DashboardComponent }
])
export class AppComponent { }
Routes
140
app/app.component.ts
@Component({
selector: 'my-app',
directives: [ROUTER_DIRECTIVES],
template: `
<h1>My First Angular 2 App</h1>
<ul>
<li><a [routerLink]="['/Home']">Home</a></li>
<li><a [routerLink]="['/Login']">Login</a></li>
<li><a [routerLink]="['/Dashboard']">Dashboard</a></li>
</ul>
<div><router-outlet></router-outlet></div>
`
})
Usamos [routerLink] para definir um link para a rota, onde devemos informar o nome da
rota.
Routes
141
142
143
Na prxima tela, escolha a aba Single-node e o plano Sandbox, que gratuito, conforme a
figura a seguir:
Ainda nesta tela, fornea o nome do banco de dados. Pode ser blog e clique no boto Create
new MongoDB deployment. Na prxima tela, com o banco de dados criado, acesse-o e verifique
se a mensagem A database user is required. surge, conforme a imagem a seguir:
https://mongolab.com/welcome/
144
Clique no link e adicione um usurio qualquer (login e senha) que ir acessar este banco,
conforme a imagem a seguir:
145
Aps criar o usurio, iremos usar a URI de conexo conforme indicado na sua tela de
administrao:
146
Aps inserir as informaes sobre o projeto, o arquivo package.json criado. Vamos usar o
npm para instalar todas as bibliotecas que utilizaremos no projeto:
$ npm i express body-parser jsonwebtoken mongoose angular2 systemjs bootstra\
p --S
147
A pasta app conter a aplicao Angular 2 em TypeScript, sendo que quando a aplicao
for compilada, ela ser copiada automaticamente para a pasta public. Isso significa que os
arquivos com a extenso .ts estaro localizados na pasta app e os arquivos com a extenso
.js e .js.map estaro localizados na pasta public.
148
var mongoose
var Schema
= require('mongoose');
= mongoose.Schema;
O modelo User criado com o auxlio da biblioteca mongoose. Atravs do Schema criamos
um modelo (como se fosse uma tabela) chamada User, que possui os campos name, login e
password.
A criao dos Posts exibida a seguir:
/model/post.js
var mongoose
var Schema
= require('mongoose');
= mongoose.Schema;
149
Na criao do modelo Post, usamos quase todos os mesmos conceitos do User, exceto pelo
relacionamento entre Post e User, onde configuramos que o Post o possui uma referncia ao
modelo User.
1
2
3
4
var
var
var
var
express = require('express');
app = express();
bodyParser = require('body-parser');
jwt = require('jsonwebtoken');
5
6
Na linha 6 criamos uma varivel chamada secrectKey que ser usada em conjunto com o
mdulo jsonwebtoken, para que possamos gerar um token de acesso ao usurio, quando ele
logar. Em um servidor de produo, voc dever alterar o MySuperSecretKey por qualquer
outro texto.
150
server.js
7
8
9
10
11
12
13
14
Nas linhas 13 e 14 configuramos o bodyParser, atravs do mtodo use do express, que est
representado pela varivel app. O bodyParser ir obter os dados de uma requisio em JSON
e format-los para que possamos usar na aplicao.
server.js
15
16
17
Nas linhas 16 e 17 importamos os models que foram criados e que referenciam Post e User.
Estes esquemas (Schema) sero referenciados pelas variveis Post e User.
server.js
15
16
A linha 16 cria o router, que a preparao para o express se comportar como uma API. Um
router responsvel em obter as requisies e executar um determinado cdigo dependendo
do formato da requisio. Geralmente temos quatro tipos de requisio:
151
GET: Usada para obter dados. Pode ser acessada pelo navegador atravs de uma URL.
POST: Usada para inserir dados, geralmente vindos de um formulrio.
DELETE: Usado para excluir dados
PUT: Pode ser usado para editar dados. No iremos usar PUT neste projeto, mas isso
no lhe impede de us-lo.
Alm do tipo de requisio tambm temos a url e a passagem parmetros, que veremos mais
adiante.
server.js
17
18
19
20
21
22
//Static files
app.use('/', express.static(__dirname+'/public'));
app.use('/libs', express.static(__dirname+'/node_modules/bootstrap/dist'));
app.use('/libs', express.static(__dirname+'/node_modules/systemjs/dist'));
app.use('/libs', express.static(__dirname+'/node_modules/rxjs/bundles/'));
app.use('/libs', express.static(__dirname+'/node_modules/angular2/bundles'));
Na linha 18 configuramos o diretrio public como esttico, ou seja, todo o contedo neste
diretrio ser tratado como um arquivo que, quando requisitado, dever ser entregue ao
requisitante. Este conceito semelhante ao diretrio webroot de outros servidores web. Veja
que, no caso do express, os diretrios que no pertencem ao express.static no podero
ser acessados pelo navegador web. Isso melhora a segurana do servidor inibindo a exibio
de arquivos que devem ser executados somente no servidor, como o arquivo model/user.js,
por exemplo.
Tambm usamos a varivel __dirname que retorna o caminho completo at o arquivo
server.js. Isso necessrio para um futuro deploy da aplicao em servidores reais.
Nas linhas 19 22, configuramos o diretrio esttico /libs no qual ir apontar para
as bibliotecas javascript do diretrio node_modules. Estamos adicionando as bibliotecas
do Bootstrap,SystemJS, rxjs e Angular 2, todas elas que sero adicionadas no arquivo
public/index.html do projeto, no prximo captulo.
152
server.js
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//middleware: auth
var auth = function (req, res, next) {
var token = req.body.token || req.query.token
|| req.headers['x-access-token'];
if (token) {
jwt.verify(token, secretKey, function (err, decoded) {
if (err) {
return res.status(403).send({
success: false,
message: 'Access denied'
});
} else {
req.decoded = decoded;
42
43
44
45
46
47
48
49
50
51
52
153
next();
}
});
}
else {
return res.status(403).send({
success: false,
message: 'Access denied'
});
}
}
Na linha 30 temos outro middleware, chamado de auth, que um pouco mais complexo e
tem como objetivo verificar se o token fornecido pela requisio vlido. Quando o usurio
logar no site, o cliente receber um token que ser usado em toda a requisio. Esta forma
de processamento diferente em relao ao gerenciamento de sesses no servidor, muito
comum em autenticao com outras linguagens como PHP e Java.
Na linha 31 criamos a varivel token que tenta recebe o contedo do token vindo do cliente.
No caso do blog, sempre que precisamos repassar o token ao servidor, iremos utilizar o
cabealho http repassando a varivel x-access-token.
Na linha 34 usa-se o mtodo jwt.verify para analisar o token, repassado pelo cliente. Veja
que a varivel secretKey usada neste contexto, e que no terceiro parmetro do mtodo
verify repassado um callback.
Na linha 35 verificamos se o callback possui algum erro. Em caso positivo, o token repassado
no vlido e na linha 36 retornarmos o erro atravs do mtodo res.status(403).send()
onde o status 403 uma informao de acesso no autorizado (Erro http, assim como 404
o not found).
Na linha 40 o token vlido, pois nenhum erro foi encontrado. O objeto decodificado
armazenado na varivel req.decoded para que possa ser utilizada posteriormente e o mtodo
next ir continuar o fluxo de execuo da requisio.
A linha 46 executada se no houver um token sendo repassado pelo cliente, retornando
tambm um erro do tipo 403.
154
server.js
53
54
55
56
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
router.route('/users')
.get(auth, function (req, res) {
User.find(function (err, users) {
if (err)
res.send(err);
res.json(users);
});
})
.post(function (req, res) {
var user = new User();
user.name = req.body.name;
user.login = req.body.login;
user.password = req.body.password;
user.save(function (err) {
if (err)
res.send(err);
res.json(user);
})
});
Na linha 56 comeamos a configurar o roteamento dos usurios, que ser acessado inicialmente pela url /users. Na linha 57 configuramos uma requisio GET url /users,
155
adicionando como middleware o mtodo auth, que foi criado na linha 29. Isso significa que,
antes de executar o callback do mtodo GET /users iremos verificar se o token repassado
pelo cliente vlido. Se for vlido, o callback executado e na linha 58 usamos o schema
User para encontrar todos os usurios do banco. Na linha 61 retornamos este array de usurio
para o cliente.
Na linha 64 configuramos o mtodo POST /users que tem como finalidade cadastrar o
usurio. Perceba que neste mtodo no usamos o middleware auth, ou seja, para execut-lo
no preciso estar autenticado. Na linha 65 criamos uma varivel que usa as propriedades
do Schema User para salvar o registro. Os dados que o cliente repassou ao express so
acessados atravs da varivel req.body, que est devidamente preenchida graas ao bodyparser.
O mtodo user.save salva o registro no banco, e usado o res.json para retornar o objeto
user ao cliente.
server.js
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
156
} else {
User.findOne({ login: req.body.login,
password: req.body.password }, 'name')
.exec(function (err, user) {
if (err) res.send(err);
if (user != null) {
var token = jwt.sign(user, secretKey, {
expiresIn: "1 day"
});
res.json({ user: user, token: token });
}else{
res.status(400).send('Login/Senha incorretos');
}
});
}
});
Na linha 76 temos a funcionalidade para o Login, acessado atravs da url /login. Quando
o cliente faz a requisio POST /login verificamos na linha 77 se a propriedade isNew
verdadeira, pois atravs dela que estamos controlando se o usurio est tentando logar ou
est criando um novo cadastro.
Na linha 78 usamos o mtodo findOne repassando o filtro {login:req.body.login} para
verificar se o login que o usurio preencher no existe. O segundo parmetro deste mtodo
so os campos que devero ser retornados, caso um usurio seja encontrado. O mtodo .exec
ir executar o findOne e o callback ser chamado, onde podemos retornar um erro, j que
no possvel cadastrar o mesmo login.
Se req.body.isNew for falso, o cdigo na linha 99 executado e fazemos a pesquisa ao banco
pelo login e senha. Se houver um usurio com estas informaes, usamos o mtodo jwt.sign
na linha 103 para criar o token de autenticao do usurio, e o retornarmos na linha 106. Se
no houver um usurio no banco com o mesmo login e senha, retornamos o erro na linha
108.
157
server.js
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
router.route('/posts/:post_id?')
.get(function (req, res) {
Post
.find()
.sort([['date', 'descending']])
.populate('user', 'name')
.exec(function (err, posts) {
if (err)
res.send(err);
res.json(posts);
});
})
.post(auth, function (req, res) {
var post = new Post();
post.title = req.body.title;
post.text = req.body.text;
post.user = req.body.user._id;
if (post.title==null)
res.status(400).send('Ttulo no pode ser nulo');
post.save(function (err) {
if (err)
res.send(err);
res.json(post);
});
})
.delete(auth, function (req, res) {
Post.remove({
_id: req.params.post_id
}, function(err, post) {
if (err)
res.send(err);
res.json({ message: 'Successfully deleted' });
});
});
Na linha 114 usamos /posts/:post_id? para determinar a url para obteno de posts.
158
O uso do :post_id adiciona uma varivel a url, por exemplo /posts/5. J que usamos
/posts/:post_id?, o uso do ? torna a varivel opcional.
Na linha 115 estamos tratando o mtodo GET /posts que ir obter todos os posts do banco
de dados. Na linha 118 usamos o mtodo sort para ordenar os posts, e na linha 119 usamos o
mtodo populate para adicionar uma referncia ao modelo user, que o autor do Post. Esta
referncia possvel porque adicionamos na definio do schema de Post.
Na linha 126 criamos o mtodo POST /posts que ir adicionar um Post. Criamos uma
validao na linha 131 e usamos o mtodo Post.save() para salvar o post no banco de dados.
Na linha 139 adicionamos o mtodo DELETE /posts/, onde o post apagado do banco de
dados. Para apag-lo, usamos o mtodo Post.remove na linha 140, repassando o id (chave)
do Post e usando o parmetro req.params.post_id, que veio da url /posts/:post_id?.
server.js
148
149
150
151
152
153
//register router
app.use('/api', router);
//start server
var port = process.env.PORT || 8080;
app.listen(port);
console.log('Listen: ' + port);
Finalizando o script do servidor, apontamos a varivel router para o endereo /api, na linha
149. Com isso, toda a api ser exposta na url /api. Por exemplo, para obter todos os posts do
banco de dados, deve-se fazer uma chamada GET ao endereo /api/posts. Nas linha 151 e
152 definimos a porta em que o servidor express estar escutando e na linha 153 informamos
via console.log qual a porta foi escolhida.
Onde temos uma simples resposta: Listen: 8080. Se houver alguma alterao no arquivo
server.js, esta alterao no ser refletida na execuo corrente do servidor, ser necessrio
terminar a execuo e reiniciar. Para evitar este retrabalho, vamos instalar a biblioteca
nodemon que ir recarregar o servidor sempre que o o arquivo server.js for editado.
159
A resposta j indica que, sempre quando o arquivo server.js for atualizado, o comando
node server.js tambm ser.
160
Perceba que obtemos a resposta hello world, conforme configurado no servidor. Para criar
um usurio, podemos realizar um POST url /users repassando os seguintes dados:
161
Para testar o login, vamos tentar acessar a url /api/users. Como configuramos que esta url
deve passar pelo middleware, o token no ser encontrado e um erro ser gerado:
162
163
Veja que ao repassarmos login e password, o token gerado e retornado para o postman.
Copie e cole este token para que possamos utiliz-lo nas prximas chamadas ao servidor. No
Angular, iremos armazenar este token em uma varivel.
Com o token, possvel retornar a chamada GET /users e repass-lo no cabealho da
requisio HTTP, conforme a imagem a seguir:
164
Veja que com o token os dados sobre os usurios so retornados. Experimente alterar algum
caractere do token e refazer a chamada, para obter o erro Failed to authenticate.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Blog</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<base href="/"> <!-- <<<< IMPORTANTE --->
<!-- LIBS -->
<script src="libs/angular2-polyfills.js"></script>
<script src="libs/system.src.js"></script>
<script src="libs/Rx.js"></script>
165
166
<script src="libs/angular2.dev.js"></script>
<script src="libs/http.dev.js"></script>
<script src="libs/router.dev.js"></script>
<link rel="stylesheet" href="libs/css/bootstrap.min.css">
<!-- BOOT -->
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('app/boot')
.then(null, console.error.bind(console));
</script>
</head>
<body>
<my-app>Loading...</my-app>
</body>
</html>
O documento html que contm a configurao inicial da aplicao blog possui algumas diferenas em relao aos outros que criamos durante esta obra. Primeiro, estamos adicionando
todas as bibliotecas utilizando o caminho /libs, que fisicamente no existe, mas foi criado
virtualmente no express. Isso importante para no expor toda a pasta node_modules ao
acesso pblico.
Aps adicionar as bibliotecas, iniciamos a configurao do System.config, que idntica aos
exemplos anteriores, mas possui um detalhe muito importante. Como o arquivo index.html
est na pasta public, ao executarmos System.import('app/boot') estamos na verdade
executando o arquivo public/app/boot.js, que a princpio ainda no existe. Perceba que
no podemos configurar o import com o caminho ../app/boot para acessar o diretrio
blog/app, pois este no visvel para acesso. Isso significa que neste projeto teremos os
arquivos TypeScript no diretrio /blog/app e os arquivos js e js.map (so os arquivos
compilados do TypeScript) no diretrio blog/public/app. Para que esta configurao seja
167
{
"compilerOptions": {
"target": "es5",
"module": "system",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false,
"watch":true,
"outDir": "./public/app"
},
"exclude": [
"node_modules"
]
}
Veja que o arquivo tsconfig.json muito semelhante aos anteriores, tendo como diferena
o atributo outDir, onde indicamos um diretrio em que os arquivos compilados sero criados.
Neste contexto usamos o caminho ./public/app.
Os arquivos app/boot.ts e app/component.ts podem ser criados, a princpio, com o mesmo
cdigo da aplicao AngularBase:
app/boot.ts
Veja que o bootstrap j carrega HTTP_PROVIDERS, pois iremos injetar Http no services.
A classe AppComponent, inicialmente, pode ser a seguinte:
168
Antes de compilar a aplicao, se estiver utilizando o Visual Studio Code, configure o arquivo
.vscode/tasks.json (ele ser criado na primeira vez que pressionar ctrl+shift+b) com o
seguinte texto:
.vscode/tasks.json
{
"version": "0.1.0",
"command": "tsc",
"isShellCommand": true,
"showOutput": "silent",
"problemMatcher": "$tsc"
}
169
170
171
public/appComponent.html
<h1>My Blog</h1>
Teremos o mesmo resultado, mas com o template separado do componente. Neste template,
podemos usar algumas classes css do bootstrap para estilizar a aplicao. A princpio,
podemos adicionar o seguinte html:
public/appComponent.html
172
Veja que separamos a aplicao em um menu e o seu contedo. Iremos a partir deste ponto
implementar o router.
173
app/component/home.ts
app/component/login.ts
app/component/addpost.ts
Para definir uma library destes componentes, crie o arquivo app/component.ts com o
seguinte cdigo:
174
175
])
export class AppComponent {
}
176
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
<div style="padding-top:50px">
CONTEUDO
</div>
Para testar a aplicao, recarregue a pgina e clique nos menus para verificar o contedo
sendo carregado.
E a classe user.ts:
export class User{
public _id:string;
public name: string;
public email: string;
public password:
string;
public isNew: Boolean;
constructor(){}
}
177
178
app/service/post.ts
Usamos o recurso de injeo de dependncia da classe Http, que foi configurado no bootstrap
da aplicao (app/boot.ts). O mtodo getPosts ir fazer uma requisio ajax ao endereo
./api/posts e retornar uma instncia da classe Observable do Angular 2, que ser tratada
na classe de quem realizou a chamada ao mtodo getPosts().
Como queremos exibir os posts na pgina principal, devemos configurar o HomeController
para acessar o este service. Isso feito da seguinte forma:
app/component/home.ts
179
Importamos PostService e o adicionamos aos providers do componente. Com isso poderemos injet-lo no mtodo construtor. Aps injet-lo, chamamos postService.getPosts()
que, quando responder com os posts, executa o mtodo subscribe onde possvel obter
os posts ou tratar algum eventual erro. Repare que os posts sero armazenados na varivel
this.posts, na qual podemos us-la para exibir os posts na pgina.
Para finalizar, alteramos o template do componente para:
// imports...
@Component({
providers: [PostService],
template: `
<div class="jumbotron" *ngFor="#p of posts">
<h1>{{p.title}}</h1>
<p>content</p>
</div>
`
})
export class HomeComponent { ..... }
Usamos *ngFor para realizar um loop entre os itens de posts e, por enquanto, exibimos o
ttulo do post. O resultado semelhante a imagem a seguir:
9.5 Login
A tela de login definida pelo LoginComponent, que possui a seguinte estrutura:
app/component/login.ts
import
import
import
import
import
@Component({
providers: [UserService],
template: `
>>TEMPLATE<<
`
})
export class LoginComponent {
private user:User=new User();
private showLoading:boolean = false;
private errorMessage:string = null;
180
181
constructor(private userService:UserService,
private loginService:LoginService,
private router:Router){
}
onClick(event){
event.preventDefault();
this.showLoading = true;
this.errorMessage = null;
this.userService.insert(this.user).subscribe(
result => this.onLoginResult(result),
error => this.onLoginError(error)
);
}
onLoginResult(result){
console.log(result);
this.loginService.setLogin(result.user,result.token);
this.router.navigate( ['Home'] );
}
onLoginError(error){
this.showLoading = false;
this.errorMessage = error._body;
}
}
Antes de exibir o template (que um pouco mais complexo que o template do HomeController), vamos analisar como o LoginComponent funciona. Nos imports, percebe-se que
estamos utilizando UserService e LoginService, classes que tem como objetivo prover dados
a classe do componente. Veremos estas classes em breve!
No LoginComponent, temos duas variveis que controlam o template da aplicao. Por
exemplo, showLoading ir controlar se uma mensagem Carregando ser exibida, como no
exemplo a seguir:
<div class="col-md-4 col-md-offset-4" *ngIf="showLoading">
Aguarde...
</div>
182
Com o template acima, possvel configurar a sua visualizao apenas definindo o valor da
varivel showLoading para true ou false.
O template completo do LoginComponent exibido a seguir:
<div class="col-md-4 col-md-offset-4" *ngIf="!showLoading">
<div *ngIf="errorMessage" class="alert alert-danger" role="alert">
{{errorMessage}}
</div>
<form ngForm>
<div class="form-group">
<label for="login">Login</label>
<input type="text" class="form-control"
id="login"
required
placeholder="Login"
[(ngModel)]="user.login">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control"
id="password"
required
placeholder="Password"
[(ngModel)]="user.password">
</div>
<div class="checkbox">
<label>
<input id="createAccount" type="checkbox"
[(ngModel)]="user.isNew"> Create Account?
</label>
</div>
<div class="form-group" *ngIf="user.isNew">
<label for="login">Your Name</label>
<input type="text" class="form-control"
id="name"
183
placeholder="Your Name"
[(ngModel)]="user.name">
</div>
<button type="submit" class="btn btn-default pull-right"
(click)="onClick($event)" >Login</button>
</form>
</div>
<div class="col-md-4 col-md-offset-4"
*ngIf="showLoading">
Aguarde...
</div>
9.6 Services
Algumas classes como LoginService e HeadersService podem ser usadas em vrios componentes, ento eles so injetados no arquivo boot.ts, veja:
app/boot.ts
import
import
import
import
import
Existem alguns services que so usados em poucas classes, como no exemplo do UserService,
ento ele injetado na classe atravs do providers, como foi visto no LoginComponent.
9.6.1 LoginService
A classe LoginService prov algumas informaes sobre o login do usurio:
app/service/login.ts
9.6.2 UserService
A classe UserService tem a responsabilidade de inserir um usurio.
184
import
import
import
import
import
185
@Injectable()
export class UserService {
constructor(private _http: Http, private _header:HeadersService) {
}
public insert(u:User) {
return this._http
.post('./api/login',
JSON.stringify(u),
this._header.getJsonHeaders())
.map(res => res.json());
}
}
9.6.3 HeadersService
Esta classe usada para fornecer informaes de cabealho http s requisies que o cliente
faz ao servidor:
import {Injectable} from 'angular2/core'
import {Headers} from 'angular2/http'
@Injectable()
export class HeadersService {
constructor(){}
getJsonHeaders(token?:string){
var headers = new Headers();
headers.append('Content-Type', 'application/json');
if (token)
headers.append('x-access-token',token)
186
import
import
import
import
import
@Component({
providers: [UserService],
template: `
<div class="col-md-4 col-md-offset-4" *ngIf="!showLoading">
<div *ngIf="errorMessage" class="alert alert-danger" role="alert">
{{errorMessage}}
</div>
<form ngForm>
<div class="form-group">
<label for="login">Login</label>
<input type="text" class="form-control" id="login" required plac\
187
eholder="Login" [(ngModel)]="user.login">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control" id="password" requir\
ed placeholder="Password" [(ngModel)]="user.password">
</div>
<div class="checkbox">
<label>
<input id="createAccount" type="checkbox" [(ngModel)]="user.isNe\
w"> Create Account?
</label>
</div>
<div class="form-group" *ngIf="user.isNew">
<label for="login">Your Name</label>
<input type="text" class="form-control" id="name" placeholder="Y\
our Name" [(ngModel)]="user.name">
</div>
<button type="submit" class="btn btn-default pull-right" (click)="on\
Click($event)" >Login</button>
</form>
</div>
<div class="col-md-4 col-md-offset-4" *ngIf="showLoading">
Aguarde...
</div>
`
})
export class LoginComponent {
private user:User=new User();
private showLoading:boolean = false;
private errorMessage:string = null;
constructor(private userService:UserService,
private loginService:LoginService,
private router:Router){
188
}
onClick(event){
event.preventDefault();
this.showLoading = true;
this.errorMessage = null;
this.userService.insert(this.user).subscribe(
result => this.onLoginResult(result),
error => this.onLoginError(error)
);
}
onLoginResult(result){
console.log(result);
this.loginService.setLogin(result.user,result.token);
this.router.navigate( ['Home'] );
}
onLoginError(error){
this.showLoading = false;
this.errorMessage = error._body;
}
}
189
9.8 Posts
Aps o usurio logar, ele poder clicar no link AddPost, que ser redirecionado para o
component AddPostComponent.
app/component/addPost.ts
import
import
import
import
import
@Component({
providers: [PostService],
template: `
>>TEMPLATE<<
`
})
export class AddPostComponent {
private post:Post = new Post();
private errorMessage:string = null;
private showLoading:boolean = false;
constructor(private _loginService:LoginService,
private _router:Router,
private _postService:PostService) {
if (!_loginService.isLogged())
this._router.navigate( ['Login'] );
this.post.user = this._loginService.getUser();
}
onClick(event){
event.preventDefault();
this.showLoading = true;
this.errorMessage = null;
this._postService.insert(this.post).subscribe(
result => this.onInsertPostResult(result),
190
191
9.8.1 PostService
A classe PostService responsvel em conectar no servidor e realizar trs operaes bsicas:
obter posts, incluir posts e excluir posts:
app/service/post.ts
import
import
import
import
import
import
@Injectable()
export class PostService {
constructor(private _http: Http,
private _headerService:HeadersService,
private _loginService:LoginService) { }
public getPosts() {
return this._http
.get('./api/posts')
.map(res => res.json());
}
192
public insert(p:Post){
return this._http
.post('./api/posts', JSON.stringify(p),
this._headerService.
getJsonHeaders(this._loginService.getToken()))
.map(res => res.json());
}
public delete(p:Post){
return this._http
.delete('./api/posts/' + p._id ,
this._headerService.
getJsonHeaders(this._loginService.getToken()))
.map(res => res.json());
}
}
O mtodo getPosts realiza uma chamada GET /api/posts ao servidor. O mtodo insert
ir realizar uma chamada POST /api/posts, mas aqui temos um detalhe importante:
.post('./api/posts',
JSON.stringify(p),
this._headerService.
getJsonHeaders(this._loginService.getToken())
)
Perceba que usamos a classe HeaderService para chamar o mtodo getJsonHeaders, que
ir formatar o cabealho da requisio (Requisio HTTP) como JSON (application/json).
Pode-se conferir este comportamento analisando a requisio na aba networking do Google
Chrome Developer Tools. Alm desta formatao, repassamos como parmetro o Token, pois
neste estgio o usurio tem que estar logado.
O mtodo delete adiciona na url o id do post, juntamente com o Token atravs do
HeaderService.
app/component/home.ts
import
import
import
import
import
@Component({
providers: [PostService], // OBS: LoginService at boot.ts
template: `
<div class="alert alert-info" *ngIf="showLoading">
Aguarde...
</div>
<div *ngIf="!showLoading">
<div *ngIf="_loginService.isLogged()"
class="alert alert-success">
Ol {{_loginService.getUser().name}}
<a href="#" (click)="logout($event)"
class="pull-right" >
Sair</a>
</div>
<div class="jumbotron" *ngFor="#p of posts">
<h1>{{p.title}}</h1>
<p>{{p.text}}</p>
<p>Por: {{p.user?.name}}</p>
<a href="#" (click)="deletePost(p)"
*ngIf="checkPost(p)">Apagar</a>
</div>
</div>
`
})
export class HomeComponent {
private posts: Array<Post>;
private showLoading:boolean=false;
193
194
195
console.log(error);
}
}
Existem duas diferenas importantes no componente. A primeira que inclumos uma barra
exibido se o usurio est logado:
<div *ngIf="_loginService.isLogged()"
class="alert alert-success">
Ol {{_loginService.getUser().name}}
<a href="#" (click)="logout($event)"
class="pull-right" >
Sair
</a>
</div>
Veja que usamos LoginService para controlar tanto a visibilidade quanto as informaes
do login. Tambm criamos um mtodo logout que ir chamar o mtodo this._loginService.logout(), atualizando toda a tela novamente.
Outro detalhe que inclumos o boto Excluir em cada Post:
<a href="#" (click)="deletePost(p)"
*ngIf="checkPost(p)">Apagar</a>
A visibilidade do link controlada pelo mtodo checkPost que retorna verdadeiro se, e
somente se, o usurio logado o dono do Post.
Aps estas modificaes, a tela principal do sistema fica semelhante figura a seguir:
196
9.10 Concluso
Ainda existem alguns detalhes a serem realizados no projeto blog, no qual deixaremos como
exerccio para o leitor. Por exemplo, a edio de Posts, a troca de senha, envio de senha por
email, incluso de comentrios etc. Todos estes processos usam os mesmos conceitos que
aprendermos no decorrer da obra.
10.1 Instalao
Se deseja instalar o Sublime Text, recomendamos a verso 3, que pode ser encontrada em
(neste link)[http://www.sublimetext.com/3]. Faa o download de acordo com a verso do seu
sistema operacional, e aps instalar o editor, voc dever instalar um plugin chamado Package Control. Este plugin encontrado (neste link)[https://packagecontrol.io/installation#st3].
O processo de instalao dele realizado da seguinte forma:
Copie o texto de instalao, certifique-se que a aba Sublime Text 3 est ativa.
Com o editor aberto, v at View > Show Console
Com o console aberto, cole o texto que copiou no site e pressione enter
Aguarde alguns at a instalao terminar. Se surgir alguma mensagem de erro, reinicie o
editor para que as bibliotecas sejam recarregadas.
Lembre-se que precisamos ter node e typescript instalados, conforme visto nos
captulos anteriores.
198
Surge a lista de plugins que podem ser instalados no Sublime Text. Escreva Typescript e
instale o plugin. Aps a instalao, j podemos testar o TypeScript. Acesse File>open Folder
e abra um diretrio vazio. Adicione o arquivo (File > New File) index.ts e inclua algum
cdigo TypeScript, como no exemplo a seguir:
199
class HelloWorld{
constructor(){
}
static say(){
return "Hello World";
}
}
Aps a criao da classe, pressione ctrl+b para iniciar o processo de compilao. Na caixa de
texto Build Parameters que abre, deixe em branco e pressione enter. O Plugin ir executar
o comando tsc internamente, assim como feito no Visual Studio Code e o arquivo com a
extenso .js ser criado.
200
Aps criar o arquivo, compile a aplicao com ctrl+b e veja que o diretrio public foi criado,
conforme a propriedade outDir da configurao.
201
202
criar a sua conta. Aps fornecer o seu email e senha, surge a tela de configurao semelhante
a exibida a seguir:
Nesta tela voc ir confirmar o seu email, depois atualizar as suas informaes de pagamento
(necessita carto de crdito ou paypal) e ento clicar no boto Create Droplet. Na Digital
Ocean, um droplet um servidor que estar sempre ativo, e ser nele que iremos publicar a
aplicao.
203
Aps escolher a distribuio, escolha o preo. Vamos optar pelo mas barato, de $5.00, com
isso pode-se testar o servio por dois meses:
O prximo passo escolher o local onde o servidor ser instalado. Escolha de acordo com
a sua regio mais prxima. Em Select additional options e Add your SSH keys deixe o
valor padro.
No item How many Droplets? escolha 1 Droplet e no item Choose a hostname voc dever
escolher o nome do seu servidor. No necessrio inserir o nome do domnio, ento
204
Clique no boto Create para criar o seu servidor. Aguarde alguns minutos e verifique na sua
caixa de email informaes sobre o servidor recm criado, como a senha de root e o seu ip.
205
No campo host name (or ip adress), insira o ip do seu servidor (est no email que a
Digital Ocean lhe enviou ao criar o droplet). Em Port deixe a 22, que a porta padro
de conexo SSH. Clique no boto Open e clique em Yes na mensagem que surge pelo Putty.
Surge ento a tela de login do seu servidor, no qual voc deve informar o login e a senha que
est no email.
Dica: Copie a senha e cole no putty usando o boto direito do mouse. Basta clicar
uma vez no boto direito do mouse para automaticamente colar o texto.
Aps logar, ser necessrio redefinir a sua senha de root. use uma senha forte com letras e
nmeros, pois o usurio root tem controle total sobre o seu servidor. O resultado do login
at a mudana de senha semelhante a figura seguir:
206
207
Pode-se usar o nome de usurio que desejar, insira o seu nome por exemplo. Nesta
obra, usaremos username.
Aps criar o usurio, vamos dar um pouco de poderes a ele pelo comando sudo. Se voc no
sabe o que sudo, verifique neste link o seu significado.
# adduser username sudo
Adding user 'username' to group 'sudo'
Done.
Com o usurio criado, podemos relogar com ele. Feche o putty (ou digite exit) e logue
novamente, agora com o usurio que acabou de criar:
https://en.wikipedia.org/wiki/Sudo
208
Atravs deste usurio faremos todas as alteraes no servidor necessrias para que possamos
instalar a aplicao blog.
Aps instalar o git, podemos obter o cdigo fonte da aplicao com o seguinte comando:
$ git clone https://github.com/danielschmitz/angular2-codigos.git
Todo o cdigo fonte da obra ser baixado, mas precisamos apenas do diretrio blog que est
no captulo 8. Vamos copiar o diretrio blog para o diretrio raiz do usurio. Para fazer isso,
execute o seguinte comando:
209
$ cp angular2-codigos/08/blog /home/username/ -R
210
Aps essa configurao, pode-se verificar se o servio nginx est ativo pelo comando:
$ ps aux | grep nginx
Neste ponto pode-se realizar um teste no navegador, bastando acessar o ip do servidor, como
no exemplo a seguir:
211
Aps executar o npm install, verifique que o diretrio node_modules foi criado, e as
bibliotecas adicionadas.
212
A mensagem Listem 8080 surgir, indicando que o servidor express est funcionando na
porta 8080. Isso significa que podemos acessar a aplicao atravs do ip e sua porta, conforme
a imagem a seguir:
213
Primeiro, vamos adicionar o node como um servio do servidor. Isso far com que ele seja
executado quando a mquina religada, ou quando algum problema ocorre no node, fazendo
com que o servio seja reexecutado.
Como estamos usando o Ubuntu 15, usamos novamente o systemd para adicionar este servio.
Atravs do editor de textos nano, vamos adicionar o seguinte arquivo:
$ sudo nano /etc/systemd/system/node-app-1.service
Este arquivo node-app-1.service uma configurao que ir, de acordo com o parmetro
ExecStart executar o comando /usr/bin/node /home/username/blog/server.js. Outros
parmetros so adicionados, como por exemplo o Enviroment=NODE_ENV=production, que
pode ser usado na sua aplicao.
Outro parmetro importante PORT=5000, configurando a porta do servio. Se observar no
arquivo server.js a definio da porta feita pelo cdigo var port = process.env.PORT
|| 8080, neste momento a porta 5000 ser utilizada.
Com o arquivo criado, podemos iniciar e ativar o servio, atravs do seguinte comando:
$ sudo systemctl start node-app-1
$ sudo systemctl enable node-app-1
Agora o servio node-app-1 est ativo. Pode-se verificar isso atravs do seguinte comando:
214
215
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_redirect off;
proxy_buffering off;
proxy_pass http://node_server;
}
}
Nesta configurao, usamos o comando upstream para criar um servidor chamado node_server. Este servidor ir apontar para a prpria mquina na porta 5000, a mesma que
configuramos no servio node-app-1.
O servidor principal est escutando a porta 80, e definimos atravs do location / o
proxy para o servidor node_server. Atravs desta configurao, fazemos que com que o
servidor ngix escute a porta 80 e faa um proxy a porta 5000, executando assim o node e
consequentemente o express.
Aps alterar a configurao, precisamos reiniciar o nginx com o seguinte comando:
$ sudo systemctl restart nginx
216
11.13 Domnio
Se voc criou um domnio para a sua aplicao, chegou o momento de configur-lo. Para isso,
acesse o painel de administrao do seu domnio, que varia de acordo com o seu provedor.
Usamos aqui o name.com, que vc pode adquirir um cupom de $5.00 nesta url.
Aps criar o domnio, acesse o painel de DNS RECORDS, conforme a figura a seguir:
https://www.name.com/referral/2794d9
217
Na tela Edit DNS Record, adicione duas entradas informando o ip do servidor que a Digital
Ocean criou para voc, da seguinte forma:
Com estas duas entradas, aguarde algumas horas para a publicao DNS ocorrer e acesse o
domnio que voc criou, no mais o IP. O blog que criamos em nossa obra ser carregado:
218
11.14 Concluso
Aps criarmos a aplicao em nosso ambiente de desenvolvimento, conseguimos executar
todos os passos necessrios para ter a mesma aplicao no servidor de produo, ligando um
domnio ao servidor que criamos pela Digital Ocean, e usando nginx+node para hospedar a
aplicao.
Como estamos publicando a obra pela Leanpub, possvel estender o livro com mais
contedo, mas para isso precisamos do feedback da comunidade. Acesse a pgina desta obra
em https://leanpub.com/livro-angular2 e clique em Discuss this Book.
http://www.practicalangular2.com/