Tutorial Angular 6: Novos recursos com novo poder
Publicados: 2022-03-11Saiu o Angular 6! As mudanças mais marcantes estão em sua CLI e como os serviços são injetados. Se você deseja escrever seu primeiro aplicativo Angular 6 - ou aplicativo Angular/Firebase - neste tutorial, veremos as etapas básicas da configuração inicial e criaremos um pequeno aplicativo de diário.
Angular 6: O básico
Se você nunca usou o Angular antes, deixe-me dar uma breve descrição dele e como ele funciona.
Angular é uma estrutura JavaScript projetada para oferecer suporte à criação de aplicativos de página única (SPAs) para desktop e dispositivos móveis.
A estrutura inclui um conjunto completo de diretivas e módulos que permitem implementar facilmente alguns dos cenários mais comuns para um aplicativo Web, como navegação, autorização, formulários e relatórios. Ele também vem com todos os pacotes necessários para adicionar testes usando o framework Jasmine e executá-los usando os executores de teste Karma ou Protractor.
A arquitetura angular é baseada em componentes, templates, diretivas e serviços. Ele fornece um mecanismo de injeção de dependência interno para seus serviços, bem como vinculação de dados bidirecional para conectar suas exibições ao código do componente.
O Angular usa TypeScript, um superconjunto tipado de JS, e facilitará algumas coisas, especialmente se você tiver um background de linguagem tipada.
Angular 6: novos recursos
Um breve resumo dos novos recursos do Angular 6:
- Uma política de sincronização dos principais números de versão para os pacotes de estrutura (
@angular/core
,@angular/common
,@angular/compiler
, etc.), CLI, Material e CDK. Isso ajudará a tornar a compatibilidade cruzada mais clara daqui para frente: você pode dizer com uma rápida olhada no número da versão se os pacotes de chaves são compatíveis entre si. - Novos comandos
ng
CLI:-
ng update
para atualizar versões de pacotes de forma inteligente, atualizando versões de dependências e mantendo-as sincronizadas. (Por exemplo, ao executarng update @angular/core
, todos os frameworks serão atualizados, assim como o RxJS.) Ele também executará esquemas se o pacote os incluir. (Se uma versão mais recente incluir alterações importantes que exigem alterações no código, o esquema atualizará seu código para você.) -
ng add
para adicionar novos pacotes (e executar scripts, se aplicável)
-
- Os serviços agora fazem referência aos módulos que os fornecerão, em vez de módulos que fazem referência aos serviços, como costumavam ter.
Como exemplo do que essa última alteração significa, onde seu código costumava ser:
@NgModule({ // ... providers: [MyService] })
…com essa mudança específica no Angular 6, ficará assim:
@Injectabe({ providedIn: 'root', })
Eles são chamados de provedores que podem ser sacudidos em árvore e permitem que o compilador remova serviços não referenciados, resultando em pacotes de tamanho menor.
Angular 6 CLI
A interface de linha de comando ng
é uma parte muito importante do Angular e permite que você se mova mais rapidamente ao codificar seu aplicativo.
Com a CLI, você pode organizar a configuração inicial do seu aplicativo com muita facilidade, gerar novos componentes, diretivas, etc., e criar e executar seu aplicativo em seu ambiente local.
Criando um projeto Angular 6
Ok, chega de conversa. Vamos sujar as mãos e começar a codificar.
Para começar, você precisará do Node.js e do npm instalados em sua máquina.
Agora, vamos em frente e instalar a CLI:
npm install -g @angular/cli
Isso instalará o comando ng
CLI globalmente, devido à opção -g
.
Uma vez que temos isso, podemos obter o scaffold inicial para nosso aplicativo com ng new
:
ng new my-memories --style=scss
Isso criará uma pasta my-memories
, criará todos os arquivos necessários para preparar sua configuração inicial e instalará todos os pacotes necessários. A opção --style=scss
é opcional e configurará o compilador para compilar arquivos SCSS para CSS, que precisaremos mais tarde.
Quando a instalação estiver concluída, você poderá fazer cd my-memories
e executar ng serve
. Isso iniciará o processo de compilação e um servidor Web local que atende seu aplicativo em http://localhost:4200
.
O que está acontecendo nos bastidores é que a CLI transpila todos os .ts
(arquivos TypeScript) para vanilla JS, reúne todas as dependências necessárias da pasta de pacotes node_modules
e gera o resultado em um conjunto de arquivos que são servidos por meio de um servidor web local que roda na porta 4200.
Arquivos de projeto
Se você não estiver familiarizado com a estrutura de pastas do projeto do Angular, o mais importante que você precisa saber é que todo o código relacionado ao aplicativo vai dentro da pasta src
. Você geralmente criará todos os seus módulos e diretivas nessa pasta seguindo a arquitetura do seu aplicativo (por exemplo, usuário, carrinho, produto.)
Configuração inicial
Ok, até agora temos a configuração inicial do nosso aplicativo. Vamos começar a fazer algumas mudanças.
Antes de começarmos, vamos nos aprofundar um pouco na pasta src
. A página inicial é index.html
:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>MyMemories</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> </head> <body> <app-root></app-root> </body> </html>
Aqui vemos algum HTML básico e a tag <app-root>
. Este é um componente Angular, e onde o Angular 6 insere nosso código de componente.
Encontraremos o arquivo app/app.component.ts
, que tem o seletor app-root
para corresponder ao que está no arquivo index.html
.
O componente é uma classe TypeScript decorada e, nesse caso, contém a propriedade title
. O decorador @Component
diz ao Angular para incluir o comportamento do componente na classe. Além do seletor, ele especifica qual arquivo HTML renderizar e quais folhas de estilo usar.
import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent { title = 'app'; }
Se olharmos para app.component.html
, veremos a ligação de interpolação {{title}}
. Aqui é onde toda a ligação mágica acontece, e o Angular renderizará o valor da propriedade do título da classe e o atualizará sempre que for alterado.
<!--The content below is only a placeholder and can be replaced.--> <div> <h1> Welcome to {{ title }}! </h1> <img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg=="> </div> <h2>Here are some links to help you start: </h2> <ul> <li> <h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2> </li> <li> <h2><a target="_blank" rel="noopener" href="https://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2> </li> <li> <h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2> </li> </ul>
Vamos atualizar o title
da aula para 'My Memories!'
.
... export class AppComponent { title = 'My Memories!'; } ...
Veremos o compilador processar nossa alteração e o navegador atualizar para mostrar nosso título atualizado.
Isso significa que o ng serve
do Angular 6 observa nossas alterações de arquivo e renderiza toda vez que uma alteração é introduzida em qualquer arquivo.
Para tornar a codificação mais amigável e evitar a atualização de página inteira toda vez que fizermos alterações, podemos aproveitar o Webpack Hot Module Replacement (HMR), que apenas atualiza a parte do JS/CSS que foi alterada em vez de produzir uma atualização completa para mostre suas alterações.
Configurando HMR
Primeiro, precisamos configurar o ambiente.
Crie um arquivo src/environments/environment.hmr.ts
com o seguinte conteúdo:
export const environment = { production: false, hmr: true };
Atualize src/environments/environment.prod.ts
e adicione o hmr: false
ao ambiente:
export const environment = { production: true, hmr: false };
Em seguida, atualize src/environments/environment.ts
e adicione o hmr: false
ao ambiente também:
export const environment = { production: false, hmr: false };
Em seguida, no arquivo angular.json
, atualize esta parte:
"projects": { "my-memories": { // ... "architect": { "build": { // ... "configurations": { "hmr":{ "fileReplacements":[ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.hmr.ts" } ] }, // ...
E em projects
→ my-memories
→ architect
→ serve
→ configurations
:
"projects": { "my-memories": { "architect": { // ... "serve": { // ... "configurations": { "hmr": { "browserTarget": "my-memories:build:hmr" }, // ...
Agora atualize tsconfig.app.json
para incluir os types
necessários (bem, tipo) adicionando isso em compilerOptions
:
"compilerOptions": { // ... "types": [ "node" ]
Em seguida, instalaremos o módulo @angularclass/hmr
como uma dependência de desenvolvimento:
npm install --save-dev @angularclass/hmr
Em seguida, configure-o criando um arquivo src/hmr.ts
:
import { NgModuleRef, ApplicationRef } from '@angular/core'; import { createNewHosts } from '@angularclass/hmr'; export const hmrBootstrap = (module: any, bootstrap: () => Promise<NgModuleRef<any>>) => { let ngModule: NgModuleRef<any>; module.hot.accept(); bootstrap().then(mod => ngModule = mod); module.hot.dispose(() => { const appRef: ApplicationRef = ngModule.injector.get(ApplicationRef); const elements = appRef.components.map(c => c.location.nativeElement); const makeVisible = createNewHosts(elements); ngModule.destroy(); makeVisible(); }); };
Em seguida, atualize src/main.ts
para usar a função acima:
import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; import { hmrBootstrap } from './hmr'; if (environment.production) { enableProdMode(); } const bootstrap = () => platformBrowserDynamic().bootstrapModule(AppModule); if (environment.hmr) { if (module[ 'hot' ]) { hmrBootstrap(module, bootstrap); } else { console.error('HMR is not enabled for webpack-dev-server!'); console.log('Are you using the --hmr flag for ng serve?'); } } else { bootstrap().catch(err => console.log(err)); }
O que estamos fazendo aqui é fazer o bootstrap chamar uma função anônima e, em seguida, perguntar se o sinalizador environment.hmr
é verdadeiro. Se for, chamamos a função definida anteriormente de hmr.ts
que permitiu a substituição do módulo quente; caso contrário, nós o inicializamos como costumávamos fazer.
Agora, quando executarmos ng serve --hmr --configuration=hmr
, invocaremos a configuração hmr
e, quando fizermos alterações nos arquivos, obteremos atualizações sem uma atualização completa. O primeiro --hmr
é para webpack e --configuration=hmr
é para Angular usar o ambiente hmr
.
Aplicativo Web Progressivo (PWA)
Para adicionar suporte ao Angular 6 PWA e habilitar o carregamento offline para o aplicativo, podemos usar um dos novos comandos da CLI, ng add
:
ng add @angular/[email protected]
Observe que estou adicionando a versão, pois a versão mais recente quando escrevi este tutorial estava gerando um erro. (Você pode tentar sem ele e verificar se funciona para você usando simplesmente ng add @angular/pwa
.)
Ok, então, depois de executarmos o comando, veremos muitas mudanças em nosso projeto. As mudanças mais importantes são que ele adicionou:
- Uma referência a
manifest.json
no arquivo de recursosangular.json
, para que seja incluído na saída da compilação, bem como"serviceWorker": true
em compilações de produção - O arquivo
ngsw-config.json
com a configuração inicial para armazenar em cache todos os arquivos necessários para a execução do aplicativo - Uma metatag
manifest.json
no arquivoindex.html
- O próprio arquivo
manifest.json
, com uma configuração básica para o aplicativo - A carga do service worker no módulo do aplicativo
ServiceWorkerModule.register('/ngsw-worker.js', { enabled: environment.production })
(observe que o service worker só será habilitado em ambientes de produção)
Então, isso agora significa que quando o usuário acessar o URL pela primeira vez, os arquivos serão baixados. Depois disso, se o usuário tentar acessar a URL sem serviço de rede, o aplicativo ainda funcionará puxando esses arquivos em cache.
Adicionando a biblioteca de interface do usuário do Material Angular 6
Até agora, temos a configuração inicial e estamos prontos para começar a construir nosso aplicativo. Para fazer uso de componentes já construídos, podemos usar a versão Angular 6 do Material.
Para instalar o pacote de material
em nosso aplicativo, usaremos novamente ng add
:
ng add @angular/material
Depois de executarmos isso, veremos alguns novos pacotes adicionados e algumas configurações básicas de estilo:
-
index.html
inclui a fonte Roboto e os ícones de material -
BrowserAnimationsModule
é adicionado ao nossoAppModule
-
angular.json
tem o tema índigo-rosa já incluído para nós
Você precisará reiniciar o ng serve
para pegar o tema ou pode escolher outro tema pré-criado.
Esquema Básico
Para ter o layout inicial do sidenav, usaremos os esquemas que vêm com o Material. Mas tudo bem se você quiser usar um layout diferente.
(Em poucas palavras, os esquemas permitem que você aplique transformações a um projeto: você pode criar, modificar ou excluir arquivos conforme necessário. Nesse caso, ele cria um layout de navegação lateral para nosso aplicativo.)
ng generate @angular/material:material-nav --name=my-nav
Isso criará um componente sidenav com a configuração mínima pronta para iniciar. Não é ótimo?
Também incluiu todos os módulos necessários em nosso app.module.ts
.
Como estamos usando SCSS, precisamos renomear o arquivo my my-nav.component.css
para my-nav.component.scss
e em my-nav.component.ts
atualizar os styleUrls
de referência correspondentes para usar o novo nome.
Agora para fazer uso do novo componente, vamos para app.component.html
e remova todo o código inicial, deixando apenas:
<app-my-nav></app-my-nav>
Quando voltarmos ao navegador, eis o que veremos:
Vamos atualizar os links para ter apenas as duas opções que queremos.
Primeiro, vamos criar dois novos componentes:
ng gc AddMemory ng generate @angular/material:material-table --name=view-memories
(O segundo é um esquema de material usado para criar uma tabela.)
Em seguida, no my-nav
, atualizaremos para configurar os links e incluir o <router-outlet>
para exibir nossos componentes de conteúdo:
<mat-sidenav-container class="sidenav-container"> <mat-sidenav #drawer class="sidenav" fixedInViewport="true" [attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'" [mode]="(isHandset$ | async) ? 'over' : 'side'" [opened]="!(isHandset$ | async)"> <mat-toolbar color="primary">Menu</mat-toolbar> <mat-nav-list> <a mat-list-item [routerLink]="['/add-memory']">Add Memory</a> <a mat-list-item [routerLink]="['/view-memories']">View My Memories</a> </mat-nav-list> </mat-sidenav> <mat-sidenav-content> <mat-toolbar color="primary"> <button type="button" aria-label="Toggle sidenav" mat-icon-button (click)="drawer.toggle()" *ngIf="isHandset$ | async"> <mat-icon aria-label="Side nav toggle icon">menu</mat-icon> </button> <span>my-memories</span> </mat-toolbar> <router-outlet></router-outlet> </mat-sidenav-content> </mat-sidenav-container>
Além disso, em app.component.html
, precisamos atualizá-lo para ter apenas o <router-outlet>
principal (ou seja, remover <my-nav>
):

<router-outlet></router-outlet>
Em seguida, no AppModule
vamos incluir as rotas:
import { RouterModule, Routes } from '@angular/router'; // ... imports: [ // ... RouterModule.forRoot([ { path: '', component: MyNavComponent, children: [ { path: 'add-memory', component: AddMemoryComponent }, { path: 'view-memories', component: ViewMemoriesComponent } ] }, ]), ]
Observe que estamos definindo MyNavComponent
como pai e os dois componentes que criamos como filhos. Isso ocorre porque incluímos o <router-outlet>
em MyNavComponent
, e sempre que atingirmos uma dessas duas rotas, renderizaremos o componente filho onde o <router-outlet>
foi colocado.
Depois disso, ao servirmos o aplicativo, devemos ver:
Construa o aplicativo (um diário de memórias)
Ok, agora vamos criar o formulário para salvar novas memórias em nosso diário.
Precisaremos importar alguns módulos de material e o módulo de formulários para nosso app.module.ts
:
import { FormsModule } from '@angular/forms'; import { MatCardModule, MatFormFieldModule, MatInputModule, MatDatepickerModule, MatNativeDateModule } from '@angular/material'; // ... Imports:[ // ... MatCardModule, MatFormFieldModule, MatInputModule, MatDatepickerModule, FormsModule, MatNativeDateModule, // ... ]
E então em add-memory.component.html
, adicionaremos o formulário:
<form #form="ngForm" (ngSubmit)="onSubmit()"> <mat-card class="memory-card"> <mat-card-title> Add a memory </mat-card-title> <mat-card-content> <mat-form-field> <input disabled matInput placeholder="Select the date..." [(ngModel)]="memory.date" name="date" [matDatepicker]="date"> <mat-datepicker-toggle matSuffix [for]="date"></mat-datepicker-toggle> <mat-datepicker disabled="false" #date></mat-datepicker> </mat-form-field> <mat-form-field> <textarea placeholder="Enter your memory..." rows="3" maxlength="300" matInput [(ngModel)]="memory.text" name="memory"></textarea> </mat-form-field> </mat-card-content> <mat-card-actions> <button mat-button type="submit">Save me!</button> </mat-card-actions> </mat-card> </form> <pre> {{ memory | json }} </pre>
Aqui estamos usando um mat-card
e adicionando dois campos, uma date
e uma área de textarea
.
Observe que estamos usando [(ngModel)]
. Essa diretiva Angular vinculará a expressão memory.date
e a propriedade memory
na classe uma à outra, como veremos mais adiante. ( [(ngModel)]
é açúcar sintático - um atalho para realizar a ligação de dados bidirecional da classe para a exibição e da exibição para a classe. Isso significa que quando você insere texto na exibição, memory.date
refletirá essas alterações na instância de classe e se você fizer alterações em memory.date
na instância de classe, a exibição refletirá as alterações.)
No add-memory.component.ts
, o código ficará assim:
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-add-memory', templateUrl: './add-memory.component.html', styleUrls: ['./add-memory.component.scss'] }) export class AddMemoryComponent implements OnInit { memory: any = {}; constructor() { } ngOnInit() { } onSubmit() { console.log(this.memory); } }
Aqui, estamos inicializando a propriedade memory
vinculada via ngModel
. Quando o componente AddMemoryComponent
for instanciado, a memory
será um objeto vazio. Então, quando a diretiva ngModel
executada, ela poderá atribuir o valor de entrada a memory.date
e memory.text
. Se não fizermos isso, obteremos um erro de Cannot set property 'date/text' of undefined
.
Enquanto isso, add-memory.component.scss
precisa ter:
.memory-card { min-width: 150px; max-width: 400px; width: 100%; margin: auto; } .mat-form-field { width: 100%; }
Como temos <pre> {{ memory | json }} </pre>
<pre> {{ memory | json }} </pre>
podemos ver o estado atual da memory
na visualização. Se formos ao navegador, aqui está o resultado até agora:
Na visualização, vinculamos o formulário via (ngSubmit)="onSubmit()"
à função onSubmit
na classe.
onSubmit() { console.log(this.memory); }
Então, quando você clica no botão "Salve-me!" botão, você obterá a representação interna da entrada do usuário enviada para o log do console:
Tutorial Angular 6: Conectando-se com o Firebase
O que faremos a seguir é conectar nosso projeto ao Firebase para salvar nossas memórias.
Primeiro, iremos ao console do Firebase e criaremos um projeto lá.
Em segundo lugar, instalaremos os pacotes firebase
e angularfire2
:
npm install firebase angularfire2 --save
E então em cada um desses três arquivos:
-
/src/environments/environment.ts
-
/src/environments/environment.hmr.ts
-
/src/environments/environment.prod.ts
…adicionaremos nossa configuração do Firebase:
export const environment = { // ... firebase: { apiKey: '<your-key>', authDomain: '<your-project-authdomain>', databaseURL: '<your-database-URL>', projectId: '<your-project-id>', storageBucket: '<your-storage-bucket>', messagingSenderId: '<your-messaging-sender-id>' } };
Você pode obter os detalhes de configuração necessários para os arquivos acima clicando em "Adicionar Firebase ao seu aplicativo da web" na página de visão geral do projeto.
Depois disso, incluiremos os módulos do Firebase em nosso app.module.ts
:
import { AngularFireModule } from 'angularfire2'; import { AngularFireDatabaseModule } from 'angularfire2/database'; import { environment } from '../environments/environment'; // ... Imports:[ // ... AngularFireModule.initializeApp(environment.firebase), AngularFireDatabaseModule, // ... ]
E em add-memory.component.ts
, injetamos o banco de dados no construtor e salvamos os valores do formulário no banco de dados. Quando a promessa de push do Firebase é bem-sucedida, registramos o sucesso no console e redefinimos o modelo:
import { AngularFireDatabase } from 'angularfire2/database'; // ... constructor(private db: AngularFireDatabase) { } // ... onSubmit() { this.memory.date = new Date(this.memory.date).valueOf(); this.db.list('memories').push(this.memory) .then(_ => { this.memory = {} console.log('success') }) }
Você precisará permitir o acesso público às regras do banco de dados, para que usuários anônimos possam ler e gravar nele. Observe que, com essa configuração, qualquer usuário poderá ler/alterar/excluir os dados do seu aplicativo. Certifique-se de configurar suas regras de acordo antes de ir para a produção.
Além disso, para obter as alterações de ambiente, você precisará reiniciar o processo ng serve
.
Agora, quando voltarmos ao navegador e clicarmos em nosso botão salvar, veremos que a memória foi adicionada ao banco de dados:
Vamos dar uma olhada em como podemos recuperar nossas memórias e exibi-las na tabela Material.
Quando criamos a tabela usando ng generate @angular/material:material-table --name=view-memories', we automatically got a file
view-memories/view-memories-datasource.ts`. Este arquivo contém dados falsos, então precisaremos alterá-lo para começar a extrair do Firebase.
Em view-memories-datasource.ts
, removeremos o EXAMPLE_DATA
e definiremos uma matriz vazia:
export class ViewMemoriesDataSource extends DataSource<ViewMemoriesItem> { data: ViewMemoriesItem[] = []; // ...
E em getSortedData
vamos atualizar os nomes dos campos:
private getSortedData(data: ViewMemoriesItem[]) { if (!this.sort.active || this.sort.direction === '') { return data; } return data.sort((a, b) => { const isAsc = this.sort.direction === 'asc'; switch (this.sort.active) { case 'text': return compare(a.name, b.name, isAsc); case 'date': return compare(+a.id, +b.id, isAsc); default: return 0; } }); }
Em view-memories.component.html
, atualizaremos os nomes das colunas para a date
e o text
do nosso modelo de memória. Observe que, como salvamos a data no formato de milissegundos, aqui estamos usando um pipe de data para transformar o valor para exibição em um formato de data mais amigável. Por fim, estamos removendo o [length]="dataSource.data.length"
do paginador, pois carregaremos os dados de forma assíncrona do Firebase:
<div class="mat-elevation-z8"> <table mat-table #table [dataSource]="dataSource" matSort aria-label="Elements"> <!-- Id Column --> <ng-container matColumnDef="date"> <th mat-header-cell *matHeaderCellDef mat-sort-header>Date</th> <td mat-cell *matCellDef="let row">{{row.date | date:'short'}}</td> </ng-container> <!-- Name Column --> <ng-container matColumnDef="text"> <th mat-header-cell *matHeaderCellDef mat-sort-header>Text</th> <td mat-cell *matCellDef="let row">{{row.text}}</td> </ng-container> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> </table> <mat-paginator #paginator [pageIndex]="0" [pageSize]="50" [pageSizeOptions]="[25, 50, 100, 250]"> </mat-paginator> </div>
Altere o view-memories.component.css
para view-memories.component.scss
e defina o estilo da tabela:
table{ width: 100%; }
Em view-memories.component.ts
, alteraremos os styleUrls
para refletir a renomeação acima para ./view-memories.component.scss
. Também atualizaremos a matriz displayedColumns
para ['date', 'text']
e configuraremos a fonte de dados da tabela para obter dados do Firebase.
O que está acontecendo aqui é que estamos assinando a lista de memórias e quando recebemos os dados instanciamos o ViewMemoriesDataSource
e definimos sua propriedade data com os dados do Firebase.
this.subscription = this.db.list<ViewMemoriesItem>('memories').valueChanges().subscribe(d=>{ console.log('data streaming'); this.dataSource = new ViewMemoriesDataSource(this.paginator, this.sort); this.dataSource.data = d; });
O Firebase retorna uma matriz observável no estilo ReactiveX.
Observe que estamos lançando this.db.list<ViewMemoriesItem>('memories')
—os valores extraídos do caminho 'memories'
—para ViewMemoriesItem
. Isso é feito pela biblioteca angularfire2
.
Também incluímos a chamada de unsubscribe
de assinatura no gancho onDestroy
do ciclo de vida do componente Angular.
import { Component, OnInit, ViewChild, OnDestroy } from '@angular/core'; import { MatPaginator, MatSort } from '@angular/material'; import { ViewMemoriesDataSource, ViewMemoriesItem } from './view-memories-datasource'; import { AngularFireDatabase } from 'angularfire2/database'; import { Subscription } from 'rxjs'; import { map, first } from 'rxjs/operators'; @Component({ selector: 'app-view-memories', templateUrl: './view-memories.component.html', styleUrls: ['./view-memories.component.scss'] }) export class ViewMemoriesComponent implements OnInit, OnDestroy{ @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; dataSource: ViewMemoriesDataSource; /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ displayedColumns = ['date', 'text']; subscription: Subscription; constructor(private db: AngularFireDatabase) { } ngOnInit() { this.subscription = this.db.list<ViewMemoriesItem>('memories').valueChanges().subscribe(d=>{ console.log('data streaming'); this.dataSource = new ViewMemoriesDataSource(this.paginator, this.sort); this.dataSource.data = d; }); } ngOnDestroy(): void { this.subscription.unsubscribe(); } }
Como implantar no Firebase Hosting
Agora, para ativar nosso aplicativo, vamos implantá-lo no Firebase Hosting. Para isso, instalaremos a Firebase CLI, que disponibiliza o comando firebase
:
npm install -g firebase-tools
Agora podemos usar o Firebase CLI para fazer login:
firebase login
Isso solicitará que você selecione sua conta do Google.
Em seguida, inicializaremos o projeto e configuraremos o Firebase Hosting:
firebase init
Vamos apenas selecionar a opção Hospedagem.
Em seguida, quando for solicitado o caminho, vamos defini-lo como dist/my-memories
. Quando nos perguntam se devemos configurá-lo como um aplicativo de página única (ou seja, reescrever todos os URLs para /index.html
), responderemos "sim".
Finalmente, vamos clicar, “File dist/my-memories/index.html já existe. Sobrescrever?” Aqui diremos “não”.
Isso criará os arquivos de configuração do Firebase .firebaserc
e firebase.json
com a configuração fornecida.
O último passo é executar:
ng build --prod firebase deploy
E com isso, teremos publicado o aplicativo no Firebase, que fornece uma URL para navegarmos, como https://my-memories-b4c52.firebaseapp.com/view-memories, onde você pode ver minhas próprias publicações demonstração.
Uau, você passou pelo tutorial! Espero que tenha gostado. Você também pode conferir o código completo no GitHub.
Um passo de cada vez
Angular é uma estrutura muito poderosa para criar aplicativos da web. Ele existe há muito tempo e provou ser bom para aplicativos pequenos e simples e também para aplicativos grandes e complexos - o Angular 6 não é exceção aqui.
No futuro, a Angular planeja continuar melhorando e seguindo novos paradigmas da Web, como componentes da Web (Elementos Angulares). Se você estiver interessado em criar aplicativos híbridos, confira o Ionic, que usa o Angular como estrutura subjacente.
Este tutorial cobriu etapas muito básicas para começar a usar Angular, Material e Firebase. Mas você deve levar em consideração que, para aplicativos do mundo real, você precisará adicionar validação e, para tornar seu aplicativo mais fácil de manter e dimensionar, você provavelmente deseja seguir as práticas recomendadas, como o uso de serviços, componentes reutilizáveis etc. Isso terá que ser o assunto de outro artigo - espero que este tenha sido suficiente para aguçar seu apetite pelo desenvolvimento Angular!