You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
abp/docs/pt-BR/Tutorials/Angular/Part-I.md

21 KiB

Tutorial Angular - Parte I

Sobre este tutorial

Nesta série de tutoriais, você criará um aplicativo usado para gerenciar uma lista de livros e seus autores. Angular será usado como estrutura da interface do usuário e MongoDB será usado como provedor de banco de dados.

Esta é a primeira parte da série de tutoriais angulares. Veja todas as peças:

Você pode acessar o código fonte do aplicativo no repositório GitHub .

Criando o projeto

Crie um novo projeto nomeado Acme.BookStoreselecionando Angular como a estrutura da interface do usuário e MongoDB como o provedor de banco de dados, crie o banco de dados e execute o aplicativo seguindo o documento Introdução .

Estrutura da solução (back-end)

É assim que a estrutura da solução em camadas cuida da criação:

solução de back-end da livraria

Você pode ver o documento do modelo de aplicativo para entender a estrutura da solução em detalhes. No entanto, você entenderá o básico com este tutorial.

Criar a entidade do livro

A camada de domínio no modelo de inicialização é separada em dois projetos:

  • Acme.BookStore.Domaincontém suas entidades , serviços de domínio e outros objetos principais de domínio.
  • Acme.BookStore.Domain.Shared contém constantes, enumerações ou outros objetos relacionados ao domínio que podem ser compartilhados com os clientes.

Defina entidades na camada de domínio ( Acme.BookStore.Domainprojeto) da solução. A entidade principal do aplicativo é a Book. Crie uma classe, chamada Book, no Acme.BookStore.Domainprojeto, como mostrado abaixo:

using System;
using Volo.Abp.Domain.Entities.Auditing;

namespace Acme.BookStore
{
    public class Book : AuditedAggregateRoot<Guid>
    {
        public string Name { get; set; }

        public BookType Type { get; set; }

        public DateTime PublishDate { get; set; }

        public float Price { get; set; }
    }
}
  • O ABP possui duas classes base fundamentais para entidades: AggregateRoote Entity. A raiz agregada é um dos conceitos de DDD (Domain Driven Design) . Consulte o documento da entidade para obter detalhes e melhores práticas.
  • Bookentidade herda AuditedAggregateRootque adiciona algumas propriedades de auditoria ( CreationTime, CreatorId, LastModificationTime... etc.) no topo da AggregateRootclasse.
  • Guidé o tipo de chave primária da Bookentidade.

BookType Enum

Defina a BookTypeenumeração no Acme.BookStore.Domain.Sharedprojeto:

namespace Acme.BookStore
{
    public enum BookType
    {
        Undefined,
        Adventure,
        Biography,
        Dystopia,
        Fantastic,
        Horror,
        Science,
        ScienceFiction,
        Poetry
    }
}

Adicionar entidade de livro ao seu DbContext

Adicione uma IMongoCollectionpropriedade ao BookStoreMongoDbContextinterior do Acme.BookStore.MongoDBprojeto:

public class BookStoreMongoDbContext : AbpMongoDbContext
{
    public IMongoCollection<Book> Books => Collection<Book>();
    ...
}

Adicionar dados de semente (amostra)

Esta seção é opcional, mas seria bom ter um dado inicial no banco de dados na primeira execução. O ABP fornece um sistema de semente de dados . Crie uma classe derivada de IDataSeedContributorno .Domainprojeto:

using System;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;

namespace Acme.BookStore
{
    public class BookStoreDataSeederContributor
        : IDataSeedContributor, ITransientDependency
    {
        private readonly IRepository<Book, Guid> _bookRepository;

        public BookStoreDataSeederContributor(IRepository<Book, Guid> bookRepository)
        {
            _bookRepository = bookRepository;
        }

        public async Task SeedAsync(DataSeedContext context)
        {
            if (await _bookRepository.GetCountAsync() > 0)
            {
                return;
            }

            await _bookRepository.InsertAsync(
                new Book
                {
                    Name = "1984",
                    Type = BookType.Dystopia,
                    PublishDate = new DateTime(1949, 6, 8),
                    Price = 19.84f
                }
            );

            await _bookRepository.InsertAsync(
                new Book
                {
                    Name = "The Hitchhiker's Guide to the Galaxy",
                    Type = BookType.ScienceFiction,
                    PublishDate = new DateTime(1995, 9, 27),
                    Price = 42.0f
                }
            );
        }
    }
}

BookStoreDataSeederContributorsimplesmente insere dois livros no banco de dados se não houver nenhum livro adicionado antes. O ABP descobre e executa automaticamente essa classe quando você propaga o banco de dados executando o Acme.BookStore.DbMigratorprojeto.

Crie o serviço de aplicativo

O próximo passo é criar um serviço de aplicativo para gerenciar (criar, listar, atualizar, excluir ...) os livros. A camada de aplicativo no modelo de inicialização é separada em dois projetos:

  • Acme.BookStore.Application.Contracts contém principalmente seus DTOs e interfaces de serviço de aplicativo.
  • Acme.BookStore.Application contém as implementações dos seus serviços de aplicativo.

BookDto

Crie uma classe DTO denominada BookDtono Acme.BookStore.Application.Contractsprojeto:

using System;
using Volo.Abp.Application.Dtos;

namespace Acme.BookStore
{
    public class BookDto : AuditedEntityDto<Guid>
    {
        public string Name { get; set; }

        public BookType Type { get; set; }

        public DateTime PublishDate { get; set; }

        public float Price { get; set; }
    }
}
  • As classes DTO são usadas para transferir dados entre a camada de apresentação e a camada de aplicativo . Consulte o documento Objetos de transferência de dados para obter mais detalhes.
  • BookDto é usado para transferir dados do livro para a camada de apresentação para mostrar as informações do livro na interface do usuário.
  • BookDtoé derivado do AuditedEntityDto<Guid>que possui propriedades de auditoria exatamente como a Bookclasse definida acima.

Será necessário converter Bookentidades em BookDtoobjetos enquanto retorna os livros para a camada de apresentação. A biblioteca do AutoMapper pode automatizar essa conversão quando você define o mapeamento adequado. O modelo de inicialização é fornecido com o AutoMapper configurado, para que você possa definir o mapeamento na BookStoreApplicationAutoMapperProfileclasse no Acme.BookStore.Applicationprojeto:

using AutoMapper;

namespace Acme.BookStore
{
    public class BookStoreApplicationAutoMapperProfile : Profile
    {
        public BookStoreApplicationAutoMapperProfile()
        {
            CreateMap<Book, BookDto>();
        }
    }
}

CreateUpdateBookDto

Crie uma classe DTO denominada CreateUpdateBookDtono Acme.BookStore.Application.Contractsprojeto:

using System;
using System.ComponentModel.DataAnnotations;

namespace Acme.BookStore
{
    public class CreateUpdateBookDto
    {
        [Required]
        [StringLength(128)]
        public string Name { get; set; }

        [Required]
        public BookType Type { get; set; } = BookType.Undefined;

        [Required]
        public DateTime PublishDate { get; set; }

        [Required]
        public float Price { get; set; }
    }
}
  • Essa classe DTO é usada para obter informações do livro a partir da interface do usuário ao criar ou atualizar um livro.
  • Ele define atributos de anotação de dados (como [Required]) para definir validações para as propriedades. Os DTOs são validados automaticamente pela estrutura ABP.

Em seguida, adicione um mapeamento BookStoreApplicationAutoMapperProfiledo CreateUpdateBookDtoobjeto à Bookentidade:

CreateMap<CreateUpdateBookDto, Book>();

IBookAppService

Defina uma interface nomeada IBookAppServiceno Acme.BookStore.Application.Contractsprojeto:

using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;

namespace Acme.BookStore
{
    public interface IBookAppService :
        ICrudAppService< //Defines CRUD methods
            BookDto, //Used to show books
            Guid, //Primary key of the book entity
            PagedAndSortedResultRequestDto, //Used for paging/sorting on getting a list of books
            CreateUpdateBookDto, //Used to create a new book
            CreateUpdateBookDto> //Used to update a book
    {

    }
}
  • A definição de interfaces para serviços de aplicativos não é requerida pela estrutura. No entanto, é sugerido como uma prática recomendada.
  • ICrudAppServicedefine comuns CRUD métodos: GetAsync, GetListAsync, CreateAsync, UpdateAsynce DeleteAsync. Não é necessário estendê-lo. Em vez disso, você pode herdar da IApplicationServiceinterface vazia e definir seus próprios métodos manualmente.
  • Existem algumas variações de ICrudAppServiceonde você pode usar DTOs separados para cada método.

BookAppService

Implemente IBookAppServicecomo nomeado BookAppServiceno Acme.BookStore.Applicationprojeto:

using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;

namespace Acme.BookStore
{
    public class BookAppService :
        CrudAppService<Book, BookDto, Guid, PagedAndSortedResultRequestDto,
                            CreateUpdateBookDto, CreateUpdateBookDto>,
        IBookAppService
    {
        public BookAppService(IRepository<Book, Guid> repository)
            : base(repository)
        {

        }
    }
}
  • BookAppServiceé derivado do CrudAppService<...>qual implementa todos os métodos CRUD definidos acima.
  • BookAppServiceinjeta IRepository<Book, Guid>qual é o repositório padrão da Bookentidade. O ABP cria automaticamente repositórios padrão para cada raiz (ou entidade) agregada. Veja o documento do repositório .
  • BookAppServiceusa IObjectMapperpara converter Bookobjetos em BookDtoobjetos e CreateUpdateBookDtoobjetos em Bookobjetos. O modelo de inicialização usa a biblioteca AutoMapper como o provedor de mapeamento de objetos. Você definiu os mapeamentos antes, para que funcionem conforme o esperado.

Controladores de API automática

Você normalmente cria controladores para expor serviços de aplicativos como pontos de extremidade da API HTTP . Assim, permite que navegadores ou clientes de terceiros os chamem via AJAX. O ABP pode configurar automaticamente seus serviços de aplicativo como controladores de API MVC por convenção.

UI do Swagger

O modelo de inicialização está configurado para executar a interface do usuário do swagger usando a biblioteca Swashbuckle.AspNetCore . Execute o Acme.BookStore.HttpApi.Hostaplicativo e insira https://localhost:XXXX/swagger/(substitua XXXX por sua própria porta) como URL no seu navegador.

Você verá alguns pontos de extremidade de serviço internos, bem como o Bookserviço e seus pontos de extremidade no estilo REST:

livraria-arrogância

O Swagger tem uma ótima interface para testar APIs. Você pode tentar executar a [GET] /api/app/bookAPI para obter uma lista de livros.

Crie a página de livros

Neste tutorial;

  • A CLI angular será usada para criar módulos, componentes e serviços
  • NGXS será usado como a biblioteca de gerenciamento de estado
  • O Bootstrap será usado como a biblioteca de componentes da interface do usuário.
  • O Visual Studio Code será usado como editor de código (você pode usar seu editor favorito).

Instalar pacotes NPM

Abra uma janela do terminal, vá para a angularpasta e execute o yarn comando para instalar os pacotes NPM:

yarn

BooksModule

Execute a seguinte linha de comando para criar um novo módulo, denominado BooksModule:

yarn ng generate module books --route books --module app.module

Creating-Books-Module.terminal

Execute yarn start, aguarde Angular para executar o aplicativo e abra http://localhost:4200/booksem um navegador:

página inicial dos livros

Encaminhamento

Abra app-routing.module.tse substitua booksconforme mostrado abaixo:

import { ApplicationLayoutComponent } from '@abp/ng.theme.basic';-

//...
{
  path: 'books',
  component: ApplicationLayoutComponent,
  loadChildren: () => import('./books/books.module').then(m => m.BooksModule),
  data: {
    routes: {
      name: 'Books',
    } as ABP.Route,
  },
},

ApplicationLayoutComponentconfiguração define o layout do aplicativo para a nova página. Se você deseja ver sua rota na barra de navegação (menu principal), também deve adicionar o dataobjeto com namepropriedade à sua rota.

página inicial dos livros

Componente da lista de livros

Primeiro, substitua pela books.component.htmlseguinte linha para colocar a saída do roteador:

<router-outlet></router-outlet>

Em seguida, execute o comando abaixo no terminal na pasta raiz para gerar um novo componente, chamado book-list:

yarn ng generate component books/book-list

terminal-criando-lista-de-livros

Importe SharedModulepara BooksModulepara reutilizar alguns componentes e serviços definidos em:

import { SharedModule } from '../shared/shared.module';

@NgModule({
  //...
  imports: [
    //...
    SharedModule,
  ],
})
export class BooksModule {}

Em seguida, atualize o routesno books-routing.module.tspara adicionar o novo componente book-list:

import { BookListComponent } from './book-list/book-list.component';

const routes: Routes = [
  {
    path: '',
    component: BooksComponent,
    children: [{ path: '', component: BookListComponent }],
  },
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule],
})
export class BooksRoutingModule {}

página inicial da lista de livros

Criar BooksState

Execute o seguinte comando no terminal para criar um novo estado, denominado BooksState:

yarn ng generate ngxs-schematic:state books

Este comando cria vários novos arquivos e edições app.modules.tspara importar o NgxsModulecom o novo estado:

// app.module.ts

import { BooksState } from './store/states/books.state';

@NgModule({
  imports: [
    //...
    NgxsModule.forRoot([BooksState]),
  ],
  //...
})
export class AppModule {}

Obter dados de livros do back-end

Primeiro, crie tipos de dados para mapear os dados que retornam do back-end (você pode verificar a interface do swagger ou a API do back-end para conhecer o formato dos dados).

Modifique o books.tscomo mostrado abaixo:

export namespace Books {
  export interface State {
    books: Response;
  }

  export interface Response {
    items: Book[];
    totalCount: number;
  }

  export interface Book {
    name: string;
    type: BookType;
    publishDate: string;
    price: number;
    lastModificationTime: string;
    lastModifierId: string;
    creationTime: string;
    creatorId: string;
    id: string;
  }

  export enum BookType {
    Undefined,
    Adventure,
    Biography,
    Dystopia,
    Fantastic,
    Horror,
    Science,
    ScienceFiction,
    Poetry,
  }
}

Adicionada Bookinterface que representa um objeto de livro e BookTypeenum representa uma categoria de livro.

BooksService

Agora, crie um novo serviço, nomeado BooksServicepara executar chamadas HTTP para o servidor:

yarn ng generate service books/shared/books

serviço-terminal-saída

Modifique books.service.tscomo mostrado abaixo:

import { Injectable } from '@angular/core';
import { RestService } from '@abp/ng.core';
import { Books } from '../../store/models';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class BooksService {
  constructor(private restService: RestService) {}

  get(): Observable<Books.Response> {
    return this.restService.request<void, Books.Response>({
      method: 'GET',
      url: '/api/app/book'
    });
  }
}

Adicionado o getmétodo para obter a lista de livros executando uma solicitação HTTP no terminal relacionado.

Substitua o books.actions.tsconteúdo conforme mostrado abaixo:

export class GetBooks {
  static readonly type = '[Books] Get';
}

Implementar o BooksState

Abra o books.state.tse altere o arquivo, como mostrado abaixo:

import { State, Action, StateContext, Selector } from '@ngxs/store';
import { GetBooks } from '../actions/books.actions';
import { Books } from '../models/books';
import { BooksService } from '../../books/shared/books.service';
import { tap } from 'rxjs/operators';
import { Injectable } from '@angular/core';

@State<Books.State>({
  name: 'BooksState',
  defaults: { books: {} } as Books.State,
})
@Injectable()
export class BooksState {
  @Selector()
  static getBooks(state: Books.State) {
    return state.books.items || [];
  }

  constructor(private booksService: BooksService) {}

  @Action(GetBooks)
  get(ctx: StateContext<Books.State>) {
    return this.booksService.get().pipe(
      tap(booksResponse => {
        ctx.patchState({
          books: booksResponse,
        });
      }),
    );
  }
}

Adicionada a GetBooksação que usa o BookServicedefinido acima para obter os livros e corrigir o estado.

O NGXS exige retornar o observável sem assiná-lo, conforme feito nesta amostra (na função get).

BookListComponent

Modifique o book-list.component.tscomo mostrado abaixo:

import { Component, OnInit } from '@angular/core';
import { Store, Select } from '@ngxs/store';
import { BooksState } from '../../store/states';
import { Observable } from 'rxjs';
import { Books } from '../../store/models';
import { GetBooks } from '../../store/actions';

@Component({
  selector: 'app-book-list',
  templateUrl: './book-list.component.html',
  styleUrls: ['./book-list.component.scss'],
})
export class BookListComponent implements OnInit {
  @Select(BooksState.getBooks)
  books$: Observable<Books.Book[]>;

  booksType = Books.BookType;

  loading = false;

  constructor(private store: Store) {}

  ngOnInit() {
    this.loading = true;
    this.store.dispatch(new GetBooks()).subscribe(() => {
      this.loading = false;
    });
  }
}

Consulte as ações de despacho e selecione na documentação do NGXS para obter mais informações sobre esses recursos do NGXS.

Substitua o book-list.component.htmlconteúdo conforme mostrado abaixo:

<div id="wrapper" class="card">
  <div class="card-header">
    <div class="row">
      <div class="col col-md-6">
        <h5 class="card-title">
          Books
        </h5>
      </div>
    </div>
  </div>
  <div class="card-body">
    <p-table [value]="books$ | async" [loading]="loading" [paginator]="true" [rows]="10">
      <ng-template pTemplate="header">
        <tr>
          <th>Book name</th>
          <th>Book type</th>
          <th>Publish date</th>
          <th>Price</th>
        </tr>
      </ng-template>
      <ng-template pTemplate="body" let-data>
        <tr>
          <td>{%{{{ data.name }}}%}</td>
          <td>{%{{{ booksType[data.type] }}}%}</td>
          <td>{%{{{ data.publishDate | date }}}%}</td>
          <td>{%{{{ data.price }}}%}</td>
        </tr>
      </ng-template>
    </p-table>
  </div>
</div>

Usamos a tabela PrimeNG neste componente.

A página de livros resultante é mostrada abaixo:

livraria-lista-de-livros

E esta é a estrutura de pastas e arquivos no final deste tutorial:

img

Este tutorial segue o Guia de estilo angular .

Próxima parte

Veja a próxima parte deste tutorial.