Tutorial Angular 6: Novos recursos com novo poder

Publicados: 2022-03-11

Saiu 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 executar ng 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 .

Um aplicativo Angular 6 imediatamente após o andaime

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.)

A estrutura de pastas do projeto Angular 6

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 projectsmy-memoriesarchitectserveconfigurations :

 "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 recursos angular.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 arquivo index.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 nosso AppModule
  • angular.json tem o tema índigo-rosa já incluído para nós

Indicando sua escolha de um tema Angular 6 pré-construído

Você precisará reiniciar o ng serve para pegar o tema ou pode escolher outro tema pré-criado.

Relacionado: Crie aplicativos da Web ultramodernos com material angular

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 .

Um componente Angular 6 recém-criado "my-nav"

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:

Um layout de quatro painéis com Menu no canto superior esquerdo, minhas memórias ao lado e três links numerados em Menu; o quarto painel está vazio

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:

Os links da esquerda foram substituídos por Add Memory e View My Memories, com o último selecionado. O painel vazio agora tem uma tabela com números e nomes de id.

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:

O protótipo do aplicativo diário "minhas-memórias", mostrando a representação interna da entrada do usuário (data e texto).

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:

A representação interna da entrada do usuário no 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á.

Adicionando um projeto do Firebase.

Em segundo lugar, instalaremos os pacotes firebase e angularfire2 :

 npm install firebase angularfire2 --save

E então em cada um desses três arquivos:

  1. /src/environments/environment.ts
  2. /src/environments/environment.hmr.ts
  3. /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') }) } 

Permitir acesso de leitura e gravação ao seu banco de dados do Firebase.

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:

Nossa memória de teste adicionada ao banco de dados Firebase do nosso aplicativo diário.

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(); } }
Relacionado: Criando aplicativos móveis multiplataforma em tempo real: exemplos usando Ionic Framework e Firebase

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!

Relacionado: Todas as vantagens, sem complicações: um tutorial do Angular 9